Skip to content

Testing

StableTested and in active use. Breaking changes will be called out.

There are 1,161 test files (test_*.mojo) under tests/, 107 of them GPU tests. Running them all takes hours, so they are organised into tiers by cost, plus manifests for the gates that need something the default tiers do not have — a reference dump, a download, a GPU, hardware.

Terminal window
pixi run test-smoke # curated fast CPU gates — what CI runs per push
pixi run examples-compile # compile-probe the root examples
pixi run test-cpu # every non-GPU test — hours
pixi run -e apple test-gpu # GPU tests on Metal
pixi run -e nvidia test-gpu # GPU tests on CUDA

test-smoke runs the curated manifest tests/manifests/smoke.txt (90 entries). It is deliberately self-contained: no references/, no ROMs, no GPU, no downloads. It is the gate on every push.

examples-compile builds the examples listed in tests/manifests/examples-compile.txt (27 entries) without running them. Examples rot faster than tests because nothing forces them to compile — this catches it cheaply.

test-cpu discovers every test_*.mojo under tests/ that is not a GPU test. Run it before a large merge; CI runs it weekly.

test-gpu discovers the GPU tests — files ending in _gpu.mojo, _gpu_vs_cpu.mojo or _gpu_parity.mojo. It never runs in CI (there are no free GPU runners), so it is a task for a local or rented GPU box.

All of them go through scripts/run_tests.sh, which prints the last 40 lines of each failing test, a summary table, and exits with the number of failures. It reads the exit code only: a test that prints FAIL and returns normally counts as a pass, so a gate must raise or exit non-zero to fail.

A manifest is a list of test files, one per line, optionally with arguments. Each header says what the gates need before they can run.

TaskManifestNeeds
test-smokesmoke.txtnothing — the CI tier
examples-compileexamples-compile.txtnothing (compile only)
test-ioio.txtParquet, resize and synthetic LeRobot dumps from tools/io/ and tools/act/; the noeira.ai clients run against a local mock server
test-actact.txtthe PyTorch ACT reference dump (-e act-ref) and a converted LeRobot store
test-act-gpuact-gpu.txt-e apple or -e nvidia
test-interopinterop.txtact-ref dumps — safetensors and torch-name interop
test-vlavla.txtnothing — SmolVLA structural gates, no weights
test-vla-gpuvla-gpu.txta GPU environment
test-vla-weightsvla-weights.txtthe published SmolVLA weights (~907 MB, cached)
test-vla-refvla-ref.txtpixi run dump-vla-ref first
test-fbfb.txtnothing — Forward-Backward, CPU
test-fb-gpufb-gpu.txt-e nvidia or -e apple
test-g1g1.txtthe baked Unitree G1 assets and motion file
test-nn-gpunn-gpu.txt-e apple or -e nvidia — the trainers the nn examples run through and the kernels they spend their step in
test-nn-distributednn-distributed.txt-e apple or -e nvidia — DDP and ZeRO on the one-device simulator
test-lewmlewm.txtnothing — experimental, CPU, toy dims
test-lewm-gpulewm-gpu.txt-e apple or -e nvidia
test-lewm-reflewm-ref.txtpixi run dump-lewm-ref first; a GPU environment
test-bfm-refbfm-ref.txtruns in -e act-ref (PyTorch in-process)
test-physics3d-mujocophysics3d-mujoco.txtthe pixi MuJoCo 3.12 runtime; about two hours
test-physics3d-gpuphysics3d-gpu.txt-e nvidia
test-concurrentconcurrent.txtnothing — the pthread / ring / atomics backbone
test-swmswm.txtnothing — experimental, CPU
test-visionvision.txtthe OpenCV shim, which the task builds first (build-opencv)

All manifests live in tests/manifests/.

Terminal window
pixi run mojo run -I . tests/nn/test_mnist_loader.mojo
pixi run mojo run -I . tests/physics3d/test_euler_fields_vs_mujoco.mojo
pixi run -e nvidia mojo run -I . tests/deep_agents/test_storage_sac_gpu_pendulum.mojo

Several single gates also have a named task — pixi run appearance-test, pixi run device-placement, pixi run libero-task-hooks and others; see pixi run --list.

DirectoryTest filesCovers
physics3d/264MuJoCo comparison (131 *_vs_mujoco files), solvers, collision, integrators, cameras
nn/234modules, autodiff, optimizers, checkpoints, kernels
deep_agents/220agent convergence, GPU parity, imitation
pcn/ · experimental/83 · 61research code
dm_control/69DeepMind Control domains
envs/65environment dynamics
io/ · data/23 · 12native I/O, trajectory store, LeRobot
tasks/ · libero/19 · 12the task layer and LIBERO
planners/18MPPI, CEM, iLQR, tree search
robots/ · robot/ · vision/13 · 6 · 6robot models, the servo bus, cameras
arcade_games/ · atari/ · board_games/13 · 1 · 4game engines, emulator, self-play
core/ · concurrent/ · cuda/ · render/ · physics2d/the rest

.github/workflows/ci.yml runs everything in the cpu environment, which leaves out the CUDA toolchain:

TriggerJobs
push to dev or main, pull requestsmoke (test-smoke), model-dims (gen-dims-check), examples-compile
weekly cron, Sunday 03:17 UTCcpu-full (test-cpu)
manual dispatchthe smoke tier or cpu-full

Pushes that touch only Markdown, images or docs/ are skipped. The pixi environment is cached on pixi.lock, so steady-state runs skip the toolchain download. cpu-full is continue-on-error: parts of the long tail need local-only assets and fail on a runner; the summary still shows what is red and whether that changed week to week.

model-dims exists because the compile-time model dimensions are generated from the .xml assets through MuJoCo. A stale generated file still compiles — it just sizes the buffers for a model that no longer exists — so the check runs on every push. The other generators carry --check tasks too (gen-park-scenes-check, gen-family-scenes-check, gen-placement-tables-check, gen-libero-envs-check, vendor-panda-check, libero-arenas-check, libero-objects-check), but CI does not run them; run them after changing a family, a scene or a generated asset.

.github/workflows/docs.yml builds this site on changes under docs-site/, with two guards around the build: check:refs fails on any repository path a page cites that does not exist, and check:og fails if a page’s social image does not resolve.

Nothing on a GPU runs in CI. test-gpu, test-act-gpu, test-vla-gpu, test-fb-gpu and test-physics3d-gpu are run by hand, on a Mac for Metal and on an NVIDIA box for CUDA.

Two rules that have repeatedly paid off in this project.

Physics has its own comparison path against live MuJoCo — see Validating the physics. Order matters when chasing a mismatch: forward kinematics first, then the model build, then the integrator, then the solver.

Finite differences work, with two constraints:

  • eps ≈ 1e-2 for deep chains. Smaller steps drown in accumulated float error.
  • Never on TF32. The reduced mantissa makes the comparison meaningless. Force fp32.

Some gates are expected to fail on one backend:

  • pixi run box-box-sap-gpu fails on Metal: the block SAP kernel drops the box/box pair.
  • pixi run libero-osc-batched is NVIDIA only — Metal cannot compile the kernel library at that model size.
  • Parts of test-cpu need references/ or ROMs and fail on a bare runner.

Put it in the directory matching the subsystem and name it test_*.mojo — discovery only picks up that prefix — with a _gpu.mojo suffix if it needs an accelerator. If it is fast, CPU-only and self-contained, add it to tests/manifests/smoke.txt so CI runs it on every push. If it needs a dump, a download or a GPU, add it to the manifest for its area and write the prerequisite commands in the manifest header.