Myrddin: libtestr

Summary

Libtestr is a library that makes mbld subtests easy to write. All it does is produce specifically formatted test log output which meshes well with mbld.

pkg testr =
    type ctx

    type spec = struct
    name    : byte[:]
    fn  : (ctx : ctx# -> void)
    ;;

    /* test runner */
    const run   : (specs : spec[:] -> void)

    /* assertions */
    const ok    : (ctx : ctx# -> void)
    const fail  : (ctx : ctx#, msg : byte[:], args : ... -> void)
    const check : (ctx : ctx#, cond : bool, msg : byte[:], args : ... -> void)

    generic eq  : (ctx : ctx#, a : @t, b : @t -> void) :: std.equatable @t
    generic neq : (ctx : ctx#, a : @t, b : @t -> void) :: std.equatable @t
;;

Overview

The testr library provides a simple library for running unit tests. It outputs subtest results in a format that mbld will be able to understand, log, and report on.

Types

type ctx

The context for the current test. Used to record the state of the set of tests currently running.

type spec = struct
        name    : byte[:]
        fn  : (ctx : ctx# -> void)
;;  

The specification for a single test. Contains the name of the test being run, and the code used to execute it. The function takes a context, which is used to

Runner

const run   : (specs : spec[:] -> void)

Runs a sequence of tests, recording the state of the test and outputting an appropriate log for mtest to consume.

Aserts

const ok    : (ctx : ctx# -> void)

Records a test success. It does not leave the current scope.

const fail  : (ctx : ctx#, msg : byte[:] args : ... -> void)

Records a test failure. It does not leave the current scope. The message is used as the failure reason.

const check : (ctx : ctx#, cond : bool, msg : byte[:], args : ... -> void)

Records a test result. If cond is true, a success is recorded, otherwise a failure with the messsage msg is recorded.

generic eq  : (ctx : ctx#, a : @t, b : @t -> void) :: std.equatable @t
generic neq : (ctx : ctx#, a : @t, b : @t -> void) :: std.equatable @t

Asserts that two values are equal or unequal, respectively.

Example

use std
use testr

const main = {
    testr.run([
        /* 64 bit tests */
        [.name="nop", .fn={ctx; testr.ok(ctx)}],
        [.name="alwaysfail", .fn={ctx; testr.fail(ctx, "I was always a failure")}],
        [.name="inteq", .fn={ctx; testr.check(ctx, 1 == 1, "identity is identity")}],
        [.name="intneq", .fn={ctx; testr.check(ctx, 1 != 2, "nonidentity isn't identity")}],
        [.name="flt64-exptoobig", .fn={ctx; floatparse(ctx, "1.7976931348623157e309", std.flt64inf())}],
    ][:])
}

const floatparse = {ctx, str, expectval
    match std.flt64parse(str)
    | `std.Some f:
        testr.check(ctx, f == expectval, \
            "mismatch: {} (from {}) != {}\n", f, str, expectval)
    | `std.None:    
        testr.fail(ctx, "unable to parse float from {}\n", str)
    ;;
}