table
import std::test::table; · source
Free Functions
Table-driven tests
v1.0 uses the inline-loop pattern: the test owns a loop over a const array of cases and calls the ordinary assertions on each row. The helpers here turn a per-row failure into a message naming the offending case, so a failure in row 7 reads case [7]: ... instead of an anonymous assertion failure.
type struct Case { input: i32; want: i32; }
![test]
function doubling_table() -> Result<(), TestError> {
const cases: [Case] = [
Case { input: 1, want: 2 },
Case { input: 2, want: 4 },
Case { input: 3, want: 6 },
];
mut i: i64 = 0;
while (i < cases.length) {
const c: &Case = &cases[i];
match (expect_eq(c.input * 2, c.want)) {
Result::Ok(_) => { }
Result::Err(e) => { return Result::Err(prefix_case(i as u64, e)); }
}
i = i + 1;
}
return Result::Ok(());
}
prefix_named_case labels the row by name rather than index.
function prefix_case(index: u64, e: TestError) -> TestError;
function prefix_case(index: u64, e: TestError) -> TestError;Wrap e's message with a case [<index>]: prefix, preserving the error's variant (Failed vs Other). Consumes e and returns the new error; call it on the failing-row path: return Result::Err(prefix_case(i as u64, e));
function prefix_named_case(name: Str, e: TestError) -> TestError;
function prefix_named_case(name: Str, e: TestError) -> TestError;Like prefix_case but uses a caller-supplied case label instead of a numeric index: case [empty input]: .... Useful when the table rows carry their own names.
type struct Name { ... }
type enum Name { ... }
type trait Name { ... }
type union Name { ... }Begins a type declaration; the keyword that follows says which kind. On its own, type Name = Existing; declares an alias.
type struct Name {
field: Type;
method(&this) -> Ret { ... }
}A value type with named fields and optional methods. Structs live on the stack and are passed by value. Fields are public by default and may carry = default values; methods take their receiver as &this, mut &this, or this.
type struct CaseCase.input: i32Case.want: i32function name<T>(param: Type) -> Ret { ... }Declares a function. Parameters are written name: Type and are never inferred; the return type follows -> and defaults to void. Declaration order does not matter: every signature is collected before any body is checked, so functions may call each other freely and recurse.
function doubling_table() -> Result<(), TestError>primitive ()The unit type: a value that carries no information, and the empty tuple. Distinct from void, which is the absence of a value - Result<(), E> succeeds with nothing to return. 0 bytes.
type enum TestError {
Failed(String);
Other(String);
}Why a test failed. The runner prints the variant's message verbatim to stderr from the child process before exiting non-zero, so the parent can show the failing message in the test summary.
std::test::errorconst name: Type = value;An immutable binding: it cannot be reassigned after initialisation. The annotation may be omitted when an initialiser is present - the binding takes the initialiser's concrete type, inferred locally. Mutability is opt-in through mut.
const cases: [Case]type struct CaseCase.input: i32Case.want: i32mut name: Type = value;A mutable binding, reassignable after declaration - mutability is opt-in, const is the default. At module level it declares mutable global state. Before & it marks an exclusive reference or receiver instead.
mut i: i64while (condition) { ... }Repeats its body while the condition holds. The condition is checked before every iteration, so the body may run zero times.
language referencemut i: i64const cases: [Case]Array.length: i64const c: &Casematch (subject) {
Pattern(x) => { ... }
_ => { ... }
}The primary discriminator: branches on enum variants, integers, ranges, and destructuring patterns, and the compiler rejects a match that misses a case. There is no fallthrough. As an expression it evaluates to the chosen arm's value, so every arm must produce the same type.
function expect_eq<T>(a: T, b: T) -> Result<(), TestError>
where T: Eq + DisplayOk(()) if a.equals(&b), otherwise Err carrying a multi-line "assertion left == right failed" message that Display-formats both values. Allocates the message on the heap; on allocation failure during message building, returns a generic fallback message.
const c: &CaseResult::Ok(T)_pattern => { body }Separates a match arm's pattern from its body. Arms do not fall through.
Result::Err(E)e: TestErrorreturn value;Leaves the current function with the given value. A function whose return type is not void must return one; a bare return; is for void. In an async function it completes the future with the value.
function prefix_case(index: u64, e: TestError) -> TestErrorWrap e's message with a case [<index>]: prefix, preserving the error's variant (Failed vs Other). Consumes e and returns the new error; call it on the failing-row path: return Result::Err(prefix_case(i as u64, e));
value as TypeExplicit conversion: between numeric types, or reinterpreting one pointer type as another. Cryo never converts implicitly, and as inserts no range checks - a narrowing cast is the programmer's responsibility.
e: TestErrorimport a::b;
import a::b::{ X, Y };
import a::b as c;
import a::b::*;Brings a module, or selected items from it, into scope. One path per declaration: use the brace form for several items, as to alias, and * for everything public (prefer the brace form; wildcards invite name collisions).
namespace std::test::table;Parametrized (data-driven) tests: run one test body over a table of cases and identify which row failed.
module pagefunction name(count: i32, args...)A variadic parameter: the trailing bucket receives any number of further arguments with C's calling convention, so the function stays ABI-compatible with the printf family. Wrap the bucket in VaArgs to read typed values from it.
function prefix_named_case(name: Str, e: TestError) -> TestErrorLike prefix_case but uses a caller-supplied case label instead of a numeric index: case [empty input]: .... Useful when the table rows carry their own names.
function prefix_case(index: u64, e: TestError) -> TestErrorWrap e's message with a case [<index>]: prefix, preserving the error's variant (Failed vs Other). Consumes e and returns the new error; call it on the failing-row path: return Result::Err(prefix_case(i as u64, e));
(parameter) index: u64(parameter) e: TestErrorTestError::Other(String)Any other reason a test could not produce a definitive result: I/O error during setup, environment problem, etc.
std::test::errorfunction prefix_named_case(name: Str, e: TestError) -> TestErrorLike prefix_case but uses a caller-supplied case label instead of a numeric index: case [empty input]: .... Useful when the table rows carry their own names.
(parameter) name: Strtype struct Str {
bytes: u8*;
length: u64;
}Str: a borrowed UTF-8 string slice.
std::collections::str