summary refs log tree commit diff
path: root/nixos/doc/manual/from_md/development
diff options
context:
space:
mode:
authorJacek Galowicz <jacek@galowicz.de>2022-01-08 18:28:25 +0100
committerGitHub <noreply@github.com>2022-01-08 18:28:25 +0100
commit048fd95f106ef4dca58ff01159207a027ab612d9 (patch)
tree3231875fa322010481f57a5d0b10102072709903 /nixos/doc/manual/from_md/development
parent41216b003f541efc899956a97106937e02f4aad2 (diff)
parent793a2f50f13f0c630cffbbb214f4128254945701 (diff)
downloadnixpkgs-048fd95f106ef4dca58ff01159207a027ab612d9.tar
nixpkgs-048fd95f106ef4dca58ff01159207a027ab612d9.tar.gz
nixpkgs-048fd95f106ef4dca58ff01159207a027ab612d9.tar.bz2
nixpkgs-048fd95f106ef4dca58ff01159207a027ab612d9.tar.lz
nixpkgs-048fd95f106ef4dca58ff01159207a027ab612d9.tar.xz
nixpkgs-048fd95f106ef4dca58ff01159207a027ab612d9.tar.zst
nixpkgs-048fd95f106ef4dca58ff01159207a027ab612d9.zip
Merge pull request #146905 from Synthetica9/failure_mode
nixos/test-driver: add polling_condition
Diffstat (limited to 'nixos/doc/manual/from_md/development')
-rw-r--r--nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml846
1 files changed, 457 insertions, 389 deletions
diff --git a/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml b/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml
index 0d523681b63..45c9c40c609 100644
--- a/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml
+++ b/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml
@@ -117,407 +117,413 @@ if not &quot;Linux&quot; in machine.succeed(&quot;uname&quot;):
   <programlisting language="python">
 start_all()
 </programlisting>
-  <para>
-    The following methods are available on machine objects:
-  </para>
-  <variablelist>
-    <varlistentry>
-      <term>
-        <literal>start</literal>
-      </term>
-      <listitem>
-        <para>
-          Start the virtual machine. This method is asynchronous — it
-          does not wait for the machine to finish booting.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>shutdown</literal>
-      </term>
-      <listitem>
-        <para>
-          Shut down the machine, waiting for the VM to exit.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>crash</literal>
-      </term>
-      <listitem>
-        <para>
-          Simulate a sudden power failure, by telling the VM to exit
-          immediately.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>block</literal>
-      </term>
-      <listitem>
-        <para>
-          Simulate unplugging the Ethernet cable that connects the
-          machine to the other machines.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>unblock</literal>
-      </term>
-      <listitem>
-        <para>
-          Undo the effect of <literal>block</literal>.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>screenshot</literal>
-      </term>
-      <listitem>
-        <para>
-          Take a picture of the display of the virtual machine, in PNG
-          format. The screenshot is linked from the HTML log.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>get_screen_text_variants</literal>
-      </term>
-      <listitem>
-        <para>
-          Return a list of different interpretations of what is
-          currently visible on the machine's screen using optical
-          character recognition. The number and order of the
-          interpretations is not specified and is subject to change, but
-          if no exception is raised at least one will be returned.
-        </para>
-        <note>
+  <section xml:id="ssec-machine-objects">
+    <title>Machine objects</title>
+    <para>
+      The following methods are available on machine objects:
+    </para>
+    <variablelist>
+      <varlistentry>
+        <term>
+          <literal>start</literal>
+        </term>
+        <listitem>
           <para>
-            This requires passing <literal>enableOCR</literal> to the
-            test attribute set.
+            Start the virtual machine. This method is asynchronous — it
+            does not wait for the machine to finish booting.
           </para>
-        </note>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>get_screen_text</literal>
-      </term>
-      <listitem>
-        <para>
-          Return a textual representation of what is currently visible
-          on the machine's screen using optical character recognition.
-        </para>
-        <note>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>shutdown</literal>
+        </term>
+        <listitem>
           <para>
-            This requires passing <literal>enableOCR</literal> to the
-            test attribute set.
+            Shut down the machine, waiting for the VM to exit.
           </para>
-        </note>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>send_monitor_command</literal>
-      </term>
-      <listitem>
-        <para>
-          Send a command to the QEMU monitor. This is rarely used, but
-          allows doing stuff such as attaching virtual USB disks to a
-          running machine.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>send_key</literal>
-      </term>
-      <listitem>
-        <para>
-          Simulate pressing keys on the virtual keyboard, e.g.,
-          <literal>send_key(&quot;ctrl-alt-delete&quot;)</literal>.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>send_chars</literal>
-      </term>
-      <listitem>
-        <para>
-          Simulate typing a sequence of characters on the virtual
-          keyboard, e.g.,
-          <literal>send_chars(&quot;foobar\n&quot;)</literal> will type
-          the string <literal>foobar</literal> followed by the Enter
-          key.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>execute</literal>
-      </term>
-      <listitem>
-        <para>
-          Execute a shell command, returning a list
-          <literal>(status, stdout)</literal>. If the command detaches,
-          it must close stdout, as <literal>execute</literal> will wait
-          for this to consume all output reliably. This can be achieved
-          by redirecting stdout to stderr <literal>&gt;&amp;2</literal>,
-          to <literal>/dev/console</literal>,
-          <literal>/dev/null</literal> or a file. Examples of detaching
-          commands are <literal>sleep 365d &amp;</literal>, where the
-          shell forks a new process that can write to stdout and
-          <literal>xclip -i</literal>, where the
-          <literal>xclip</literal> command itself forks without closing
-          stdout. Takes an optional parameter
-          <literal>check_return</literal> that defaults to
-          <literal>True</literal>. Setting this parameter to
-          <literal>False</literal> will not check for the return code
-          and return -1 instead. This can be used for commands that shut
-          down the VM and would therefore break the pipe that would be
-          used for retrieving the return code.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>succeed</literal>
-      </term>
-      <listitem>
-        <para>
-          Execute a shell command, raising an exception if the exit
-          status is not zero, otherwise returning the standard output.
-          Commands are run with <literal>set -euo pipefail</literal>
-          set:
-        </para>
-        <itemizedlist>
-          <listitem>
-            <para>
-              If several commands are separated by <literal>;</literal>
-              and one fails, the command as a whole will fail.
-            </para>
-          </listitem>
-          <listitem>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>crash</literal>
+        </term>
+        <listitem>
+          <para>
+            Simulate a sudden power failure, by telling the VM to exit
+            immediately.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>block</literal>
+        </term>
+        <listitem>
+          <para>
+            Simulate unplugging the Ethernet cable that connects the
+            machine to the other machines.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>unblock</literal>
+        </term>
+        <listitem>
+          <para>
+            Undo the effect of <literal>block</literal>.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>screenshot</literal>
+        </term>
+        <listitem>
+          <para>
+            Take a picture of the display of the virtual machine, in PNG
+            format. The screenshot is linked from the HTML log.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>get_screen_text_variants</literal>
+        </term>
+        <listitem>
+          <para>
+            Return a list of different interpretations of what is
+            currently visible on the machine's screen using optical
+            character recognition. The number and order of the
+            interpretations is not specified and is subject to change,
+            but if no exception is raised at least one will be returned.
+          </para>
+          <note>
             <para>
-              For pipelines, the last non-zero exit status will be
-              returned (if there is one, zero will be returned
-              otherwise).
+              This requires passing <literal>enableOCR</literal> to the
+              test attribute set.
             </para>
-          </listitem>
-          <listitem>
+          </note>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>get_screen_text</literal>
+        </term>
+        <listitem>
+          <para>
+            Return a textual representation of what is currently visible
+            on the machine's screen using optical character recognition.
+          </para>
+          <note>
             <para>
-              Dereferencing unset variables fail the command.
+              This requires passing <literal>enableOCR</literal> to the
+              test attribute set.
             </para>
-          </listitem>
-          <listitem>
+          </note>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>send_monitor_command</literal>
+        </term>
+        <listitem>
+          <para>
+            Send a command to the QEMU monitor. This is rarely used, but
+            allows doing stuff such as attaching virtual USB disks to a
+            running machine.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>send_key</literal>
+        </term>
+        <listitem>
+          <para>
+            Simulate pressing keys on the virtual keyboard, e.g.,
+            <literal>send_key(&quot;ctrl-alt-delete&quot;)</literal>.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>send_chars</literal>
+        </term>
+        <listitem>
+          <para>
+            Simulate typing a sequence of characters on the virtual
+            keyboard, e.g.,
+            <literal>send_chars(&quot;foobar\n&quot;)</literal> will
+            type the string <literal>foobar</literal> followed by the
+            Enter key.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>execute</literal>
+        </term>
+        <listitem>
+          <para>
+            Execute a shell command, returning a list
+            <literal>(status, stdout)</literal>. If the command
+            detaches, it must close stdout, as
+            <literal>execute</literal> will wait for this to consume all
+            output reliably. This can be achieved by redirecting stdout
+            to stderr <literal>&gt;&amp;2</literal>, to
+            <literal>/dev/console</literal>,
+            <literal>/dev/null</literal> or a file. Examples of
+            detaching commands are <literal>sleep 365d &amp;</literal>,
+            where the shell forks a new process that can write to stdout
+            and <literal>xclip -i</literal>, where the
+            <literal>xclip</literal> command itself forks without
+            closing stdout. Takes an optional parameter
+            <literal>check_return</literal> that defaults to
+            <literal>True</literal>. Setting this parameter to
+            <literal>False</literal> will not check for the return code
+            and return -1 instead. This can be used for commands that
+            shut down the VM and would therefore break the pipe that
+            would be used for retrieving the return code.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>succeed</literal>
+        </term>
+        <listitem>
+          <para>
+            Execute a shell command, raising an exception if the exit
+            status is not zero, otherwise returning the standard output.
+            Commands are run with <literal>set -euo pipefail</literal>
+            set:
+          </para>
+          <itemizedlist>
+            <listitem>
+              <para>
+                If several commands are separated by
+                <literal>;</literal> and one fails, the command as a
+                whole will fail.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                For pipelines, the last non-zero exit status will be
+                returned (if there is one, zero will be returned
+                otherwise).
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                Dereferencing unset variables fail the command.
+              </para>
+            </listitem>
+            <listitem>
+              <para>
+                It will wait for stdout to be closed. See
+                <literal>execute</literal> for the implications.
+              </para>
+            </listitem>
+          </itemizedlist>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>fail</literal>
+        </term>
+        <listitem>
+          <para>
+            Like <literal>succeed</literal>, but raising an exception if
+            the command returns a zero status.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>wait_until_succeeds</literal>
+        </term>
+        <listitem>
+          <para>
+            Repeat a shell command with 1-second intervals until it
+            succeeds.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>wait_until_fails</literal>
+        </term>
+        <listitem>
+          <para>
+            Repeat a shell command with 1-second intervals until it
+            fails.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>wait_for_unit</literal>
+        </term>
+        <listitem>
+          <para>
+            Wait until the specified systemd unit has reached the
+            <quote>active</quote> state.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>wait_for_file</literal>
+        </term>
+        <listitem>
+          <para>
+            Wait until the specified file exists.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>wait_for_open_port</literal>
+        </term>
+        <listitem>
+          <para>
+            Wait until a process is listening on the given TCP port (on
+            <literal>localhost</literal>, at least).
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>wait_for_closed_port</literal>
+        </term>
+        <listitem>
+          <para>
+            Wait until nobody is listening on the given TCP port.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>wait_for_x</literal>
+        </term>
+        <listitem>
+          <para>
+            Wait until the X11 server is accepting connections.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>wait_for_text</literal>
+        </term>
+        <listitem>
+          <para>
+            Wait until the supplied regular expressions matches the
+            textual contents of the screen by using optical character
+            recognition (see <literal>get_screen_text</literal> and
+            <literal>get_screen_text_variants</literal>).
+          </para>
+          <note>
             <para>
-              It will wait for stdout to be closed. See
-              <literal>execute</literal> for the implications.
+              This requires passing <literal>enableOCR</literal> to the
+              test attribute set.
             </para>
-          </listitem>
-        </itemizedlist>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>fail</literal>
-      </term>
-      <listitem>
-        <para>
-          Like <literal>succeed</literal>, but raising an exception if
-          the command returns a zero status.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>wait_until_succeeds</literal>
-      </term>
-      <listitem>
-        <para>
-          Repeat a shell command with 1-second intervals until it
-          succeeds.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>wait_until_fails</literal>
-      </term>
-      <listitem>
-        <para>
-          Repeat a shell command with 1-second intervals until it fails.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>wait_for_unit</literal>
-      </term>
-      <listitem>
-        <para>
-          Wait until the specified systemd unit has reached the
-          <quote>active</quote> state.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>wait_for_file</literal>
-      </term>
-      <listitem>
-        <para>
-          Wait until the specified file exists.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>wait_for_open_port</literal>
-      </term>
-      <listitem>
-        <para>
-          Wait until a process is listening on the given TCP port (on
-          <literal>localhost</literal>, at least).
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>wait_for_closed_port</literal>
-      </term>
-      <listitem>
-        <para>
-          Wait until nobody is listening on the given TCP port.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>wait_for_x</literal>
-      </term>
-      <listitem>
-        <para>
-          Wait until the X11 server is accepting connections.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>wait_for_text</literal>
-      </term>
-      <listitem>
-        <para>
-          Wait until the supplied regular expressions matches the
-          textual contents of the screen by using optical character
-          recognition (see <literal>get_screen_text</literal> and
-          <literal>get_screen_text_variants</literal>).
-        </para>
-        <note>
+          </note>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>wait_for_console_text</literal>
+        </term>
+        <listitem>
           <para>
-            This requires passing <literal>enableOCR</literal> to the
-            test attribute set.
+            Wait until the supplied regular expressions match a line of
+            the serial console output. This method is useful when OCR is
+            not possibile or accurate enough.
           </para>
-        </note>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>wait_for_console_text</literal>
-      </term>
-      <listitem>
-        <para>
-          Wait until the supplied regular expressions match a line of
-          the serial console output. This method is useful when OCR is
-          not possibile or accurate enough.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>wait_for_window</literal>
-      </term>
-      <listitem>
-        <para>
-          Wait until an X11 window has appeared whose name matches the
-          given regular expression, e.g.,
-          <literal>wait_for_window(&quot;Terminal&quot;)</literal>.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>copy_from_host</literal>
-      </term>
-      <listitem>
-        <para>
-          Copies a file from host to machine, e.g.,
-          <literal>copy_from_host(&quot;myfile&quot;, &quot;/etc/my/important/file&quot;)</literal>.
-        </para>
-        <para>
-          The first argument is the file on the host. The file needs to
-          be accessible while building the nix derivation. The second
-          argument is the location of the file on the machine.
-        </para>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>systemctl</literal>
-      </term>
-      <listitem>
-        <para>
-          Runs <literal>systemctl</literal> commands with optional
-          support for <literal>systemctl --user</literal>
-        </para>
-        <programlisting language="python">
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>wait_for_window</literal>
+        </term>
+        <listitem>
+          <para>
+            Wait until an X11 window has appeared whose name matches the
+            given regular expression, e.g.,
+            <literal>wait_for_window(&quot;Terminal&quot;)</literal>.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>copy_from_host</literal>
+        </term>
+        <listitem>
+          <para>
+            Copies a file from host to machine, e.g.,
+            <literal>copy_from_host(&quot;myfile&quot;, &quot;/etc/my/important/file&quot;)</literal>.
+          </para>
+          <para>
+            The first argument is the file on the host. The file needs
+            to be accessible while building the nix derivation. The
+            second argument is the location of the file on the machine.
+          </para>
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>systemctl</literal>
+        </term>
+        <listitem>
+          <para>
+            Runs <literal>systemctl</literal> commands with optional
+            support for <literal>systemctl --user</literal>
+          </para>
+          <programlisting language="python">
 machine.systemctl(&quot;list-jobs --no-pager&quot;) # runs `systemctl list-jobs --no-pager`
 machine.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`
 </programlisting>
-      </listitem>
-    </varlistentry>
-    <varlistentry>
-      <term>
-        <literal>shell_interact</literal>
-      </term>
-      <listitem>
-        <para>
-          Allows you to directly interact with the guest shell. This
-          should only be used during test development, not in production
-          tests. Killing the interactive session with
-          <literal>Ctrl-d</literal> or <literal>Ctrl-c</literal> also
-          ends the guest session.
-        </para>
-      </listitem>
-    </varlistentry>
-  </variablelist>
-  <para>
-    To test user units declared by
-    <literal>systemd.user.services</literal> the optional
-    <literal>user</literal> argument can be used:
-  </para>
-  <programlisting language="python">
+        </listitem>
+      </varlistentry>
+      <varlistentry>
+        <term>
+          <literal>shell_interact</literal>
+        </term>
+        <listitem>
+          <para>
+            Allows you to directly interact with the guest shell. This
+            should only be used during test development, not in
+            production tests. Killing the interactive session with
+            <literal>Ctrl-d</literal> or <literal>Ctrl-c</literal> also
+            ends the guest session.
+          </para>
+        </listitem>
+      </varlistentry>
+    </variablelist>
+    <para>
+      To test user units declared by
+      <literal>systemd.user.services</literal> the optional
+      <literal>user</literal> argument can be used:
+    </para>
+    <programlisting language="python">
 machine.start()
 machine.wait_for_x()
 machine.wait_for_unit(&quot;xautolock.service&quot;, &quot;x-session-user&quot;)
 </programlisting>
-  <para>
-    This applies to <literal>systemctl</literal>,
-    <literal>get_unit_info</literal>, <literal>wait_for_unit</literal>,
-    <literal>start_job</literal> and <literal>stop_job</literal>.
-  </para>
-  <para>
-    For faster dev cycles it's also possible to disable the code-linters
-    (this shouldn't be commited though):
-  </para>
-  <programlisting language="bash">
+    <para>
+      This applies to <literal>systemctl</literal>,
+      <literal>get_unit_info</literal>,
+      <literal>wait_for_unit</literal>, <literal>start_job</literal> and
+      <literal>stop_job</literal>.
+    </para>
+    <para>
+      For faster dev cycles it's also possible to disable the
+      code-linters (this shouldn't be commited though):
+    </para>
+    <programlisting language="bash">
 import ./make-test-python.nix {
   skipLint = true;
   machine =
@@ -531,13 +537,13 @@ import ./make-test-python.nix {
     '';
 }
 </programlisting>
-  <para>
-    This will produce a Nix warning at evaluation time. To fully disable
-    the linter, wrap the test script in comment directives to disable
-    the Black linter directly (again, don't commit this within the
-    Nixpkgs repository):
-  </para>
-  <programlisting language="bash">
+    <para>
+      This will produce a Nix warning at evaluation time. To fully
+      disable the linter, wrap the test script in comment directives to
+      disable the Black linter directly (again, don't commit this within
+      the Nixpkgs repository):
+    </para>
+    <programlisting language="bash">
   testScript =
     ''
       # fmt: off
@@ -545,4 +551,66 @@ import ./make-test-python.nix {
       # fmt: on
     '';
 </programlisting>
+  </section>
+  <section xml:id="ssec-failing-tests-early">
+    <title>Failing tests early</title>
+    <para>
+      To fail tests early when certain invariables are no longer met
+      (instead of waiting for the build to time out), the decorator
+      <literal>polling_condition</literal> is provided. For example, if
+      we are testing a program <literal>foo</literal> that should not
+      quit after being started, we might write the following:
+    </para>
+    <programlisting language="python">
+@polling_condition
+def foo_running():
+    machine.succeed(&quot;pgrep -x foo&quot;)
+
+
+machine.succeed(&quot;foo --start&quot;)
+machine.wait_until_succeeds(&quot;pgrep -x foo&quot;)
+
+with foo_running:
+    ...  # Put `foo` through its paces
+</programlisting>
+    <para>
+      <literal>polling_condition</literal> takes the following
+      (optional) arguments:
+    </para>
+    <para>
+      <literal>seconds_interval</literal>
+    </para>
+    <para>
+      : specifies how often the condition should be polled:
+    </para>
+    <programlisting>
+```py
+@polling_condition(seconds_interval=10)
+def foo_running():
+    machine.succeed(&quot;pgrep -x foo&quot;)
+```
+</programlisting>
+    <para>
+      <literal>description</literal>
+    </para>
+    <para>
+      : 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:
+    </para>
+    <programlisting>
+```py
+@polling_condition
+def foo_running():
+    &quot;check that foo is running&quot;
+    machine.succeed(&quot;pgrep -x foo&quot;)
+```
+
+```py
+@polling_condition(description=&quot;check that foo is running&quot;)
+def foo_running():
+    machine.succeed(&quot;pgrep -x foo&quot;)
+```
+</programlisting>
+  </section>
 </section>