xref: /qemu/tests/functional/qemu_test/decorators.py (revision d82bb3f5dd5647e0f470b4189096aced1447b09f)
1# SPDX-License-Identifier: GPL-2.0-or-later
2#
3# Decorators useful in functional tests
4
5import importlib
6import os
7import platform
8import resource
9from unittest import skipIf, skipUnless
10
11from .cmd import which
12
13'''
14Decorator to skip execution of a test if the list
15of command binaries is not available in $PATH.
16Example:
17
18  @skipIfMissingCommands("mkisofs", "losetup")
19'''
20def skipIfMissingCommands(*args):
21    has_cmds = True
22    for cmd in args:
23         if not which(cmd):
24             has_cmds = False
25             break
26
27    return skipUnless(has_cmds, 'required command(s) "%s" not installed' %
28                                ", ".join(args))
29
30'''
31Decorator to skip execution of a test if the current
32host operating system does match one of the prohibited
33ones.
34Example
35
36  @skipIfOperatingSystem("Linux", "Darwin")
37'''
38def skipIfOperatingSystem(*args):
39    return skipIf(platform.system() in args,
40                  'running on an OS (%s) that is not able to run this test' %
41                  ", ".join(args))
42
43'''
44Decorator to skip execution of a test if the current
45host machine does not match one of the permitted
46machines.
47Example
48
49  @skipIfNotMachine("x86_64", "aarch64")
50'''
51def skipIfNotMachine(*args):
52    return skipUnless(platform.machine() in args,
53                      'not running on one of the required machine(s) "%s"' %
54                      ", ".join(args))
55
56'''
57Decorator to skip execution of flaky tests, unless
58the $QEMU_TEST_FLAKY_TESTS environment variable is set.
59A bug URL must be provided that documents the observed
60failure behaviour, so it can be tracked & re-evaluated
61in future.
62
63Historical tests may be providing "None" as the bug_url
64but this should not be done for new test.
65
66Example:
67
68  @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/NNN")
69'''
70def skipFlakyTest(bug_url):
71    if bug_url is None:
72        bug_url = "FIXME: reproduce flaky test and file bug report or remove"
73    return skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'),
74                      f'Test is unstable: {bug_url}')
75
76'''
77Decorator to skip execution of tests which are likely
78to execute untrusted commands on the host, or commands
79which process untrusted code, unless the
80$QEMU_TEST_ALLOW_UNTRUSTED_CODE env var is set.
81Example:
82
83  @skipUntrustedTest()
84'''
85def skipUntrustedTest():
86    return skipUnless(os.getenv('QEMU_TEST_ALLOW_UNTRUSTED_CODE'),
87                      'Test runs untrusted code / processes untrusted data')
88
89'''
90Decorator to skip execution of tests which need large
91data storage (over around 500MB-1GB mark) on the host,
92unless the $QEMU_TEST_ALLOW_LARGE_STORAGE environment
93variable is set
94
95Example:
96
97  @skipBigDataTest()
98'''
99def skipBigDataTest():
100    return skipUnless(os.getenv('QEMU_TEST_ALLOW_LARGE_STORAGE'),
101                      'Test requires large host storage space')
102
103'''
104Decorator to skip execution of tests which have a really long
105runtime (and might e.g. time out if QEMU has been compiled with
106debugging enabled) unless the $QEMU_TEST_ALLOW_SLOW
107environment variable is set
108
109Example:
110
111  @skipSlowTest()
112'''
113def skipSlowTest():
114    return skipUnless(os.getenv('QEMU_TEST_ALLOW_SLOW'),
115                      'Test has a very long runtime and might time out')
116
117'''
118Decorator to skip execution of a test if the list
119of python imports is not available.
120Example:
121
122  @skipIfMissingImports("numpy", "cv2")
123'''
124def skipIfMissingImports(*args):
125    has_imports = True
126    for impname in args:
127        try:
128            importlib.import_module(impname)
129        except ImportError:
130            has_imports = False
131            break
132
133    return skipUnless(has_imports, 'required import(s) "%s" not installed' %
134                                   ", ".join(args))
135
136'''
137Decorator to skip execution of a test if the system's
138locked memory limit is below the required threshold.
139Takes required locked memory threshold in kB.
140Example:
141
142  @skipLockedMemoryTest(2_097_152)
143'''
144def skipLockedMemoryTest(locked_memory):
145    # get memlock hard limit in bytes
146    _, ulimit_memory = resource.getrlimit(resource.RLIMIT_MEMLOCK)
147
148    return skipUnless(
149        ulimit_memory == resource.RLIM_INFINITY or ulimit_memory >= locked_memory * 1024,
150        f'Test required {locked_memory} kB of available locked memory',
151    )
152