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>
		
			
				
	
	
		
			121 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			121 lines
		
	
	
	
		
			3 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
{ 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
 | 
						||
    ;
 | 
						||
}
 |