xref: /openbmc/qemu/docs/devel/testing/functional.rst (revision dbaaef7d)
1.. _checkfunctional-ref:
2
3Functional testing with Python
4==============================
5
6The ``tests/functional`` directory hosts functional tests written in
7Python. They are usually higher level tests, and may interact with
8external resources and with various guest operating systems.
9The functional tests have initially evolved from the Avocado tests, so there
10is a lot of similarity to those tests here (see :ref:`checkavocado-ref` for
11details about the Avocado tests).
12
13The tests should be written in the style of the Python `unittest`_ framework,
14using stdio for the TAP protocol. The folder ``tests/functional/qemu_test``
15provides classes (e.g. the ``QemuBaseTest``, ``QemuUserTest`` and the
16``QemuSystemTest`` classes) and utility functions that help to get your test
17into the right shape, e.g. by replacing the 'stdout' python object to redirect
18the normal output of your test to stderr instead.
19
20Note that if you don't use one of the QemuBaseTest based classes for your
21test, or if you spawn subprocesses from your test, you have to make sure
22that there is no TAP-incompatible output written to stdio, e.g. either by
23prefixing every line with a "# " to mark the output as a TAP comment, or
24e.g. by capturing the stdout output of subprocesses (redirecting it to
25stderr is OK).
26
27Tests based on ``qemu_test.QemuSystemTest`` can easily:
28
29 * Customize the command line arguments given to the convenience
30   ``self.vm`` attribute (a QEMUMachine instance)
31
32 * Interact with the QEMU monitor, send QMP commands and check
33   their results
34
35 * Interact with the guest OS, using the convenience console device
36   (which may be useful to assert the effectiveness and correctness of
37   command line arguments or QMP commands)
38
39 * Download (and cache) remote data files, such as firmware and kernel
40   images
41
42Running tests
43-------------
44
45You can run the functional tests simply by executing:
46
47.. code::
48
49  make check-functional
50
51It is also possible to run tests for a certain target only, for example
52the following line will only run the tests for the x86_64 target:
53
54.. code::
55
56  make check-functional-x86_64
57
58To run a single test file without the meson test runner, you can also
59execute the file directly by specifying two environment variables first,
60the PYTHONPATH that has to include the python folder and the tests/functional
61folder of the source tree, and QEMU_TEST_QEMU_BINARY that has to point
62to the QEMU binary that should be used for the test, for example::
63
64  $ export PYTHONPATH=../python:../tests/functional
65  $ export QEMU_TEST_QEMU_BINARY=$PWD/qemu-system-x86_64
66  $ python3 ../tests/functional/test_file.py
67
68The test framework will automatically purge any scratch files created during
69the tests. If needing to debug a failed test, it is possible to keep these
70files around on disk by setting ```QEMU_TEST_KEEP_SCRATCH=1``` as an env
71variable.  Any preserved files will be deleted the next time the test is run
72without this variable set.
73
74Overview
75--------
76
77The ``tests/functional/qemu_test`` directory provides the ``qemu_test``
78Python module, containing the ``qemu_test.QemuSystemTest`` class.
79Here is a simple usage example:
80
81.. code::
82
83  #!/usr/bin/env python3
84
85  from qemu_test import QemuSystemTest
86
87  class Version(QemuSystemTest):
88
89      def test_qmp_human_info_version(self):
90          self.vm.launch()
91          res = self.vm.cmd('human-monitor-command',
92                            command_line='info version')
93          self.assertRegex(res, r'^(\d+\.\d+\.\d)')
94
95  if __name__ == '__main__':
96      QemuSystemTest.main()
97
98By providing the "hash bang" line at the beginning of the script, marking
99the file as executable and by calling into QemuSystemTest.main(), the test
100can also be run stand-alone, without a test runner. OTOH when run via a test
101runner, the QemuSystemTest.main() function takes care of running the test
102functions in the right fassion (e.g. with TAP output that is required by the
103meson test runner).
104
105The ``qemu_test.QemuSystemTest`` base test class
106^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
107
108The ``qemu_test.QemuSystemTest`` class has a number of characteristics
109that are worth being mentioned.
110
111First of all, it attempts to give each test a ready to use QEMUMachine
112instance, available at ``self.vm``.  Because many tests will tweak the
113QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``)
114is left to the test writer.
115
116The base test class has also support for tests with more than one
117QEMUMachine. The way to get machines is through the ``self.get_vm()``
118method which will return a QEMUMachine instance. The ``self.get_vm()``
119method accepts arguments that will be passed to the QEMUMachine creation
120and also an optional ``name`` attribute so you can identify a specific
121machine and get it more than once through the tests methods. A simple
122and hypothetical example follows:
123
124.. code::
125
126  from qemu_test import QemuSystemTest
127
128  class MultipleMachines(QemuSystemTest):
129      def test_multiple_machines(self):
130          first_machine = self.get_vm()
131          second_machine = self.get_vm()
132          self.get_vm(name='third_machine').launch()
133
134          first_machine.launch()
135          second_machine.launch()
136
137          first_res = first_machine.cmd(
138              'human-monitor-command',
139              command_line='info version')
140
141          second_res = second_machine.cmd(
142              'human-monitor-command',
143              command_line='info version')
144
145          third_res = self.get_vm(name='third_machine').cmd(
146              'human-monitor-command',
147              command_line='info version')
148
149          self.assertEqual(first_res, second_res, third_res)
150
151At test "tear down", ``qemu_test.QemuSystemTest`` handles all the QEMUMachines
152shutdown.
153
154QEMUMachine
155-----------
156
157The QEMUMachine API is already widely used in the Python iotests,
158device-crash-test and other Python scripts.  It's a wrapper around the
159execution of a QEMU binary, giving its users:
160
161 * the ability to set command line arguments to be given to the QEMU
162   binary
163
164 * a ready to use QMP connection and interface, which can be used to
165   send commands and inspect its results, as well as asynchronous
166   events
167
168 * convenience methods to set commonly used command line arguments in
169   a more succinct and intuitive way
170
171QEMU binary selection
172^^^^^^^^^^^^^^^^^^^^^
173
174The QEMU binary used for the ``self.vm`` QEMUMachine instance will
175primarily depend on the value of the ``qemu_bin`` class attribute.
176If it is not explicitly set by the test code, its default value will
177be the result the QEMU_TEST_QEMU_BINARY environment variable.
178
179Attribute reference
180-------------------
181
182QemuBaseTest
183^^^^^^^^^^^^
184
185The following attributes are available on any ``qemu_test.QemuBaseTest``
186instance.
187
188arch
189""""
190
191The target architecture of the QEMU binary.
192
193Tests are also free to use this attribute value, for their own needs.
194A test may, for instance, use this value when selecting the architecture
195of a kernel or disk image to boot a VM with.
196
197qemu_bin
198""""""""
199
200The preserved value of the ``QEMU_TEST_QEMU_BINARY`` environment
201variable.
202
203QemuUserTest
204^^^^^^^^^^^^
205
206The QemuUserTest class can be used for running an executable via the
207usermode emulation binaries.
208
209QemuSystemTest
210^^^^^^^^^^^^^^
211
212The QemuSystemTest class can be used for running tests via one of the
213qemu-system-* binaries.
214
215vm
216""
217
218A QEMUMachine instance, initially configured according to the given
219``qemu_bin`` parameter.
220
221cpu
222"""
223
224The cpu model that will be set to all QEMUMachine instances created
225by the test.
226
227machine
228"""""""
229
230The machine type that will be set to all QEMUMachine instances created
231by the test. By using the set_machine() function of the QemuSystemTest
232class to set this attribute, you can automatically check whether the
233machine is available to skip the test in case it is not built into the
234QEMU binary.
235
236Asset handling
237--------------
238
239Many functional tests download assets (e.g. Linux kernels, initrds,
240firmware images, etc.) from the internet to be able to run tests with
241them. This imposes additional challenges to the test framework.
242
243First there is the the problem that some people might not have an
244unconstrained internet connection, so such tests should not be run by
245default when running ``make check``. To accomplish this situation,
246the tests that download files should only be added to the "thorough"
247speed mode in the meson.build file, while the "quick" speed mode is
248fine for functional tests that can be run without downloading files.
249``make check`` then only runs the quick functional tests along with
250the other quick tests from the other test suites. If you choose to
251run only run ``make check-functional``, the "thorough" tests will be
252executed, too. And to run all functional tests along with the others,
253you can use something like::
254
255  make -j$(nproc) check SPEED=thorough
256
257The second problem with downloading files from the internet are time
258constraints. The time for downloading files should not be taken into
259account when the test is running and the timeout of the test is ticking
260(since downloading can be very slow, depending on the network bandwidth).
261This problem is solved by downloading the assets ahead of time, before
262the tests are run. This pre-caching is done with the qemu_test.Asset
263class. To use it in your test, declare an asset in your test class with
264its URL and SHA256 checksum like this::
265
266    ASSET_somename = (
267        ('https://www.qemu.org/assets/images/qemu_head_200.png'),
268        '34b74cad46ea28a2966c1d04e102510daf1fd73e6582b6b74523940d5da029dd')
269
270In your test function, you can then get the file name of the cached
271asset like this::
272
273    def test_function(self):
274        file_path = self.ASSET_somename.fetch()
275
276The pre-caching will be done automatically when running
277``make check-functional`` (but not when running e.g.
278``make check-functional-<target>``). In case you just want to download
279the assets without running the tests, you can do so by running::
280
281    make precache-functional
282
283The cache is populated in the ``~/.cache/qemu/download`` directory by
284default, but the location can be changed by setting the
285``QEMU_TEST_CACHE_DIR`` environment variable.
286
287Skipping tests
288--------------
289
290Since the test framework is based on the common Python unittest framework,
291you can use the usual Python decorators which allow for easily skipping
292tests running under certain conditions, for example, on the lack of a binary
293on the test system or when the running environment is a CI system. For further
294information about those decorators, please refer to:
295
296  https://docs.python.org/3/library/unittest.html#skipping-tests-and-expected-failures
297
298While the conditions for skipping tests are often specifics of each one, there
299are recurring scenarios identified by the QEMU developers and the use of
300environment variables became a kind of standard way to enable/disable tests.
301
302Here is a list of the most used variables:
303
304QEMU_TEST_ALLOW_LARGE_STORAGE
305^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
306Tests which are going to fetch or produce assets considered *large* are not
307going to run unless that ``QEMU_TEST_ALLOW_LARGE_STORAGE=1`` is exported on
308the environment.
309
310The definition of *large* is a bit arbitrary here, but it usually means an
311asset which occupies at least 1GB of size on disk when uncompressed.
312
313QEMU_TEST_ALLOW_UNTRUSTED_CODE
314^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
315There are tests which will boot a kernel image or firmware that can be
316considered not safe to run on the developer's workstation, thus they are
317skipped by default. The definition of *not safe* is also arbitrary but
318usually it means a blob which either its source or build process aren't
319public available.
320
321You should export ``QEMU_TEST_ALLOW_UNTRUSTED_CODE=1`` on the environment in
322order to allow tests which make use of those kind of assets.
323
324QEMU_TEST_FLAKY_TESTS
325^^^^^^^^^^^^^^^^^^^^^
326Some tests are not working reliably and thus are disabled by default.
327This includes tests that don't run reliably on GitLab's CI which
328usually expose real issues that are rarely seen on developer machines
329due to the constraints of the CI environment. If you encounter a
330similar situation then raise a bug and then mark the test as shown on
331the code snippet below:
332
333.. code::
334
335  # See https://gitlab.com/qemu-project/qemu/-/issues/nnnn
336  @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab')
337  def test(self):
338      do_something()
339
340Tests should not live in this state forever and should either be fixed
341or eventually removed.
342
343
344.. _unittest: https://docs.python.org/3/library/unittest.html
345