(std test)

Submodules:

Reference

Overview

Tests that are silent unless they fail: deftest, check, and run-tests as the file’s main.

(import (std prelude))
(import (std test))

(deftest "parse adds"
  (check (= (parse "1+2") (Ok 3))))

(deftest "a bad number is an error"
  (check-match (parse "x") (Err _)))

(run-tests)

deftest registers a test where it is written. (run-tests) is the main of the file: it runs every test that the module registered, in source order. A test that passes prints nothing. A test that fails prints its name, its file and line, and each check in it that failed, with the values that the check saw. The run ends with one line, and the program exits with 1 if a test failed:

FAIL parse adds (parse_test.bjo:4)
  parse_test.bjo:5: (check (= (parse "1+2") (Ok 3)))
    (parse "1+2") => (Ok 4)
    (Ok 3)        => (Ok 3)
1 of 2 test(s) failed in 12 ms.

Choosing tests. The arguments of the program choose the tests. A test runs when its name contains one of the arguments. With no arguments, every test runs. Arguments that start with -- are flags, such as --seed and --quick: see parse-test-args. bjo test builds and runs the test files that it is given, or every file under tests/ in the project. It gives its other arguments to each file.

Options. A test can be skipped (#:skip), expected to fail (#:todo), left out of quick runs (#:slow), or run once for each row of a table (#:cases). See deftest.

More kinds of tests. (std test prop) has property tests: for-all runs a body with many generated values, and shrinks a failing case to a small one. (std test gen) has the generators. (std test expect) compares output with a text in the test, and --promote updates the text.

Each test runs in a scope of its own, as with-test in (std simpletest) does. The scope has a deadline: 5 seconds, or #:within d, a Duration. The scope waits for the fibers that the test started. The test fails if it leaves a resource open, unless it has #:open-ok. A test that raises an exception fails with the message of the exception, and the tests after it still run. A failed check does not stop its test, so one run shows all the failed checks.

Each module runs only its own tests. Every module that imports this one registers into the same list. A test records the module it was written in, from (current-module), and (run-tests) runs only the tests of its module. Thus a test file can import a library that has tests of its own, and it does not run them.

(std test) and (std simpletest). (std simpletest) is the older harness. Its expect prints a line for each assertion, and a failure is a line that starts with FAILURE:, which is what the compiler’s own test runner looks for. It has no registry: a test file is an ordinary program that calls the assertions from main. Use (std test) for new tests and with bjo test. The two can be used together: with-fake-fs from (std simpletest) works inside a deftest.

See also: deftest, check, run-tests

Reference

Types

Functions

Macros

Types

Test

record ↑ Types

A registered test: its name, where it is, its options and its body.

name
The name given to deftest. The filters of a run match against it.
module
The key of the module that registered it, as (current-module) gives it.
file
The path of the source file that it is written in.
line
The line of the deftest.
within
The deadline in milliseconds. 0 means the default, 5000.
open-ok
#t when the test can leave resources open.
skip
The reason given with #:skip, or empty. A test with a reason does not run.
todo
The reason given with #:todo, or empty. A failure of such a test does not fail the run.
slow
#t for a #:slow test, which --quick leaves out.
capture
#t when the body uses expect-output. The output of the test then goes to a string port that take-test-output! reads.
body
The forms of the test, as a function. run-test calls it.

See also: deftest, run-test, registered-tests

TestOptions

record ↑ Types

What the arguments of a test program asked for.

filters
Parts of names. A test runs when its name contains one of them. When empty, every test runs.
seed
The seed of the run: from --seed, from the environment variable BJO_TEST_SEED, or from the operating system.
runs
The number of runs of a for-all that does not give #:runs, from --runs. 0 when not given.
shrink-limit
The most replays that shrinking does, from --shrink-limit. 0 when not given.
stats
#t with --stats: print the counts of classify and collect.
quick
#t with --quick: leave out the #:slow tests.
promote
#t with --promote: write the output that was seen into the texts of expect-output.
store
#f with --no-store: do not read or write the failure file of (std test prop).
junit
The file of --junit, to write the results to as JUnit XML.

See also: parse-test-args, current-test-options

TestStatus

union ↑ Types

How a test came out.

Passed
It ran, and nothing failed.
Failed
Something in it failed.
Skipped
It has #:skip, so it did not run.
Todo
It has #:todo, and it failed, as expected. The run does not fail.
Fixed
It has #:todo, and it passed. The run says so, so that the mark can be removed.

See also: TestResult, run-test

TestResult

record ↑ Types

What running one test came to.

test
The test that ran.
status
How it came out.
failures
The messages of its failures, in the order they occurred. Empty when it passed.

See also: run-test

Functions

test-name

function ↑ Functions

(: test-name (-> Test string))
(test-name t)

The name of a test.

t
The test.
returns
The name given to deftest.

test-module

function ↑ Functions

(: test-module (-> Test string))
(test-module t)

The module that registered a test.

t
The test.
returns
The key of the module, as (current-module) gives it.

See also: module-tests

test-file

function ↑ Functions

(: test-file (-> Test string))
(test-file t)

The source file of a test.

t
The test.
returns
The path of the file, as the compiler read it.

See also: test-line

test-line

function ↑ Functions

(: test-line (-> Test int))
(test-line t)

The line of a test’s deftest.

t
The test.
returns
The line number.

See also: test-file

register-test!

function ↑ Functions

(: register-test! (-> string string string int (Option Duration) bool string string bool bool (-> void) int))
(register-test! name module file line within open-ok skip todo slow capture body)

Adds a test to the registry. deftest writes calls to it.

name
The name of the test.
module
The key of the module, from (current-module).
file
The source file.
line
The line of the test.
within
The deadline in milliseconds, or 0 for the default.
open-ok
#t when the test can leave resources open.
skip
The reason of #:skip, or empty.
todo
The reason of #:todo, or empty.
slow
#t for #:slow.
capture
#t when the body uses expect-output.
body
The body of the test.
returns
Always 0.

It answers a number so that deftest can bind the result with a top-level def. A top-level def runs before main, so all the tests are registered before (run-tests) runs them. It is exported because the template of deftest names it. Call deftest, not this function.

See also: deftest

registered-tests

function ↑ Functions

(: registered-tests (-> (List Test)))
(registered-tests)

Every test registered so far, by every module, in the order they were registered.

returns
The tests, oldest first.

See also: module-tests

module-tests

function ↑ Functions

(: module-tests (-> string (List Test)))
(module-tests module)

The tests that one module registered, in the order they were written.

module
The key of the module, as (current-module) gives it in that module.
returns
The tests of that module, oldest first.
(module-tests (current-module))   ; the tests of this module

See also: registered-tests, run-registered-tests

parse-test-args

function ↑ Functions

(: parse-test-args (-> (Vec string) (Result string TestOptions)))
(parse-test-args args)

Reads the arguments of a test program: flags, and name filters.

args
The arguments.
returns
The options, or why the arguments are wrong.

An argument that starts with -- is a flag. The other arguments are name filters.

Without --seed, the seed comes from the environment variable BJO_TEST_SEED, and else from the operating system. bjo test gives these flags to each test file: bjo test --seed 7.

See also: TestOptions, run-registered-tests

current-test-options

function ↑ Functions

(: current-test-options (-> TestOptions))
(current-test-options)

The options of the run in progress.

returns
The options that run-registered-tests read, or the options of a run without arguments when no run is in progress.

See also: TestOptions

test-seed

function ↑ Functions

(: test-seed (-> Test ulong))
(test-seed t)

The seed of one test: the seed of the run, mixed with the test’s name.

t
The test.
returns
The seed.

Because the name is part of the seed, choosing tests with filters, or adding a test, does not change what the other tests draw.

See also: mix-seed, string-hash

mix-seed

function ↑ Functions

(: mix-seed (-> ulong ulong ulong))
(mix-seed a b)

Two seeds mixed into one, so that seeds that differ in one bit give unrelated results.

a
A seed.
b
Another seed.
returns
The finalizer of SplitMix64, applied to a xor b.

string-hash

function ↑ Functions

(: string-hash (-> string ulong))
(string-hash s)

A 64-bit hash of a string that is the same in every process.

s
The string.
returns
FNV-1a over the characters of s.

.NET’s own string hash is different in each process, so it can not make a seed that has to be the same in every run.

running-test

function ↑ Functions

(: running-test (-> (Option Test)))
(running-test)

The test that is running.

returns
The test, or None when no test runs.

See also: run-test

test-time-left

function ↑ Functions

(: test-time-left (-> long))
(test-time-left)

The milliseconds left before the running test’s deadline.

returns
The time left, or a day when no test runs.

See also: running-test

take-test-output!

function ↑ Functions

(: take-test-output! (-> (Option string)))
(take-test-output!)

What the running test wrote since it started, or since the last call.

returns
The text, or None when the test does not capture its output.

A test captures its output when its body uses expect-output: current-output-port is then a string port while the test runs. What no call took is printed when the test ends, so that the prints of a test are not lost.

See also: test-time-left

next-test-index!

function ↑ Functions

(: next-test-index! (-> string int))
(next-test-index! key)

The number of times this was called before with the same key in the running test.

key
What is counted: for-all gives the source text of its bindings.
returns
0 at the first call with a key in a test, then 1, 2 and so on.

for-all takes its position among the for-alls of its test that have the same bindings from it, to find the cases that the failure file holds for it.

failures-into

function ↑ Functions

(: failures-into (-> (Box (List string)) (-> %a) %a))
(failures-into failures body)

Runs a function with the failures of its checks going to a box instead of to the running test.

failures
The box. Each failure is added at its front.
body
The function.
returns
What the function answers.

The failures of the running test are put back afterwards, also when the function raises an exception. for-all runs each case this way, to decide which failures to keep.

See also: test-failed!

test-failed!

function ↑ Functions

(: test-failed! (-> string void))
(test-failed! message)

Records a failure of the running test, or prints it and fails the program when no test runs.

message
The text of the failure, printed as it is given.

Use it to write a check of your own. In a test, the message is added to the failures of the test, and the test goes on. Outside a test, the message is printed at once, and the exit code of the program is set to 1, so that the program fails even when main answers 0.

The run prints a failure under the FAIL line of its test, so start the message with two spaces to align it with the other failures.

See also: check-failed!, run-test

check-failed!

function ↑ Functions

(: check-failed! (-> string int string (List (Tuple string string)) void))
(check-failed! file line source seen)

Records the failure of a check: where it is, its source, and the values it saw.

file
The source file of the check.
line
The line of the check.
source
The source text of the check.
seen
Pairs of source text and value, one pair for each value to show.

The checks of this module call it. It is exported because their templates name it. It formats the failure as below, with the source texts padded to the same width, and gives it to test-failed!. Only the file name of file is shown.

file.bjo:12: (check (= a b))
  a => 1
  b => 2

See also: test-failed!, check

check-raises-message

function ↑ Functions

(: check-raises-message (-> string int string string string void))
(check-raises-message file line source message part)

The message part of check-raises: fails when the message does not contain the part.

file
The source file of the check.
line
The line of the check.
source
The source text of the check.
message
The message of the exception.
part
What it has to contain.

It is exported because the template of check-raises names it. Write check-raises with #:message, not this function.

See also: check-raises

approx-close?

function ↑ Functions

(: approx-close? (-> double double double bool))
(approx-close? a b eps)

Whether two doubles are near each other, as check-approx tells.

a
A double.
b
Another double.
eps
The bound.
returns
#t when they are equal, or differ by at most eps times the larger of 1 and their sizes.

It is exported because the template of check-approx names it.

See also: check-approx

test-result-test

function ↑ Functions

(: test-result-test (-> TestResult Test))
(test-result-test r)

The test that a result is for.

r
The result.
returns
The test.

test-result-status

function ↑ Functions

(: test-result-status (-> TestResult TestStatus))
(test-result-status r)

How a test run came out.

r
The result.
returns
The status.

test-result-failures

function ↑ Functions

(: test-result-failures (-> TestResult (List string)))
(test-result-failures r)

The failures of a test run.

r
The result.
returns
The failure messages, in the order they occurred. Empty when the test passed.

run-test

function ↑ Functions

(: run-test (-> Test TestResult))
(run-test t)

Runs one test in a scope of its own, and answers what it came to. Prints nothing.

t
The test.
returns
Its result, with each failure as the run prints it.

The body runs under with-deadline, with the deadline of the test. The failures of the checks in the body are collected, not printed. The test also fails:

- when the body leaves a resource open at its end, unless the test has #:open-ok. The check is done before the scope closes, because the scope releases the resources when it closes. - when the deadline ends the scope. A fiber that the test leaked is cancelled by the deadline, and the scope then closes cleanly, so without this the test would pass, slowly. - when the body raises an exception. The failure shows the message.

Use it to run tests and examine the results, for example to test a check that must fail.

See also: run-registered-tests, TestResult

run-registered-tests

function ↑ Functions

(: run-registered-tests (-> string (Vec string) int))
(run-registered-tests module args)

Runs the tests of a module that the filters choose, prints the failures, and answers an exit code.

module
The key of the module, as (current-module) gives it.
args
The arguments of the program: flags, and parts of names. See parse-test-args.
returns
0 when no test failed, 1 when one failed, 2 when the arguments are wrong. 0 also when no test was chosen.

For each test that fails, it prints a line FAIL name (file:line) and then the failures. For a #:todo test that passes, it prints a line FIXED name (file:line). At the end it runs the hooks given to at-run-end!, writes the JUnit file when --junit asks for one, and prints one line: how many tests passed or failed, how many were skipped or todo, and the time the run took, or that there were no tests to run. (run-tests) writes a main that calls it.

See also: run-tests, run-test

at-run-end!

function ↑ Functions

(: at-run-end! (-> string (-> void) void))
(at-run-end! key hook)

Adds a function to what runs when a run of tests ends.

key
A name for the hook. A second hook with the same key is ignored.
hook
The function.

run-registered-tests runs the hooks after the last test, oldest first, before it prints its last line. (std test prop) writes its failure file this way, and (std test expect) its promotions.

See also: run-registered-tests

deftest-cases-row

function ↑ Functions

(: deftest-cases-row (-> int (List (Tuple string string)) string))
(deftest-cases-row i seen)

The heading of a failed row of a #:cases test.

i
The number of the row, from 1.
seen
The column names, each with the source text of its value.
returns
The heading, and a line for each column.

It is exported because the template of deftest names it.

Macros

check

macro ↑ Macros

(check expr)

Passes when the expression is #t. A failed comparison shows both operands.

expr
The expression to test.
(check (= (parse "12") (Ok 12)))
(check (< (length xs) 10))
(check (string-starts-with? s "a"))

When expr is a call to =, <, >, <= or >= with two operands, each operand is evaluated once, in order, and a failure shows the source and the value of both. For any other expression, a failure shows that the expression was #f.

A failed check does not stop the test. Outside a test, a failure is printed at once and the program exits with 1.

See also: check-match, check-raises, check-fail

check-match

macro ↑ Macros

(check-match expr pattern)

Passes when the value of the expression matches the pattern. A failure shows the value.

expr
The expression. It is evaluated once.
pattern
A match pattern. The names that it binds are not available after the check.
(check-match (parse "x") (Err _))
(check-match (lookup m "k") (Some (Tuple _ 3)))

See also: check

check-raises

macro ↑ Macros

(check-raises type expr)
(check-raises type expr #:message part)

Passes when the expression raises an exception of the type.

type
A .NET exception type, by its name or by an import/class alias.
expr
The expression to evaluate.
part
A string that the message of the exception has to contain.
(check-raises System.FormatException (string->int "x"))
(check-raises System.ArgumentException (make-inbox 0 DropNewest) #:message "capacity")

When expr answers a value, the check fails and shows the value. Only exceptions of type are caught. An exception of a different type is not caught, so the test fails as a test that raises does, and the checks after it in the same test do not run.

See also: check

check-fail

macro ↑ Macros

(check-fail why)

Fails where it is: for a branch that a test must not reach.

why
A string that tells why the branch is wrong. The failure shows it.
(match (parse "12")
  ((Ok n) (check (= n 12)))
  ((Err e) (check-fail e)))

See also: check

check-approx

macro ↑ Macros

(check-approx a b)
(check-approx a b #:eps eps)

Passes when two doubles are near each other. A failure shows both.

a
A double. It is evaluated once.
b
A double. It is evaluated once.
eps
The bound. 1e-9 when it is not given.
(check-approx (sqrt 2.0) 1.4142135623730951)
(check-approx (mean xs) 2.5 #:eps 1e-6)

Two values are near when they are equal, or when they differ by at most eps, for values at most 1 in size, or by at most eps times the larger of the two sizes, for larger values. So the bound is absolute near 0 and relative away from it. Equal infinities are near. NaN is near nothing, also NaN.

See also: check

check-output

macro ↑ Macros

(check-output expr text)

Passes when what the expression writes to current-output-port is the text.

expr
The expression. Its value is dropped.
text
A string: what it has to write.
(check-output (display "hi") "hi")
(check-output (print-report r) "total: 3\n")

The expression writes to a string port instead of the real output, so nothing is printed. A failure shows what it wrote and what was wanted. For longer output, expect-output from (std test expect) shows a diff, and --promote updates the text.

See also: check

deftest

macro ↑ Macros

(deftest name body ...)
(deftest name #:within d body ...)
(deftest name #:open-ok body ...)
(deftest name #:skip why body ...)
(deftest name #:todo why body ...)
(deftest name #:slow body ...)
(deftest name #:cases ((column ...) row ...) body ...)

Registers a test. (run-tests) runs it.

name
A string: the name of the test. The filters of a run match against it.
d
The deadline of the test, a Duration. Five seconds when it is not given.
why
A string: the reason, which the run shows.
column ...
The names that each row binds.
row ...
A list of values, one for each column.
body ...
The forms of the test. At least one form. Their values are dropped.
(deftest "parse adds"
  (check (= (parse "1+2") (Ok 3))))

(deftest "the server answers" #:within (seconds 1)
  (check-match (fetch url) (Ok _)))

(deftest "parse-int" #:cases ((input expected)
                              ("1"  (Ok 1))
                              ("-0" (Ok 0))
                              ("x"  (Err _)))
  (check-match (parse-int input) expected))

(deftest "unicode case folding" #:todo "needs full case folding"
  (check (string-ci=? "STRASSE" "straße")))

Write it at the top level of a module: it expands to a top-level def, so the test is registered before main runs. The options come after the name, in any order.

The test runs in a scope with a deadline, as run-test tells.

See also: run-tests, check, run-test

run-tests

macro ↑ Macros

(run-tests)

Writes the main of a test file: it runs every test that this module registered.

(deftest "one" (check (= 1 1)))
(run-tests)

It defines main, which takes the arguments of the program as name filters and answers the exit code of run-registered-tests. Thus a test file is a program, and it can have no other main.

All the tests of the module run, also the tests written after (run-tests), because every deftest is registered before main starts. The tests of other modules, also of modules that this one imports, do not run.

See also: deftest, run-registered-tests