tvl-depot/nix/runTestsuite/default.nix
Profpatsch f4a4da134b feat(nix/runTestsuite): add assertThrows
Uses `builtins.tryEval` to check that the expression throws when
`deepSeq`-ed.

Change-Id: I0d57cc37f473bb733f57a1b1c0d889084152fd2f
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2463
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
2021-01-30 11:44:25 +00:00

157 lines
3.9 KiB
Nix
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{ lib, pkgs, depot, ... }:
# Run a nix testsuite.
#
# The tests are simple assertions on the nix level,
# and can use derivation outputs if IfD is enabled.
#
# You build a testsuite by bundling assertions into
# “it”s and then bundling the “it”s into a testsuite.
#
# Running the testsuite will abort evaluation if
# any assertion fails.
#
# Example:
#
# runTestsuite "myFancyTestsuite" [
# (it "does an assertion" [
# (assertEq "42 is equal to 42" "42" "42")
# (assertEq "also 23" 23 23)
# ])
# (it "frmbls the brlbr" [
# (assertEq true false)
# ])
# ]
#
# will fail the second it group because true is not false.
let
inherit (depot.nix.yants)
sum
struct
string
any
defun
list
drv
;
bins = depot.nix.getBins pkgs.coreutils [ "printf" "touch" ];
# rewrite the builtins.partition result
# to use `ok` and `err` instead of `right` and `wrong`.
partitionTests = pred: xs:
let res = builtins.partition pred xs;
in {
ok = res.right;
err = res.wrong;
};
# The result of an assert,
# either its true (yep) or false (nope).
# If its nope, we return the left and right
# side of the assert, together with the description.
AssertResult =
sum "AssertResult" {
yep = struct "yep" {
test = string;
};
nope-eq = struct "nope-eq" {
test = string;
left = any;
right = any;
};
nope-throw = struct "nope-throw" {
test = string;
expr = any;
};
};
# Result of an it. An it is a bunch of asserts
# bundled up with a good description of what is tested.
ItResult =
struct "ItResult" {
it-desc = string;
asserts = list AssertResult;
};
# assert that left and right values are equal
assertEq = defun [ string any any AssertResult ]
(desc: left: right:
if left == right
then { yep = { test = desc; }; }
else { nope-eq = {
test = desc;
inherit left right;
};
});
# assert that the expression throws when `deepSeq`-ed
assertThrows = defun [ string any AssertResult ]
(desc: expr:
if ! (builtins.tryEval (builtins.deepSeq expr {})).success
then { yep = { test = desc; }; }
else { nope-throw = {
test = desc;
inherit expr;
};
});
# Annotate a bunch of asserts with a descriptive name
it = desc: asserts: {
it-desc = desc;
inherit asserts;
};
# Run a bunch of its and check whether all asserts are yep.
# If not, abort evaluation with `throw`
# and print the result of the test suite.
#
# Takes a test suite name as first argument.
runTestsuite = defun [ string (list ItResult) drv ]
(name: itResults:
let
goodAss = ass: {
good = AssertResult.match ass {
yep = _: true;
nope-eq = _: false;
nope-throw = _: false;
};
x = ass;
};
goodIt = it: {
inherit (it) it-desc;
asserts = partitionTests (ass:
AssertResult.match ass {
yep = _: true;
nope-eq = _: false;
nope-throw = _: false;
}) it.asserts;
};
goodIts = partitionTests (it: (goodIt it).asserts.err == []);
res = goodIts itResults;
in
if res.err == []
then depot.nix.runExecline.local "testsuite-${name}-successful" {} [
"importas" "out" "out"
"if" [ bins.printf "%s\n" "testsuite ${name} successful!" ]
bins.touch "$out"
]
else depot.nix.runExecline.local "testsuite-${name}-failed" {} [
"importas" "out" "out"
"if" [
bins.printf "%s\n%s\n"
"testsuite ${name} failed!"
(lib.generators.toPretty {} res)
]
"exit" "1"
]);
in {
inherit
assertEq
assertThrows
it
runTestsuite
;
}