1# Writing Tests {#sec-writing-nixos-tests} 2 3A NixOS test is a module that has the following structure: 4 5```nix 6{ 7 8 # One or more machines: 9 nodes = 10 { machine = 11 { config, pkgs, ... }: { }; 12 machine2 = 13 { config, pkgs, ... }: { }; 14 15 }; 16 17 testScript = 18 '' 19 Python code… 20 ''; 21} 22``` 23 24We refer to the whole test above as a test module, whereas the values 25in [`nodes.<name>`](#test-opt-nodes) are NixOS modules themselves. 26 27The option [`testScript`](#test-opt-testScript) is a piece of Python code that executes the 28test (described below). During the test, it will start one or more 29virtual machines, the configuration of which is described by 30the option [`nodes`](#test-opt-nodes). 31 32An example of a single-node test is 33[`login.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix). 34It only needs a single machine to test whether users can log in 35on the virtual console, whether device ownership is correctly maintained 36when switching between consoles, and so on. An interesting multi-node test is 37[`nfs/simple.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs/simple.nix). 38It uses two client nodes to test correct locking across server crashes. 39 40## Calling a test {#sec-calling-nixos-tests} 41 42Tests are invoked differently depending on whether the test is part of NixOS or lives in a different project. 43 44### Testing within NixOS {#sec-call-nixos-test-in-nixos} 45 46Tests that are part of NixOS are added to [`nixos/tests/all-tests.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/all-tests.nix). 47 48```nix 49 hostname = runTest ./hostname.nix; 50``` 51 52Overrides can be added by defining an anonymous module in `all-tests.nix`. 53 54```nix 55 hostname = runTest { 56 imports = [ ./hostname.nix ]; 57 defaults.networking.firewall.enable = false; 58 }; 59``` 60 61You can run a test with attribute name `hostname` in `nixos/tests/all-tests.nix` by invoking: 62 63```shell 64cd /my/git/clone/of/nixpkgs 65nix-build -A nixosTests.hostname 66``` 67 68### Testing outside the NixOS project {#sec-call-nixos-test-outside-nixos} 69 70Outside the `nixpkgs` repository, you can instantiate the test by first importing the NixOS library, 71 72```nix 73let nixos-lib = import (nixpkgs + "/nixos/lib") { }; 74in 75 76nixos-lib.runTest { 77 imports = [ ./test.nix ]; 78 hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs 79 defaults.services.foo.package = mypkg; 80} 81``` 82 83`runTest` returns a derivation that runs the test. 84 85## Configuring the nodes {#sec-nixos-test-nodes} 86 87There are a few special NixOS options for test VMs: 88 89`virtualisation.memorySize` 90 91: The memory of the VM in megabytes. 92 93`virtualisation.vlans` 94 95: The virtual networks to which the VM is connected. See 96 [`nat.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix) 97 for an example. 98 99`virtualisation.writableStore` 100 101: By default, the Nix store in the VM is not writable. If you enable 102 this option, a writable union file system is mounted on top of the 103 Nix store to make it appear writable. This is necessary for tests 104 that run Nix operations that modify the store. 105 106For more options, see the module 107[`qemu-vm.nix`](https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix). 108 109The test script is a sequence of Python statements that perform various 110actions, such as starting VMs, executing commands in the VMs, and so on. 111Each virtual machine is represented as an object stored in the variable 112`name` if this is also the identifier of the machine in the declarative 113config. If you specified a node `nodes.machine`, the following example starts the 114machine, waits until it has finished booting, then executes a command 115and checks that the output is more-or-less correct: 116 117```py 118machine.start() 119machine.wait_for_unit("default.target") 120if not "Linux" in machine.succeed("uname"): 121 raise Exception("Wrong OS") 122``` 123 124The first line is technically unnecessary; machines are implicitly started 125when you first execute an action on them (such as `wait_for_unit` or 126`succeed`). If you have multiple machines, you can speed up the test by 127starting them in parallel: 128 129```py 130start_all() 131``` 132 133## Machine objects {#ssec-machine-objects} 134 135The following methods are available on machine objects: 136 137`start` 138 139: Start the virtual machine. This method is asynchronous --- it does 140 not wait for the machine to finish booting. 141 142`shutdown` 143 144: Shut down the machine, waiting for the VM to exit. 145 146`crash` 147 148: Simulate a sudden power failure, by telling the VM to exit 149 immediately. 150 151`block` 152 153: Simulate unplugging the Ethernet cable that connects the machine to 154 the other machines. 155 156`unblock` 157 158: Undo the effect of `block`. 159 160`screenshot` 161 162: Take a picture of the display of the virtual machine, in PNG format. 163 The screenshot is linked from the HTML log. 164 165`get_screen_text_variants` 166 167: Return a list of different interpretations of what is currently 168 visible on the machine\'s screen using optical character 169 recognition. The number and order of the interpretations is not 170 specified and is subject to change, but if no exception is raised at 171 least one will be returned. 172 173 ::: {.note} 174 This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`. 175 ::: 176 177`get_screen_text` 178 179: Return a textual representation of what is currently visible on the 180 machine\'s screen using optical character recognition. 181 182 ::: {.note} 183 This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`. 184 ::: 185 186`send_monitor_command` 187 188: Send a command to the QEMU monitor. This is rarely used, but allows 189 doing stuff such as attaching virtual USB disks to a running 190 machine. 191 192`send_key` 193 194: Simulate pressing keys on the virtual keyboard, e.g., 195 `send_key("ctrl-alt-delete")`. 196 197`send_chars` 198 199: Simulate typing a sequence of characters on the virtual keyboard, 200 e.g., `send_chars("foobar\n")` will type the string `foobar` 201 followed by the Enter key. 202 203`send_console` 204 205: Send keys to the kernel console. This allows interaction with the systemd 206 emergency mode, for example. Takes a string that is sent, e.g., 207 `send_console("\n\nsystemctl default\n")`. 208 209`execute` 210 211: Execute a shell command, returning a list `(status, stdout)`. 212 213 Commands are run with `set -euo pipefail` set: 214 215 - If several commands are separated by `;` and one fails, the 216 command as a whole will fail. 217 218 - For pipelines, the last non-zero exit status will be returned 219 (if there is one; otherwise zero will be returned). 220 221 - Dereferencing unset variables fails the command. 222 223 - It will wait for stdout to be closed. 224 225 If the command detaches, it must close stdout, as `execute` will wait 226 for this to consume all output reliably. This can be achieved by 227 redirecting stdout to stderr `>&2`, to `/dev/console`, `/dev/null` or 228 a file. Examples of detaching commands are `sleep 365d &`, where the 229 shell forks a new process that can write to stdout and `xclip -i`, where 230 the `xclip` command itself forks without closing stdout. 231 232 Takes an optional parameter `check_return` that defaults to `True`. 233 Setting this parameter to `False` will not check for the return code 234 and return -1 instead. This can be used for commands that shut down 235 the VM and would therefore break the pipe that would be used for 236 retrieving the return code. 237 238 A timeout for the command can be specified (in seconds) using the optional 239 `timeout` parameter, e.g., `execute(cmd, timeout=10)` or 240 `execute(cmd, timeout=None)`. The default is 900 seconds. 241 242`succeed` 243 244: Execute a shell command, raising an exception if the exit status is 245 not zero, otherwise returning the standard output. Similar to `execute`, 246 except that the timeout is `None` by default. See `execute` for details on 247 command execution. 248 249`fail` 250 251: Like `succeed`, but raising an exception if the command returns a zero 252 status. 253 254`wait_until_succeeds` 255 256: Repeat a shell command with 1-second intervals until it succeeds. 257 Has a default timeout of 900 seconds which can be modified, e.g. 258 `wait_until_succeeds(cmd, timeout=10)`. See `execute` for details on 259 command execution. 260 261`wait_until_fails` 262 263: Like `wait_until_succeeds`, but repeating the command until it fails. 264 265`wait_for_unit` 266 267: Wait until the specified systemd unit has reached the "active" 268 state. 269 270`wait_for_file` 271 272: Wait until the specified file exists. 273 274`wait_for_open_port` 275 276: Wait until a process is listening on the given TCP port (on 277 `localhost`, at least). 278 279`wait_for_closed_port` 280 281: Wait until nobody is listening on the given TCP port. 282 283`wait_for_x` 284 285: Wait until the X11 server is accepting connections. 286 287`wait_for_text` 288 289: Wait until the supplied regular expressions matches the textual 290 contents of the screen by using optical character recognition (see 291 `get_screen_text` and `get_screen_text_variants`). 292 293 ::: {.note} 294 This requires [`enableOCR`](#test-opt-enableOCR) to be set to `true`. 295 ::: 296 297`wait_for_console_text` 298 299: Wait until the supplied regular expressions match a line of the 300 serial console output. This method is useful when OCR is not 301 possibile or accurate enough. 302 303`wait_for_window` 304 305: Wait until an X11 window has appeared whose name matches the given 306 regular expression, e.g., `wait_for_window("Terminal")`. 307 308`copy_from_host` 309 310: Copies a file from host to machine, e.g., 311 `copy_from_host("myfile", "/etc/my/important/file")`. 312 313 The first argument is the file on the host. The file needs to be 314 accessible while building the nix derivation. The second argument is 315 the location of the file on the machine. 316 317`systemctl` 318 319: Runs `systemctl` commands with optional support for 320 `systemctl --user` 321 322 ```py 323 machine.systemctl("list-jobs --no-pager") # runs `systemctl list-jobs --no-pager` 324 machine.systemctl("list-jobs --no-pager", "any-user") # spawns a shell for `any-user` and runs `systemctl --user list-jobs --no-pager` 325 ``` 326 327`shell_interact` 328 329: Allows you to directly interact with the guest shell. This should 330 only be used during test development, not in production tests. 331 Killing the interactive session with `Ctrl-d` or `Ctrl-c` also ends 332 the guest session. 333 334`console_interact` 335 336: Allows you to directly interact with QEMU's stdin. This should 337 only be used during test development, not in production tests. 338 Output from QEMU is only read line-wise. `Ctrl-c` kills QEMU and 339 `Ctrl-d` closes console and returns to the test runner. 340 341To test user units declared by `systemd.user.services` the optional 342`user` argument can be used: 343 344```py 345machine.start() 346machine.wait_for_x() 347machine.wait_for_unit("xautolock.service", "x-session-user") 348``` 349 350This applies to `systemctl`, `get_unit_info`, `wait_for_unit`, 351`start_job` and `stop_job`. 352 353For faster dev cycles it\'s also possible to disable the code-linters 354(this shouldn\'t be commited though): 355 356```nix 357{ 358 skipLint = true; 359 nodes.machine = 360 { config, pkgs, ... }: 361 { configuration… 362 }; 363 364 testScript = 365 '' 366 Python code… 367 ''; 368} 369``` 370 371This will produce a Nix warning at evaluation time. To fully disable the 372linter, wrap the test script in comment directives to disable the Black 373linter directly (again, don\'t commit this within the Nixpkgs 374repository): 375 376```nix 377 testScript = 378 '' 379 # fmt: off 380 Python code… 381 # fmt: on 382 ''; 383``` 384 385Similarly, the type checking of test scripts can be disabled in the following 386way: 387 388```nix 389{ 390 skipTypeCheck = true; 391 nodes.machine = 392 { config, pkgs, ... }: 393 { configuration… 394 }; 395} 396``` 397 398## Failing tests early {#ssec-failing-tests-early} 399 400To fail tests early when certain invariants are no longer met (instead of waiting for the build to time out), the decorator `polling_condition` is provided. For example, if we are testing a program `foo` that should not quit after being started, we might write the following: 401 402```py 403@polling_condition 404def foo_running(): 405 machine.succeed("pgrep -x foo") 406 407 408machine.succeed("foo --start") 409machine.wait_until_succeeds("pgrep -x foo") 410 411with foo_running: 412 ... # Put `foo` through its paces 413``` 414 415`polling_condition` takes the following (optional) arguments: 416 417`seconds_interval` 418 419: 420 specifies how often the condition should be polled: 421 422```py 423@polling_condition(seconds_interval=10) 424def foo_running(): 425 machine.succeed("pgrep -x foo") 426``` 427 428`description` 429 430: 431 is used in the log when the condition is checked. If this is not provided, the description is pulled from the docstring of the function. These two are therefore equivalent: 432 433```py 434@polling_condition 435def foo_running(): 436 "check that foo is running" 437 machine.succeed("pgrep -x foo") 438``` 439 440```py 441@polling_condition(description="check that foo is running") 442def foo_running(): 443 machine.succeed("pgrep -x foo") 444``` 445 446## Adding Python packages to the test script {#ssec-python-packages-in-test-script} 447 448When additional Python libraries are required in the test script, they can be 449added using the parameter `extraPythonPackages`. For example, you could add 450`numpy` like this: 451 452```nix 453{ 454 extraPythonPackages = p: [ p.numpy ]; 455 456 nodes = { }; 457 458 # Type checking on extra packages doesn't work yet 459 skipTypeCheck = true; 460 461 testScript = '' 462 import numpy as np 463 assert str(np.zeros(4) == "array([0., 0., 0., 0.])") 464 ''; 465} 466``` 467 468In that case, `numpy` is chosen from the generic `python3Packages`. 469 470## Test Options Reference {#sec-test-options-reference} 471 472The following options can be used when writing tests. 473 474```{=docbook} 475<xi:include href="../../generated/test-options-db.xml" xpointer="test-options-list"/> 476```