Testing and benchmarks
A program you can't test is a program you can't change with confidence. Glide builds testing into the toolchain — no framework to install, no config. You write test_-prefixed functions in *_test.glide files and run glide test.
Your first test
Tests live next to your code (commonly in a tests/ directory) in files ending _test.glide. A test is a function whose name starts with test_, returns i32, and returns 0 to mean pass:
import stdlib::testing::*;
import stdlib::math::*;
fn test_ipow_basic() -> i32 {
assert_eq!(ipow(2, 10), 1024);
return 0;
}
fn test_gcd() -> i32 {
assert_eq!(gcd(48, 36), 12);
return 0;
}
Run them:
glide test tests/math_test.glide
# running tests/math_test.glide ( 2 tests )
#
# 2 passed, 0 failed
Each test file is compiled into its own binary and run, so global state never leaks between files. A failed assertion marks the test failed and prints where; the runner exits non-zero if anything failed, which is exactly what CI wants.
The assertion macros
stdlib::testing gives you five macros. Pick the right one — the specific ones print better failure messages:
assert!(cond); // cond must be true
assert_not!(cond); // cond must be false
assert_eq!(a, b); // equality for i32, bool, char
assert_str_eq!(a, b); // equality for strings — use this for text
assert_msg!(cond, "context"); // boolean check with a custom message
A test with several assertions fails on the first one that breaks:
import stdlib::testing::*;
fn slug(s: string) -> string {
return s.trim().to_lower().replace(" ", "-");
}
fn test_slug() -> i32 {
assert_str_eq!(slug(" Hello World "), "hello-world");
assert_not!(slug("A B").contains(" "));
return 0;
}
Running the suite
glide test takes a path, or runs everything in the current directory:
glide test # every *_test.glide under the cwd
glide test tests/ # every test file in a directory
glide test tests/math_test.glide # one file
The three layers
The runner supports tests at three granularities — the same glide test, different scope:
- Unit — one function or struct method in isolation: pure logic, validation, a parser rule. The
test_ipow_basicabove is a unit test. - Integration — several modules together:
fs+os, or your library using another. Same shape, just exercising more at once. - Golden — a whole program whose
stdoutis compared against a recorded.expectedfile. The runner builds the program, runs it, and diffs the output.
Golden tests run with a flag:
glide test --golden tests/golden
Each program under tests/golden/ is paired with a .expected file holding the output it should produce. The runner normalizes CRLF to LF, so a .expected written with Unix line endings works on Windows too. Golden tests are perfect for CLIs and code generators where the contract is the output.
Benchmarks
Correctness is one axis; speed is another. glide bench runs microbenchmarks declared as bench_-prefixed functions taking a *Bencher:
import stdlib::bench::*;
pub fn bench_sum(b: *Bencher) {
let mut total: i32 = 0;
for let i: i32 = 0; i < b.n; i++ {
total = total + i;
}
b.consume(&total); // keep `total` alive so the loop isn't optimized away
}
Run it:
glide bench sum_bench.glide
Two things make a benchmark honest:
- `b.n` is the iteration count, and the runner auto-tunes it — it scales
nup until the benchmark runs long enough to time reliably, then reports nanoseconds per operation. Your loop must run exactlyb.ntimes. - `b.consume(&x)` tells the optimizer the result is observed. Without it, an
-O2build would noticetotalis never used and delete your whole loop — you'd be timing nothing. Pass&xfor primitives, or the value directly for pointer-shaped types (b.consume(my_vector)).
Recap
Where to next
You can build, test, and measure Glide programs. The final chapter — FFI and escape hatches — goes the other direction: calling C, embedding raw C, inline assembly, and per-platform code for when you need to reach below the language.