6.2. dastest — Test Framework
dastest is the daslang testing framework, inspired by the
Go testing package. It discovers
and runs [test] and [benchmark] functions in .das files
and reports results.
6.2.1. Quick start
options gen2
require dastest/testing_boost public
[test]
def test_arithmetic(t : T?) {
t |> equal(2 + 2, 4)
t |> equal(3 * 3, 9, "multiplication")
}
Run it:
daslang dastest/dastest.das -- --test path/to/test.das
6.2.2. Running tests
dastest is itself a daslang script. All arguments after -- are
passed to dastest.
Running all tests in a file or directory:
daslang dastest/dastest.das -- --test path/to/tests/
Running all tests and benchmarks:
daslang dastest/dastest.das -- --bench --test path/to/tests/
Running benchmarks only (skip tests):
daslang dastest/dastest.das -- --bench --test path/to/tests/ --test-names none
Running specific benchmarks by prefix:
daslang dastest/dastest.das -- --bench --bench-names vector_alloc --test path/to/tests/ --test-names none
Multiple --test paths can be provided:
daslang dastest/dastest.das -- --test tests/lang --test tests/daslib
6.2.3. Command-line arguments
Argument |
Description |
|---|---|
|
Path to a folder or single |
|
Run only top-level tests whose name starts with prefix.
Use |
|
A |
|
Print URI-style paths (VS Code friendly). |
|
Print colored output. |
|
Print verbose output. |
|
Panic if total test time exceeds seconds.
|
|
Compile test files without running them. Useful for checking that tests compile after refactoring. |
|
Compile and run tests using AOT (ahead-of-time) compilation. |
|
Run each test file in a separate process. Useful for catching native crashes. |
|
Number of parallel processes in isolated mode.
Default: |
|
Per-test-file timeout in isolated mode (seconds).
|
|
After all tests pass, print the n slowest test files (timing outliers above 2 standard deviations). |
|
Enable benchmark execution (all benchmarks). |
|
Run only benchmarks whose name starts with prefix. |
|
Benchmark output format (see Benchmark output formats). |
|
Run all benchmarks n times (for sample collection). |
|
Write a JSON test report to path. Contains per-test timing, results, errors, and summary counts. Useful for CI integration. |
|
Suppress PASS and RUN output; show only failures and the final summary. Useful for large test runs where only errors matter. |
|
Enable code coverage and write an LCOV report to path. Each
test file’s coverage is appended (the file is truncated at the
start of the run). Requires |
Internal arguments (used by isolated mode):
Argument |
Description |
|---|---|
|
Path to a single file to run in isolated mode (used internally). |
Note
Benchmarks run only after all tests in the file have passed. If any test fails, benchmarks are skipped.
6.2.4. Writing tests
Import the testing framework and annotate functions with [test].
Each test function receives a T? context pointer used for assertions:
options gen2
require dastest/testing_boost public
[test]
def test_basic(t : T?) {
t |> equal(2 + 2, 4)
t |> success(true, "always passes")
}
6.2.5. Assertions
All assertion functions accept an optional message string as the last argument.
Function |
Behavior |
|---|---|
|
Check |
|
Check |
|
Check condition is truthy; non-fatal |
|
Always fails; non-fatal |
|
Suppress unused warnings (no assertion) |
6.2.6. Sub-tests
Group related checks with run:
[test]
def test_strings(t : T?) {
t |> run("concat") @@(t : T?) {
t |> equal("ab" + "cd", "abcd")
}
t |> run("length") @@(t : T?) {
t |> equal(length("hello"), 5)
}
}
Use @@(t : T?) { ... } (local function) for the callback.
6.2.7. Benchmarks
Annotate functions with [benchmark]. Each benchmark receives a
B? context. Use b |> run("name") to define sub-benchmarks:
options gen2
require dastest/testing_boost public
[benchmark]
def dyn_array(b : B?) {
let size = 100
b |> run("fill_vec") <| $() {
var vec : array<int>
for (i in range(size)) {
vec |> push(i)
}
}
b |> run("fill_vec_reserve") <| $() {
var vec : array<int>
vec |> reserve(size)
for (i in range(size)) {
vec |> push(i)
}
}
}
The runner measures iterations until the timing is stable and reports ns/op.
6.2.8. Benchmark output formats
native(default)Human-readable columnar output with colored values:
=== RUN BENCHMARK 'dyn_array' [INTERP] fill_vec 12345 ns/op 1024 B/op 100 allocs/op fill_vec_reserve 3456 ns/op 800 B/op 1 allocs/op
Columns: ns/op, B/op (heap bytes), allocs/op (heap allocations), SB/op (string heap bytes), strings/op (string allocations). Zero-allocation columns are highlighted green.
goGo benchmark format, tab-separated, compatible with
benchstatand other Go tooling:100 12345.0 ns/op 1024 B/op 100 allocs/op 0 SB/op 0 strings/op
jsonOne JSON object per sub-benchmark (one per line), containing all fields from the
BenchmarkRunStatsstruct:{"name":"dyn_array","sub_name":"fill_vec","n":100,"time_ns":1234500,"allocs":100,"heap_bytes":1024,"string_allocs":0,"string_heap_bytes":0,"func_type":"INTERP"}Fields:
name– top-level benchmark function namesub_name– sub-benchmark name (fromb |> run("..."))n– number of iterationstime_ns– total elapsed time in nanosecondsallocs– total heap allocationsheap_bytes– total heap bytes allocatedstring_allocs– total string heap allocationsstring_heap_bytes– total string heap bytesfunc_type– execution mode ("INTERP","AOT","JIT")
6.2.9. Folder filtering with .das_test
When dastest scans a directory, it visits all subfolders by default.
A .das_test file at the root of the test path lets you control which
folders are visited.
If dastest finds a .das_test file in the --test directory, it
compiles and simulates it, then calls its can_visit_folder function
for every subfolder it encounters. The function receives the folder
name and a bool pointer – set it to false to skip the folder.
If the function returns without writing to the pointer, the folder is
visited (default true).
options gen2
require daslib/rtti
require strings
[export, pinvoke]
def can_visit_folder(folder_name : string; var result : bool?) {
if (folder_name == "dasHV" || folder_name == "dasPUGIXML") {
*result = false
return
}
}
The function must be [export, pinvoke] because dastest calls it
from a different context via invoke_in_context.
has_module (from the rtti module) can check whether a module
is linked into the current binary. For example,
has_module("dashv") returns true only when dasHV is enabled.
Key points:
The
.das_testfile is a regular daslang script – it canrequiremodules and use any logic.The file is only loaded when dastest scans a directory. Running dastest on a single
.dasfile bypasses.das_testentirely.Files whose names start with
_are always skipped.If no
.das_testfile exists, all subfolders are visited.
6.2.10. Test file conventions
Test files live under
tests/or alongside the code they test.Each file must
require dastest/testing_boost public.Use
options gen2at the top.Files starting with
_are skipped by dastest.Use
expectto suppress specific compilation errors in negative-test files.
See also
Testing with dastest – introductory tutorial on writing tests
dastest/testing_boost – the testing boost module (assertions, sub-tests, benchmarks)
utils_dascov – standalone code coverage tool (parses LCOV output from --cov-path)