From c8e888c1d2c6dfe60a835d1810ab57d87d097e93 Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Sat, 27 Jun 2020 22:56:58 +0200 Subject: feat(nix/runTestsuite): add runTestsuite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a very simple test suite for nix expressions. It should help us set up a good suite of unit tests for our nix-based stuff. Since we allow import from derivation, these tests can also depend on derivations and e.g. use `builtins.readFile` to check outputs. This is a first PoC to get us going, we can always replace it by something different in the future if we don’t like it. Change-Id: I206c7b624db2b1dabd9c73ffce4f87e658919958 Reviewed-on: https://cl.tvl.fyi/c/depot/+/662 Reviewed-by: tazjin Tested-by: tazjin --- nix/runTestsuite/default.nix | 121 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 nix/runTestsuite/default.nix diff --git a/nix/runTestsuite/default.nix b/nix/runTestsuite/default.nix new file mode 100644 index 0000000000..0105eb6fc9 --- /dev/null +++ b/nix/runTestsuite/default.nix @@ -0,0 +1,121 @@ +{ 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 unit defun list; + + # 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 it’s true (yep) or false (nope). + # If it’s nope, we return the left and right + # side of the assert, together with the description. + AssertResult = + sum "AssertResult" { + yep = struct "yep" { + test = string; + }; + nope = struct "nope" { + test = string; + left = any; + right = 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 = { + test = desc; + inherit left right; + }; + }); + + # 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) unit ] + (name: itResults: + let + goodAss = ass: { + good = AssertResult.match ass { + yep = _: true; + nope = _: false; + }; + x = ass; + }; + goodIt = it: { + inherit (it) it-desc; + asserts = partitionTests (ass: + AssertResult.match ass { + yep = _: true; + nope = _: false; + }) it.asserts; + }; + goodIts = partitionTests (it: (goodIt it).asserts.err == []); + res = goodIts itResults; + in + if res.err == [] + then {} + # TODO(Profpatsch): pretty printing of results + # and probably also somewhat easier to read output + else throw + ( "testsuite ${name} failed!\n" + + lib.generators.toPretty {} res)); + +in { + inherit + assertEq + it + runTestsuite + ; +} -- cgit 1.4.1