A test is an ordinary function marked @test. atoll test discovers them,
runs each one, and reports a summary.
fn add(a: int, b: int): int {
return a + b
}
@test
fn add_sums_two_numbers(): void {
if add(2, 3) != 5 {
println("expected 5")
}
}$ atoll test calc.at
test add_sums_two_numbers ... ok
result: 1 ok, 0 failed, 0 ignored@test is per-function and never inherited from a containing type — methods
inside a decorated struct are not tests.
How a test fails
A test fails by returning through the error channel. Any other completion
is a pass. So an assertion is an if that raises a declared error:
error TestFailure {
Mismatch { expected: int, actual: int }
}
fn add(a: int, b: int): int { return a + b }
@test
fn add_is_correct(): void ! TestFailure {
got := add(2, 3)
if got != 5 {
error Mismatch { expected: 5, actual: got }
}
}
@test
fn deliberately_fails(): void ! TestFailure {
error Mismatch { expected: 1, actual: 2 }
}$ atoll test calc.at
test add_is_correct ... ok
test deliberately_fails ... FAILED (tag=1 value=0)
result: 1 ok, 1 failed, 0 ignoredThe runner reports the raw error tag and payload rather than a rendered message, so give variants names and fields that identify the failure on their own.
There is no assertion library in the prelude. Write the comparison you mean, and factor repeated checks into helper functions that raise on mismatch.
Skipping a test
@ignore on a @test function marks it as skipped. It is still discovered and
reported, so it does not disappear silently.
@test
@ignore
fn slow_case(): void {
println("not run by default")
}$ atoll test --list calc.at
calc.at:5 add_sums_two_numbers
calc.at:12 slow_case (ignored)
2 test(s) discoveredRunning a subset
| Flag | Effect |
|---|---|
--list |
Print discovered tests and exit without running them |
--filter <SUBSTR> |
Only run (or list) tests whose name contains the substring |
-O <LEVEL> |
Compile at the given optimization level |
$ atoll test --filter correct calc.at
test add_is_correct ... ok
result: 1 ok, 0 failed, 0 ignoredPassing a directory instead of a file compiles every .at file beneath it as
one project and runs all the tests it finds, so tests can live beside the code
they cover across a multi-module project.
Exit codes
atoll test exits 0 when every discovered test passed, 1 when at least one
failed, and 2 on an I/O error such as an unreadable input. That makes it
usable directly as a CI gate.
Benchmarks
@bench marks a function for atoll bench, which invokes each one repeatedly
and reports min/mean/max/stddev in nanoseconds. --list and --format json
work the same way as for tests.
fn add(a: int, b: int): int { return a + b }
@bench
fn bench_add(): void {
mut total := 0
for i in 0..1000 {
total = add(total, i)
}
}Benchmark at -O2 (the default) unless you are specifically measuring the
effect of a lower optimization level — -O0 skips most of the pipeline and its
numbers do not describe a shipped build.