Lyricdev
GitHub

std.test

Tests and benchmarks: the assertions a test fails by (assertEq, assertTrue, assertThrows, assertPanics and the others, fail), subtest, and the runners that lyric test (run, TestCase) and lyric bench (runBenches, BenchCase) hand a package's tests and benchmarks to. A test is a function marked @Test — no parameters, void, possibly throwing — in a module of the package or under its tests/.

An assertion that does not hold panics with what it got against what was expected, each as Debug writes it, and names the expression it got it from: xs.length() = 2, expected 3. The actual value comes first. Two values too long to compare by eye get their first difference named below: a text's first line that differs, else the first character.

Examples

Compiles; not run here

import std.test { assertEq, assertTrue };

@Test
fn aListGrows() {
    let xs = List.of([1, 2]);
    xs.push(3);
    assertEq(xs.length(), 3);
    assertTrue(xs.contains(3));
}

Types

TestCase struct — One test as lyric test hands it to run: its name — the module's path and the function's —, its body, why it is skipped, if it is, how many seconds it may take, and whether it runs beside the others that say so (@Test { skip = "…", timeout = 10, parallel = true }).
BenchCase struct — One benchmark as lyric bench hands it to runBenches: its name — the module's path and the function's — and its body, handed a bench to measure through.

Functions

pub fn run(cases: TestCase[]): int

Runs the tests one after another, each in a task of its own: a panic or an error that leaves a test fails it, and the run goes on. Those marked parallel run after the others, all at once. It writes a line for each test once it has ended — what the test printed comes before —, then the count. A test that outlives its time ends the run as a failure. Returns the program's exit code: 0 when no test failed, 1 when one did.

pub fn subtest(name: string, body: fn() -> void throws Error): void

Runs a part of the running test, reported under its own name — parent/name — and run in a task of its own, so the test goes on after one fails: a table-driven test, a row each. A failed subtest fails the test.

Panics

Fails the test when called from a parallel test, which has no subtests: it runs beside others, and a subtest is reported under the one test that runs.

pub fn assertThrows<E :: [Error]>(f: fn() -> void throws E, message: string = ""): E

Fails the test unless f throws, and returns what it threw — of the type f says it throws —, for the test to look at.

pub fn assertPanics(f: fn() -> void throws Error, message: string = ""): PanicInfo

Fails the test unless f, run in a task of its own, panics; returns what the panic left.

pub fn assertEq<T :: [Equatable, Debug]>(
    actual: T,
    expected: T,
    message: string = "",
    @callerExpr(actual) written: string = ""
): void

Fails the test unless actual equals expected: xs.length() = 2, expected 3 — the expression the call wrote for actual, where it is not the value itself —, then message, then, for values too long to compare by eye, their first difference.

pub fn assertNotEq<T :: [Equatable, Debug]>(
    actual: T,
    unexpected: T,
    message: string = "",
    @callerExpr(actual) written: string = ""
): void

Fails the test where actual equals unexpected — the assertion for "it changed", where assertEq says "it is".

pub fn assertTrue(
    condition: bool,
    message: string = "",
    @callerExpr(condition) written: string = ""
): void

Fails the test unless condition holds.

pub fn assertFalse(
    condition: bool,
    message: string = "",
    @callerExpr(condition) written: string = ""
): void

Fails the test where condition holds.

pub fn assertNull<T :: [Debug]>(
    value: ?T,
    message: string = "",
    @callerExpr(value) written: string = ""
): void

Fails the test where value is present.

pub fn assertNotNull<T>(
    value: ?T,
    message: string = "",
    @callerExpr(value) written: string = ""
): T

Fails the test where value is absent; returns the value otherwise, for the test to go on with.

pub fn assertClose(
    actual: float,
    expected: float,
    tolerance: float,
    message: string = "",
    @callerExpr(actual) written: string = ""
): void

Fails the test unless actual lies within tolerance of expected — the comparison assertEq cannot make for floating point, where 0.1 + 0.2 is not 0.3. NaN on either side fails: it is close to nothing.

pub fn assertLess<T :: [Ordered, Debug]>(
    actual: T,
    bound: T,
    message: string = "",
    @callerExpr(actual) written: string = ""
): void

Fails the test unless actual is less than bound.

pub fn assertGreater<T :: [Ordered, Debug]>(
    actual: T,
    bound: T,
    message: string = "",
    @callerExpr(actual) written: string = ""
): void

Fails the test unless actual is greater than bound.

pub fn assertContains<T :: [Debug], C :: [Contains<T>, Debug]>(
    container: C,
    x: T,
    message: string = "",
    @callerExpr(container) written: string = ""
): void

Fails the test unless container holds x — what x in container asks (Contains): assertContains("abc", 'b'), assertContains(xs, 3).

pub fn assertEmpty<C :: [Iterable, Debug]>(
    c: C,
    message: string = "",
    @callerExpr(c) written: string = ""
): void throws C.Iter.Error

Fails the test unless c holds nothing: its iterator gives no value — a list, a map, a set, an array, a slice.

Errors

Throws what the iterator of c throws — nothing, for the collections above.

pub fn fail(message: string): never

Fails the test outright, with message — for a branch a test must not reach.

pub fn runBenches(cases: BenchCase[], save: string = "", compare: string = ""): int

Runs the benchmarks one after another on this thread, each with a bench of its own, and writes a line for each: the nanoseconds per run, its allocations and their bytes, the megabytes per second where the bench said how many bytes a run handles, and the runs measured. Where compare names a file an earlier run saved, each line also says how the time per run changed against it; where save names one, this run writes it — a line per benchmark: its name, then the nanoseconds, the allocations and the bytes per run, separated by tabs. Returns the program's exit code: 0, or 1 where such a file could not be read or written.