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