Maelstrom is a suite of tools for running tests in hermetic micro-containers locally on your machine or distributed across arbitrarily large clusters. Maelstrom currently has test runners for Rust, Go, and Python, with more on the way. You might use Maelstrom to run your tests because:
- It's easy. Maelstrom provides drop-in replacements for
cargo test
,go test
, andpytest
. In most cases, it just works with your existing tests with minimal configuration. - It's reliable. Maelstrom runs every test hermetically in its own lightweight container, eliminating confusing errors caused by inter-test or implicit test-environment dependencies.
- It's scalable. Maelstrom can be run as a cluster. You can add more worker machines to linearly increase test throughput.
- It's clean. Maelstrom has built a rootless container implementation (not relying on Docker or RunC) from scratch, in Rust, optimized to be low-overhead and start quickly.
- It's fast. In most cases, Maelstrom is faster than
cargo test
orgo test
, even without using clustering. Maelstrom’s test-per-process model is inherently slower thanpytest
’s shared-process model, but Maelstrom provides test isolation at a low performance cost.
While our focus thus far has been on running tests, Maelstrom's underlying job execution system is general-purpose. We provide a command line utility to run arbitrary commands, as well a gRPC-based API and Rust bindings for programmatic access and control.
The project is currently Linux-only (x86 and ARM), as it relies on namespaces to implement containers.
See the book for more information.
To run your tests using Maelstrom, you need a test runner binary. The easiest way to get it is using cargo-binstall:
For Rust tests:
cargo binstall cargo-maelstrom
For Go tests:
cargo binstall maelstrom-go-test
For Python tests:
cargo binstall maelstrom-pytest
This will install a pre-built binary from the github releases page.
If you don't have cargo-binstall
, you can download the binaries manually.
Check out the book for more ways to get Maelstrom.
To run your Rust tests, use cargo-maelstrom
:
cargo maelstrom
This runs in "standalone" mode, meaning all tests are run locally. Each test is run in its own container, configured with a few common dependencies. It may work for your project without any further configuration.
If some tests fail, however, it likely means those tests have dependencies on
their execution environment that aren't packaged in their containers.
You can remedy this by adding directives to the cargo-maelstrom.toml
file. To
do this, run:
cargo maelstrom --init
Then edit the created cargo-maelstrom.toml
file as described in the
book.
To run your Go tests, use maelstrom-go-test
:
maelstrom-go-test
This runs in "standalone" mode, meaning all tests are run locally. Each test is run in its own container, configured with a few common dependencies. It may work for your project without any further configuration.
If some tests fail, however, it likely means those tests have dependencies on
their execution environment that aren't packaged in their containers. You can
remedy this by adding directives to the maelstrom-go-test.toml
file. To do
this, run:
maelstrom-go-test --init
Then edit the created maelstrom-go-test.toml
file as described in the
book.
Before running tests, we need to do a little setup.
First generate a maelstrom-pytest.toml
file
maelstrom-pytest --init
Then update the image in the file to have the version of Python you desire.
[[directives]]
image = "docker://python:3.11-slim"
The default configuration and our example uses an image from Docker
So that your tests can be run from the container, your project's python must be included.
Update the added_layers
in the file to make sure it includes your project's Python.
added_layers = [ { glob = "**.py" } ]
This example just adds all files with a .py
extension. You may also need to include .pyi
files
or other files.
If you have an image named "python", maelstrom-pytest
will automatically include pip packages for
you as part of the container. It expects to read these packages from a test-requirements.txt
file
in your project directory. This needs to at a minimum include the pytest
package
test-requirements.txt
pytest==8.1.1
Now we are ready to try to run tests. Just invoke maelstrom-pytest
:
maelstrom-pytest
This runs in "standalone" mode, meaning all tests are run locally. Each test is run in its own container.
Once you have finished the configuration, you only need invoke maelstrom-pytest
to run all the
tests in your project. It must be run from an environment where pytest
is in the Python path. If
you are using virtualenv for your project make sure to source that first.
To get even more out of Maelstrom, you can set up a cluster to run your tests on.
You will need to run one copy of the broker (maelstrom-broker
) somewhere, and
one copy of the worker (maelstrom-worker
) on each node of the cluster.
You can install these using multiple
methods,
including cargo-binstall
:
cargo binstall maelstrom-worker maelstrom-broker
Then you can start the broker:
maelstrom-broker --port=1234
Then a few workers:
maelstrom-worker --broker=broker-host:1234
And then run cargo-maelstrom
or maelstrom-pytest
against the cluster:
cargo maelstrom --broker=broker-host:1234
maelstrom-pytest --broker=broker-host:1234
Find our complete documentation in the book.
This project is available under the terms of either the Apache 2.0 license or the MIT license.