at 23.05-pre 26 kB view raw
1<section xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xi="http://www.w3.org/2001/XInclude" xml:id="sec-writing-nixos-tests"> 2 <title>Writing Tests</title> 3 <para> 4 A NixOS test is a module that has the following structure: 5 </para> 6 <programlisting language="bash"> 7{ 8 9 # One or more machines: 10 nodes = 11 { machine = 12 { config, pkgs, ... }: { … }; 13 machine2 = 14 { config, pkgs, ... }: { … }; 15 16 }; 17 18 testScript = 19 '' 20 Python code… 21 ''; 22} 23</programlisting> 24 <para> 25 We refer to the whole test above as a test module, whereas the 26 values in 27 <link linkend="test-opt-nodes"><literal>nodes.&lt;name&gt;</literal></link> 28 are NixOS modules themselves. 29 </para> 30 <para> 31 The option 32 <link linkend="test-opt-testScript"><literal>testScript</literal></link> 33 is a piece of Python code that executes the test (described below). 34 During the test, it will start one or more virtual machines, the 35 configuration of which is described by the option 36 <link linkend="test-opt-nodes"><literal>nodes</literal></link>. 37 </para> 38 <para> 39 An example of a single-node test is 40 <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/login.nix"><literal>login.nix</literal></link>. 41 It only needs a single machine to test whether users can log in on 42 the virtual console, whether device ownership is correctly 43 maintained when switching between consoles, and so on. An 44 interesting multi-node test is 45 <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nfs/simple.nix"><literal>nfs/simple.nix</literal></link>. 46 It uses two client nodes to test correct locking across server 47 crashes. 48 </para> 49 <section xml:id="sec-calling-nixos-tests"> 50 <title>Calling a test</title> 51 <para> 52 Tests are invoked differently depending on whether the test is 53 part of NixOS or lives in a different project. 54 </para> 55 <section xml:id="sec-call-nixos-test-in-nixos"> 56 <title>Testing within NixOS</title> 57 <para> 58 Tests that are part of NixOS are added to 59 <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/all-tests.nix"><literal>nixos/tests/all-tests.nix</literal></link>. 60 </para> 61 <programlisting language="bash"> 62 hostname = runTest ./hostname.nix; 63</programlisting> 64 <para> 65 Overrides can be added by defining an anonymous module in 66 <literal>all-tests.nix</literal>. 67 </para> 68 <programlisting language="bash"> 69 hostname = runTest { 70 imports = [ ./hostname.nix ]; 71 defaults.networking.firewall.enable = false; 72 }; 73</programlisting> 74 <para> 75 You can run a test with attribute name 76 <literal>hostname</literal> in 77 <literal>nixos/tests/all-tests.nix</literal> by invoking: 78 </para> 79 <programlisting> 80cd /my/git/clone/of/nixpkgs 81nix-build -A nixosTests.hostname 82</programlisting> 83 </section> 84 <section xml:id="sec-call-nixos-test-outside-nixos"> 85 <title>Testing outside the NixOS project</title> 86 <para> 87 Outside the <literal>nixpkgs</literal> repository, you can 88 instantiate the test by first importing the NixOS library, 89 </para> 90 <programlisting language="bash"> 91let nixos-lib = import (nixpkgs + &quot;/nixos/lib&quot;) { }; 92in 93 94nixos-lib.runTest { 95 imports = [ ./test.nix ]; 96 hostPkgs = pkgs; # the Nixpkgs package set used outside the VMs 97 defaults.services.foo.package = mypkg; 98} 99</programlisting> 100 <para> 101 <literal>runTest</literal> returns a derivation that runs the 102 test. 103 </para> 104 </section> 105 </section> 106 <section xml:id="sec-nixos-test-nodes"> 107 <title>Configuring the nodes</title> 108 <para> 109 There are a few special NixOS options for test VMs: 110 </para> 111 <variablelist> 112 <varlistentry> 113 <term> 114 <literal>virtualisation.memorySize</literal> 115 </term> 116 <listitem> 117 <para> 118 The memory of the VM in megabytes. 119 </para> 120 </listitem> 121 </varlistentry> 122 <varlistentry> 123 <term> 124 <literal>virtualisation.vlans</literal> 125 </term> 126 <listitem> 127 <para> 128 The virtual networks to which the VM is connected. See 129 <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/tests/nat.nix"><literal>nat.nix</literal></link> 130 for an example. 131 </para> 132 </listitem> 133 </varlistentry> 134 <varlistentry> 135 <term> 136 <literal>virtualisation.writableStore</literal> 137 </term> 138 <listitem> 139 <para> 140 By default, the Nix store in the VM is not writable. If you 141 enable this option, a writable union file system is mounted 142 on top of the Nix store to make it appear writable. This is 143 necessary for tests that run Nix operations that modify the 144 store. 145 </para> 146 </listitem> 147 </varlistentry> 148 </variablelist> 149 <para> 150 For more options, see the module 151 <link xlink:href="https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/virtualisation/qemu-vm.nix"><literal>qemu-vm.nix</literal></link>. 152 </para> 153 <para> 154 The test script is a sequence of Python statements that perform 155 various actions, such as starting VMs, executing commands in the 156 VMs, and so on. Each virtual machine is represented as an object 157 stored in the variable <literal>name</literal> if this is also the 158 identifier of the machine in the declarative config. If you 159 specified a node <literal>nodes.machine</literal>, the following 160 example starts the machine, waits until it has finished booting, 161 then executes a command and checks that the output is more-or-less 162 correct: 163 </para> 164 <programlisting language="python"> 165machine.start() 166machine.wait_for_unit(&quot;default.target&quot;) 167if not &quot;Linux&quot; in machine.succeed(&quot;uname&quot;): 168 raise Exception(&quot;Wrong OS&quot;) 169</programlisting> 170 <para> 171 The first line is technically unnecessary; machines are implicitly 172 started when you first execute an action on them (such as 173 <literal>wait_for_unit</literal> or <literal>succeed</literal>). 174 If you have multiple machines, you can speed up the test by 175 starting them in parallel: 176 </para> 177 <programlisting language="python"> 178start_all() 179</programlisting> 180 </section> 181 <section xml:id="ssec-machine-objects"> 182 <title>Machine objects</title> 183 <para> 184 The following methods are available on machine objects: 185 </para> 186 <variablelist> 187 <varlistentry> 188 <term> 189 <literal>start</literal> 190 </term> 191 <listitem> 192 <para> 193 Start the virtual machine. This method is asynchronous — it 194 does not wait for the machine to finish booting. 195 </para> 196 </listitem> 197 </varlistentry> 198 <varlistentry> 199 <term> 200 <literal>shutdown</literal> 201 </term> 202 <listitem> 203 <para> 204 Shut down the machine, waiting for the VM to exit. 205 </para> 206 </listitem> 207 </varlistentry> 208 <varlistentry> 209 <term> 210 <literal>crash</literal> 211 </term> 212 <listitem> 213 <para> 214 Simulate a sudden power failure, by telling the VM to exit 215 immediately. 216 </para> 217 </listitem> 218 </varlistentry> 219 <varlistentry> 220 <term> 221 <literal>block</literal> 222 </term> 223 <listitem> 224 <para> 225 Simulate unplugging the Ethernet cable that connects the 226 machine to the other machines. 227 </para> 228 </listitem> 229 </varlistentry> 230 <varlistentry> 231 <term> 232 <literal>unblock</literal> 233 </term> 234 <listitem> 235 <para> 236 Undo the effect of <literal>block</literal>. 237 </para> 238 </listitem> 239 </varlistentry> 240 <varlistentry> 241 <term> 242 <literal>screenshot</literal> 243 </term> 244 <listitem> 245 <para> 246 Take a picture of the display of the virtual machine, in PNG 247 format. The screenshot is linked from the HTML log. 248 </para> 249 </listitem> 250 </varlistentry> 251 <varlistentry> 252 <term> 253 <literal>get_screen_text_variants</literal> 254 </term> 255 <listitem> 256 <para> 257 Return a list of different interpretations of what is 258 currently visible on the machine's screen using optical 259 character recognition. The number and order of the 260 interpretations is not specified and is subject to change, 261 but if no exception is raised at least one will be returned. 262 </para> 263 <note> 264 <para> 265 This requires 266 <link linkend="test-opt-enableOCR"><literal>enableOCR</literal></link> 267 to be set to <literal>true</literal>. 268 </para> 269 </note> 270 </listitem> 271 </varlistentry> 272 <varlistentry> 273 <term> 274 <literal>get_screen_text</literal> 275 </term> 276 <listitem> 277 <para> 278 Return a textual representation of what is currently visible 279 on the machine's screen using optical character recognition. 280 </para> 281 <note> 282 <para> 283 This requires 284 <link linkend="test-opt-enableOCR"><literal>enableOCR</literal></link> 285 to be set to <literal>true</literal>. 286 </para> 287 </note> 288 </listitem> 289 </varlistentry> 290 <varlistentry> 291 <term> 292 <literal>send_monitor_command</literal> 293 </term> 294 <listitem> 295 <para> 296 Send a command to the QEMU monitor. This is rarely used, but 297 allows doing stuff such as attaching virtual USB disks to a 298 running machine. 299 </para> 300 </listitem> 301 </varlistentry> 302 <varlistentry> 303 <term> 304 <literal>send_key</literal> 305 </term> 306 <listitem> 307 <para> 308 Simulate pressing keys on the virtual keyboard, e.g., 309 <literal>send_key(&quot;ctrl-alt-delete&quot;)</literal>. 310 </para> 311 </listitem> 312 </varlistentry> 313 <varlistentry> 314 <term> 315 <literal>send_chars</literal> 316 </term> 317 <listitem> 318 <para> 319 Simulate typing a sequence of characters on the virtual 320 keyboard, e.g., 321 <literal>send_chars(&quot;foobar\n&quot;)</literal> will 322 type the string <literal>foobar</literal> followed by the 323 Enter key. 324 </para> 325 </listitem> 326 </varlistentry> 327 <varlistentry> 328 <term> 329 <literal>send_console</literal> 330 </term> 331 <listitem> 332 <para> 333 Send keys to the kernel console. This allows interaction 334 with the systemd emergency mode, for example. Takes a string 335 that is sent, e.g., 336 <literal>send_console(&quot;\n\nsystemctl default\n&quot;)</literal>. 337 </para> 338 </listitem> 339 </varlistentry> 340 <varlistentry> 341 <term> 342 <literal>execute</literal> 343 </term> 344 <listitem> 345 <para> 346 Execute a shell command, returning a list 347 <literal>(status, stdout)</literal>. 348 </para> 349 <para> 350 Commands are run with <literal>set -euo pipefail</literal> 351 set: 352 </para> 353 <itemizedlist> 354 <listitem> 355 <para> 356 If several commands are separated by 357 <literal>;</literal> and one fails, the command as a 358 whole will fail. 359 </para> 360 </listitem> 361 <listitem> 362 <para> 363 For pipelines, the last non-zero exit status will be 364 returned (if there is one; otherwise zero will be 365 returned). 366 </para> 367 </listitem> 368 <listitem> 369 <para> 370 Dereferencing unset variables fails the command. 371 </para> 372 </listitem> 373 <listitem> 374 <para> 375 It will wait for stdout to be closed. 376 </para> 377 </listitem> 378 </itemizedlist> 379 <para> 380 If the command detaches, it must close stdout, as 381 <literal>execute</literal> will wait for this to consume all 382 output reliably. This can be achieved by redirecting stdout 383 to stderr <literal>&gt;&amp;2</literal>, to 384 <literal>/dev/console</literal>, 385 <literal>/dev/null</literal> or a file. Examples of 386 detaching commands are <literal>sleep 365d &amp;</literal>, 387 where the shell forks a new process that can write to stdout 388 and <literal>xclip -i</literal>, where the 389 <literal>xclip</literal> command itself forks without 390 closing stdout. 391 </para> 392 <para> 393 Takes an optional parameter <literal>check_return</literal> 394 that defaults to <literal>True</literal>. Setting this 395 parameter to <literal>False</literal> will not check for the 396 return code and return -1 instead. This can be used for 397 commands that shut down the VM and would therefore break the 398 pipe that would be used for retrieving the return code. 399 </para> 400 <para> 401 A timeout for the command can be specified (in seconds) 402 using the optional <literal>timeout</literal> parameter, 403 e.g., <literal>execute(cmd, timeout=10)</literal> or 404 <literal>execute(cmd, timeout=None)</literal>. The default 405 is 900 seconds. 406 </para> 407 </listitem> 408 </varlistentry> 409 <varlistentry> 410 <term> 411 <literal>succeed</literal> 412 </term> 413 <listitem> 414 <para> 415 Execute a shell command, raising an exception if the exit 416 status is not zero, otherwise returning the standard output. 417 Similar to <literal>execute</literal>, except that the 418 timeout is <literal>None</literal> by default. See 419 <literal>execute</literal> for details on command execution. 420 </para> 421 </listitem> 422 </varlistentry> 423 <varlistentry> 424 <term> 425 <literal>fail</literal> 426 </term> 427 <listitem> 428 <para> 429 Like <literal>succeed</literal>, but raising an exception if 430 the command returns a zero status. 431 </para> 432 </listitem> 433 </varlistentry> 434 <varlistentry> 435 <term> 436 <literal>wait_until_succeeds</literal> 437 </term> 438 <listitem> 439 <para> 440 Repeat a shell command with 1-second intervals until it 441 succeeds. Has a default timeout of 900 seconds which can be 442 modified, e.g. 443 <literal>wait_until_succeeds(cmd, timeout=10)</literal>. See 444 <literal>execute</literal> for details on command execution. 445 </para> 446 </listitem> 447 </varlistentry> 448 <varlistentry> 449 <term> 450 <literal>wait_until_fails</literal> 451 </term> 452 <listitem> 453 <para> 454 Like <literal>wait_until_succeeds</literal>, but repeating 455 the command until it fails. 456 </para> 457 </listitem> 458 </varlistentry> 459 <varlistentry> 460 <term> 461 <literal>wait_for_unit</literal> 462 </term> 463 <listitem> 464 <para> 465 Wait until the specified systemd unit has reached the 466 <quote>active</quote> state. 467 </para> 468 </listitem> 469 </varlistentry> 470 <varlistentry> 471 <term> 472 <literal>wait_for_file</literal> 473 </term> 474 <listitem> 475 <para> 476 Wait until the specified file exists. 477 </para> 478 </listitem> 479 </varlistentry> 480 <varlistentry> 481 <term> 482 <literal>wait_for_open_port</literal> 483 </term> 484 <listitem> 485 <para> 486 Wait until a process is listening on the given TCP port (on 487 <literal>localhost</literal>, at least). 488 </para> 489 </listitem> 490 </varlistentry> 491 <varlistentry> 492 <term> 493 <literal>wait_for_closed_port</literal> 494 </term> 495 <listitem> 496 <para> 497 Wait until nobody is listening on the given TCP port. 498 </para> 499 </listitem> 500 </varlistentry> 501 <varlistentry> 502 <term> 503 <literal>wait_for_x</literal> 504 </term> 505 <listitem> 506 <para> 507 Wait until the X11 server is accepting connections. 508 </para> 509 </listitem> 510 </varlistentry> 511 <varlistentry> 512 <term> 513 <literal>wait_for_text</literal> 514 </term> 515 <listitem> 516 <para> 517 Wait until the supplied regular expressions matches the 518 textual contents of the screen by using optical character 519 recognition (see <literal>get_screen_text</literal> and 520 <literal>get_screen_text_variants</literal>). 521 </para> 522 <note> 523 <para> 524 This requires 525 <link linkend="test-opt-enableOCR"><literal>enableOCR</literal></link> 526 to be set to <literal>true</literal>. 527 </para> 528 </note> 529 </listitem> 530 </varlistentry> 531 <varlistentry> 532 <term> 533 <literal>wait_for_console_text</literal> 534 </term> 535 <listitem> 536 <para> 537 Wait until the supplied regular expressions match a line of 538 the serial console output. This method is useful when OCR is 539 not possibile or accurate enough. 540 </para> 541 </listitem> 542 </varlistentry> 543 <varlistentry> 544 <term> 545 <literal>wait_for_window</literal> 546 </term> 547 <listitem> 548 <para> 549 Wait until an X11 window has appeared whose name matches the 550 given regular expression, e.g., 551 <literal>wait_for_window(&quot;Terminal&quot;)</literal>. 552 </para> 553 </listitem> 554 </varlistentry> 555 <varlistentry> 556 <term> 557 <literal>copy_from_host</literal> 558 </term> 559 <listitem> 560 <para> 561 Copies a file from host to machine, e.g., 562 <literal>copy_from_host(&quot;myfile&quot;, &quot;/etc/my/important/file&quot;)</literal>. 563 </para> 564 <para> 565 The first argument is the file on the host. The file needs 566 to be accessible while building the nix derivation. The 567 second argument is the location of the file on the machine. 568 </para> 569 </listitem> 570 </varlistentry> 571 <varlistentry> 572 <term> 573 <literal>systemctl</literal> 574 </term> 575 <listitem> 576 <para> 577 Runs <literal>systemctl</literal> commands with optional 578 support for <literal>systemctl --user</literal> 579 </para> 580 <programlisting language="python"> 581machine.systemctl(&quot;list-jobs --no-pager&quot;) # runs `systemctl list-jobs --no-pager` 582machine.systemctl(&quot;list-jobs --no-pager&quot;, &quot;any-user&quot;) # spawns a shell for `any-user` and runs `systemctl --user list-jobs --no-pager` 583</programlisting> 584 </listitem> 585 </varlistentry> 586 <varlistentry> 587 <term> 588 <literal>shell_interact</literal> 589 </term> 590 <listitem> 591 <para> 592 Allows you to directly interact with the guest shell. This 593 should only be used during test development, not in 594 production tests. Killing the interactive session with 595 <literal>Ctrl-d</literal> or <literal>Ctrl-c</literal> also 596 ends the guest session. 597 </para> 598 </listitem> 599 </varlistentry> 600 <varlistentry> 601 <term> 602 <literal>console_interact</literal> 603 </term> 604 <listitem> 605 <para> 606 Allows you to directly interact with QEMU’s stdin. This 607 should only be used during test development, not in 608 production tests. Output from QEMU is only read line-wise. 609 <literal>Ctrl-c</literal> kills QEMU and 610 <literal>Ctrl-d</literal> closes console and returns to the 611 test runner. 612 </para> 613 </listitem> 614 </varlistentry> 615 </variablelist> 616 <para> 617 To test user units declared by 618 <literal>systemd.user.services</literal> the optional 619 <literal>user</literal> argument can be used: 620 </para> 621 <programlisting language="python"> 622machine.start() 623machine.wait_for_x() 624machine.wait_for_unit(&quot;xautolock.service&quot;, &quot;x-session-user&quot;) 625</programlisting> 626 <para> 627 This applies to <literal>systemctl</literal>, 628 <literal>get_unit_info</literal>, 629 <literal>wait_for_unit</literal>, <literal>start_job</literal> and 630 <literal>stop_job</literal>. 631 </para> 632 <para> 633 For faster dev cycles it's also possible to disable the 634 code-linters (this shouldn't be commited though): 635 </para> 636 <programlisting language="bash"> 637{ 638 skipLint = true; 639 nodes.machine = 640 { config, pkgs, ... }: 641 { configuration… 642 }; 643 644 testScript = 645 '' 646 Python code… 647 ''; 648} 649</programlisting> 650 <para> 651 This will produce a Nix warning at evaluation time. To fully 652 disable the linter, wrap the test script in comment directives to 653 disable the Black linter directly (again, don't commit this within 654 the Nixpkgs repository): 655 </para> 656 <programlisting language="bash"> 657 testScript = 658 '' 659 # fmt: off 660 Python code… 661 # fmt: on 662 ''; 663</programlisting> 664 <para> 665 Similarly, the type checking of test scripts can be disabled in 666 the following way: 667 </para> 668 <programlisting language="bash"> 669{ 670 skipTypeCheck = true; 671 nodes.machine = 672 { config, pkgs, ... }: 673 { configuration… 674 }; 675} 676</programlisting> 677 </section> 678 <section xml:id="ssec-failing-tests-early"> 679 <title>Failing tests early</title> 680 <para> 681 To fail tests early when certain invariants are no longer met 682 (instead of waiting for the build to time out), the decorator 683 <literal>polling_condition</literal> is provided. For example, if 684 we are testing a program <literal>foo</literal> that should not 685 quit after being started, we might write the following: 686 </para> 687 <programlisting language="python"> 688@polling_condition 689def foo_running(): 690 machine.succeed(&quot;pgrep -x foo&quot;) 691 692 693machine.succeed(&quot;foo --start&quot;) 694machine.wait_until_succeeds(&quot;pgrep -x foo&quot;) 695 696with foo_running: 697 ... # Put `foo` through its paces 698</programlisting> 699 <para> 700 <literal>polling_condition</literal> takes the following 701 (optional) arguments: 702 </para> 703 <para> 704 <literal>seconds_interval</literal> 705 </para> 706 <para> 707 : specifies how often the condition should be polled: 708 </para> 709 <programlisting language="python"> 710@polling_condition(seconds_interval=10) 711def foo_running(): 712 machine.succeed(&quot;pgrep -x foo&quot;) 713</programlisting> 714 <para> 715 <literal>description</literal> 716 </para> 717 <para> 718 : is used in the log when the condition is checked. If this is not 719 provided, the description is pulled from the docstring of the 720 function. These two are therefore equivalent: 721 </para> 722 <programlisting language="python"> 723@polling_condition 724def foo_running(): 725 &quot;check that foo is running&quot; 726 machine.succeed(&quot;pgrep -x foo&quot;) 727</programlisting> 728 <programlisting language="python"> 729@polling_condition(description=&quot;check that foo is running&quot;) 730def foo_running(): 731 machine.succeed(&quot;pgrep -x foo&quot;) 732</programlisting> 733 </section> 734 <section xml:id="ssec-python-packages-in-test-script"> 735 <title>Adding Python packages to the test script</title> 736 <para> 737 When additional Python libraries are required in the test script, 738 they can be added using the parameter 739 <literal>extraPythonPackages</literal>. For example, you could add 740 <literal>numpy</literal> like this: 741 </para> 742 <programlisting language="bash"> 743{ 744 extraPythonPackages = p: [ p.numpy ]; 745 746 nodes = { }; 747 748 # Type checking on extra packages doesn't work yet 749 skipTypeCheck = true; 750 751 testScript = '' 752 import numpy as np 753 assert str(np.zeros(4) == &quot;array([0., 0., 0., 0.])&quot;) 754 ''; 755} 756</programlisting> 757 <para> 758 In that case, <literal>numpy</literal> is chosen from the generic 759 <literal>python3Packages</literal>. 760 </para> 761 </section> 762 <section xml:id="sec-test-options-reference"> 763 <title>Test Options Reference</title> 764 <para> 765 The following options can be used when writing tests. 766 </para> 767 <xi:include href="../../generated/test-options-db.xml" xpointer="test-options-list"/> 768 </section> 769</section>