build-and-test

star 589

Build the Motoko compiler (moc) and run tests in the motoko repo. Use when the user asks to build, compile, rebuild, run tests, run test-runner, or verify changes to OCaml source files.

caffeinelabs By caffeinelabs schedule Updated 4/27/2026

name: build-and-test description: Build the Motoko compiler (moc) and run tests in the motoko repo. Use when the user asks to build, compile, rebuild, run tests, run test-runner, or verify changes to OCaml source files.

Build and Test — Motoko Compiler

Environment Setup

The repo uses nix-direnv to automatically load the nix dev environment. The .envrc at the repo root calls use flake, and nix-direnv caches the result in .direnv/.

Setup snippet (run once per shell session)

cd "$(git rev-parse --show-toplevel)"
eval "$(direnv export bash 2>/dev/null)"
unalias moc 2>/dev/null

This navigates to the repo root (works from any subdirectory or worktree), then loads the cached nix environment (~500ms, instant after first run). PATH is set so GNU tools (paste, sed, grep) take precedence over macOS system tools, and all project env vars (MOTOKO_BASE, MOTOKO_CORE, etc.) are set. Shell state persists across subsequent calls, so this only needs to run once.

Important: The unalias moc is still needed. The user's shell may have moc aliased to a mops-installed version (e.g. moc=$(mops toolchain bin moc)). In zsh, aliases take precedence over PATH, so even with the correct PATH, run-test would silently use the wrong moc. Always verify with which moc — it should print the local bin/moc or src/moc path, not a mops path.

Important: Stay in the repo root. Both test-runner and dune build --root src expect to be run from there. test-runner will fail silently if the expected test directories are not found relative to cwd.

Building

The dune project root is src/, not the repo root.

# Full build — all native exes, JS targets, and inline tests (recommended)
dune build --root src

# Fast build — only the moc native binary
dune build --root src exes/moc.exe

Use dune build (all targets) to catch errors in JS targets (moc.js, moc_interpreter.js, didc.js) and other executables (mo-ld, didc, etc.) that dune build exes/moc.exe would miss.

src/moc is a symlink to _build/default/exes/moc.exe.

Alternatively, src/Makefile wraps dune with convenient targets:

make -C src          # build all native exes + grammar (equivalent to dune build)
make -C src moc      # build just moc

The user typically runs dune build -w in a separate terminal for continuous rebuilds. If the user says they have a watch build running, skip the build step — src/moc is already up to date.

Running Tests

Tests live in test/ with subdirectories per category: fail/, run/, run-drun/, trap/, etc. Use test-runner (a Rust tool in the nix shell) to run tests. It wraps run-test, runs tests in parallel (8 threads), and automatically applies the correct flags per test category (-t for fail, -d for run-drun, etc.).

All test-runner commands must be run from the repo root. Always pass -b for batch mode (the interactive picker doesn't work for agents).

Examples

test-runner -baf fail/             # all fail tests + accept changes (~25s)
test-runner -baf contextual-dot    # word match — all tests with "contextual-dot" in the path
test-runner -baf "lambda.*"        # regex match
test-runner -bf fail/              # run without accepting (check for diffs)

Flags

Flag Purpose
-a Accept: update ok/ files with actual output
-f <pattern> Filter tests by name (word match or regex)
--in-file Match filter against test output file contents instead of names
--just-tc Typecheck only (overrides per-category defaults)

Never update expected output manually — always use -a to accept changes.

Avoid running full run/ or run-drun/ suites locallyrun/ takes 1–2 minutes, run-drun/ significantly longer. The full fail/ suite is fine (25 seconds). For run/ and run-drun/, prefer running only the specific tests relevant to your change:

test-runner -baf some-test         # targeted by name pattern

JS Tests

nix build .#js.moc .#js.moc_interpreter .#js.didc -L

Builds JS artifacts and runs matching tests in test/ (e.g. test/test-moc.js).

Test File Conventions

  • //MOC-FLAG --some-flag: extra flags passed to moc
  • //MOC-ENV VAR=value: environment variables for the test
  • //SKIP ext: skip a specific output extension
  • //FILTER ext <cmd>: pipe test output for phase ext through <cmd> before comparing (e.g. //FILTER comp grep -e passed)
  • //CALL ingress <canister-id> <method> "DIDL\00\00": passed to drun as additional calls. For run-drun/ tests, at least one //CALL line is required — without it the canister method is never invoked and the test silently passes with empty output.
  • //OR-CALL: like //CALL but removes the line when interpreting, allowing different behaviour between interpreter and drun
  • Expected outputs go in ok/ subdirectory as testname.ext.ok
  • Actual outputs go in _out/ (gitignored)

Troubleshooting

test-runner exits with code 1 and no output: You are not in the repo root. test-runner checks for test/ subdirectories relative to cwd and exits silently if they are missing. Run cd "$(git rev-parse --show-toplevel)" first.

paste: illegal option or sed: first RE may not be empty: Nix environment not loaded. Run the setup snippet above. If direnv export fails, check that direnv allow was run in the repo root.

library not found for -lm: Nix environment not loaded. Run the setup snippet above.

moc: target src/moc not built yet: Run dune build --root src exes/moc.exe.

Wrong moc version (from mops/system): Run unalias moc 2>/dev/null. In zsh, aliases shadow PATH lookups, so a mops alias will win even with the correct PATH. Verify with which moc — it should show the local bin/moc or src/moc path, not a mops path.

Install via CLI
npx skills add https://github.com/caffeinelabs/motoko --skill build-and-test
Repository Details
star Stars 589
call_split Forks 128
navigation Branch main
article Path SKILL.md
More from Creator
caffeinelabs
caffeinelabs Explore all skills →