summaryrefslogtreecommitdiffstats
path: root/nixos/lib/testing/testScript.nix
blob: 591330a40fd81533b4c0d340066965256c787e44 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
testModuleArgs@{
  config,
  lib,
  hostPkgs,
  nodes,
  moduleType,
  ...
}:
let
  inherit (lib) mkOption types const;
  inherit (types) coercedTo lines functionTo;
in
{
  options = {
    testScript = mkOption {
      type = coercedTo lines const (functionTo lines);
      # Only pass args the testScript function expects.
      apply = v: args: v (builtins.intersectAttrs (lib.functionArgs v) args);
      description = ''
        A series of python declarations and statements that you write to perform
        the test.
      '';
    };
    testScriptString = mkOption {
      type = lines;
      readOnly = true;
      internal = true;
    };

    includeTestScriptReferences = mkOption {
      type = types.bool;
      default = true;
      internal = true;
    };
    withoutTestScriptReferences = mkOption {
      type = moduleType;
      description = ''
        A parallel universe where the testScript is invalid and has no references.
      '';
      internal = true;
      visible = false;
    };
  };
  config = {
    withoutTestScriptReferences.includeTestScriptReferences = false;
    withoutTestScriptReferences.testScript = lib.mkForce "testscript omitted";

    testScriptString = config.testScript {
      nodes = lib.mapAttrs (
        k: v:
        if v.virtualisation.useNixStoreImage then
          # prevent infinite recursion when testScript would
          # reference v's toplevel
          config.withoutTestScriptReferences.nodesCompat.${k}
        else
          # reuse memoized config
          v
      ) config.nodesCompat;
      containers = config.containers;
    };

    nodeDefaults =
      { config, name, ... }:
      {
        # Make sure all derivations referenced by the test
        # script are available on the nodes. When the store is
        # accessed through 9p, this isn't important, since
        # everything in the store is available to the guest,
        # but when building a root image it is, as all paths
        # that should be available to the guest has to be
        # copied to the image.
        virtualisation.additionalPaths =
          lib.optional
            # A testScript may evaluate nodes, which has caused
            # infinite recursions. The demand cycle involves:
            #   testScript -->
            #   nodes -->
            #   toplevel -->
            #   additionalPaths -->
            #   hasContext testScript' -->
            #   testScript (ad infinitum)
            # If we don't need to build an image, we can break this
            # cycle by short-circuiting when useNixStoreImage is false.
            (
              config.virtualisation.useNixStoreImage
              && builtins.hasContext testModuleArgs.config.testScriptString
              && testModuleArgs.config.includeTestScriptReferences
            )
            (hostPkgs.writeStringReferencesToFile testModuleArgs.config.testScriptString);
      };
  };
}