1{ config, lib, hostPkgs, ... }:
2let
3 inherit (lib) mkOption types literalMD;
4
5 # Reifies and correctly wraps the python test driver for
6 # the respective qemu version and with or without ocr support
7 testDriver = hostPkgs.callPackage ../test-driver {
8 inherit (config) enableOCR extraPythonPackages;
9 qemu_pkg = config.qemu.package;
10 imagemagick_light = hostPkgs.imagemagick_light.override { inherit (hostPkgs) libtiff; };
11 tesseract4 = hostPkgs.tesseract4.override { enableLanguages = [ "eng" ]; };
12 };
13
14
15 vlans = map (m: (
16 m.virtualisation.vlans ++
17 (lib.mapAttrsToList (_: v: v.vlan) m.virtualisation.interfaces))) (lib.attrValues config.nodes);
18 vms = map (m: m.system.build.vm) (lib.attrValues config.nodes);
19
20 nodeHostNames =
21 let
22 nodesList = map (c: c.system.name) (lib.attrValues config.nodes);
23 in
24 nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
25
26 pythonizeName = name:
27 let
28 head = lib.substring 0 1 name;
29 tail = lib.substring 1 (-1) name;
30 in
31 (if builtins.match "[A-z_]" head == null then "_" else head) +
32 lib.stringAsChars (c: if builtins.match "[A-z0-9_]" c == null then "_" else c) tail;
33
34 uniqueVlans = lib.unique (builtins.concatLists vlans);
35 vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;
36 pythonizedNames = map pythonizeName nodeHostNames;
37 machineNames = map (name: "${name}: Machine;") pythonizedNames;
38
39 withChecks = lib.warnIf config.skipLint "Linting is disabled";
40
41 driver =
42 hostPkgs.runCommand "nixos-test-driver-${config.name}"
43 {
44 # inherit testName; TODO (roberth): need this?
45 nativeBuildInputs = [
46 hostPkgs.makeWrapper
47 ] ++ lib.optionals (!config.skipTypeCheck) [ hostPkgs.mypy ];
48 buildInputs = [ testDriver ];
49 testScript = config.testScriptString;
50 preferLocalBuild = true;
51 passthru = config.passthru;
52 meta = config.meta // {
53 mainProgram = "nixos-test-driver";
54 };
55 }
56 ''
57 mkdir -p $out/bin
58
59 vmStartScripts=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
60
61 ${lib.optionalString (!config.skipTypeCheck) ''
62 # prepend type hints so the test script can be type checked with mypy
63 cat "${../test-script-prepend.py}" >> testScriptWithTypes
64 echo "${builtins.toString machineNames}" >> testScriptWithTypes
65 echo "${builtins.toString vlanNames}" >> testScriptWithTypes
66 echo -n "$testScript" >> testScriptWithTypes
67
68 echo "Running type check (enable/disable: config.skipTypeCheck)"
69 echo "See https://nixos.org/manual/nixos/stable/#test-opt-skipTypeCheck"
70
71 mypy --no-implicit-optional \
72 --pretty \
73 --no-color-output \
74 testScriptWithTypes
75 ''}
76
77 echo -n "$testScript" >> $out/test-script
78
79 ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
80
81 ${testDriver}/bin/generate-driver-symbols
82 ${lib.optionalString (!config.skipLint) ''
83 echo "Linting test script (enable/disable: config.skipLint)"
84 echo "See https://nixos.org/manual/nixos/stable/#test-opt-skipLint"
85
86 PYFLAKES_BUILTINS="$(
87 echo -n ${lib.escapeShellArg (lib.concatStringsSep "," pythonizedNames)},
88 < ${lib.escapeShellArg "driver-symbols"}
89 )" ${hostPkgs.python3Packages.pyflakes}/bin/pyflakes $out/test-script
90 ''}
91
92 # set defaults through environment
93 # see: ./test-driver/test-driver.py argparse implementation
94 wrapProgram $out/bin/nixos-test-driver \
95 --set startScripts "''${vmStartScripts[*]}" \
96 --set testScript "$out/test-script" \
97 --set globalTimeout "${toString config.globalTimeout}" \
98 --set vlans '${toString vlans}' \
99 ${lib.escapeShellArgs (lib.concatMap (arg: ["--add-flags" arg]) config.extraDriverArgs)}
100 '';
101
102in
103{
104 options = {
105
106 driver = mkOption {
107 description = "Package containing a script that runs the test.";
108 type = types.package;
109 defaultText = literalMD "set by the test framework";
110 };
111
112 hostPkgs = mkOption {
113 description = "Nixpkgs attrset used outside the nodes.";
114 type = types.raw;
115 example = lib.literalExpression ''
116 import nixpkgs { inherit system config overlays; }
117 '';
118 };
119
120 qemu.package = mkOption {
121 description = "Which qemu package to use for the virtualisation of [{option}`nodes`](#test-opt-nodes).";
122 type = types.package;
123 default = hostPkgs.qemu_test;
124 defaultText = "hostPkgs.qemu_test";
125 };
126
127 globalTimeout = mkOption {
128 description = ''
129 A global timeout for the complete test, expressed in seconds.
130 Beyond that timeout, every resource will be killed and released and the test will fail.
131
132 By default, we use a 1 hour timeout.
133 '';
134 type = types.int;
135 default = 60 * 60;
136 example = 10 * 60;
137 };
138
139 enableOCR = mkOption {
140 description = ''
141 Whether to enable Optical Character Recognition functionality for
142 testing graphical programs. See [`Machine objects`](#ssec-machine-objects).
143 '';
144 type = types.bool;
145 default = false;
146 };
147
148 extraPythonPackages = mkOption {
149 description = ''
150 Python packages to add to the test driver.
151
152 The argument is a Python package set, similar to `pkgs.pythonPackages`.
153 '';
154 example = lib.literalExpression ''
155 p: [ p.numpy ]
156 '';
157 type = types.functionTo (types.listOf types.package);
158 default = ps: [ ];
159 };
160
161 extraDriverArgs = mkOption {
162 description = ''
163 Extra arguments to pass to the test driver.
164
165 They become part of [{option}`driver`](#test-opt-driver) via `wrapProgram`.
166 '';
167 type = types.listOf types.str;
168 default = [];
169 };
170
171 skipLint = mkOption {
172 type = types.bool;
173 default = false;
174 description = ''
175 Do not run the linters. This may speed up your iteration cycle, but it is not something you should commit.
176 '';
177 };
178
179 skipTypeCheck = mkOption {
180 type = types.bool;
181 default = false;
182 description = ''
183 Disable type checking. This must not be enabled for new NixOS tests.
184
185 This may speed up your iteration cycle, unless you're working on the [{option}`testScript`](#test-opt-testScript).
186 '';
187 };
188 };
189
190 config = {
191 _module.args = {
192 hostPkgs =
193 # Comment is in nixos/modules/misc/nixpkgs.nix
194 lib.mkOverride lib.modules.defaultOverridePriority
195 config.hostPkgs.__splicedPackages;
196 };
197
198 driver = withChecks driver;
199
200 # make available on the test runner
201 passthru.driver = config.driver;
202 };
203}