about summary refs log tree commit diff
diff options
context:
space:
mode:
authorProfpatsch <mail@profpatsch.de>2020-06-27T20·56+0200
committerProfpatsch <mail@profpatsch.de>2020-06-28T03·52+0000
commitc8e888c1d2c6dfe60a835d1810ab57d87d097e93 (patch)
treecac6522756a5469845b53434ca260458cda80cc6
parente2c7e38934772744883494a01a784acc15ad2ff7 (diff)
feat(nix/runTestsuite): add runTestsuite r/1104
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 <mail@tazj.in>
Tested-by: tazjin <mail@tazj.in>
-rw-r--r--nix/runTestsuite/default.nix121
1 files changed, 121 insertions, 0 deletions
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
+    ;
+}