Merge tag 'pull-request-2024-10-21' of https://gitlab.com/thuth/qemu into staging
[qemu/kevin.git] / tests / qemu-iotests / check
blob545f9ec7bdd8c471b57cd3aab3a0380bb7abaf18
1 #!/usr/bin/env python3
3 # Configure environment and run group of tests in it.
5 # Copyright (c) 2020-2021 Virtuozzo International GmbH
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License as
9 # published by the Free Software Foundation.
11 # This program is distributed in the hope that it would be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 import os
20 import sys
21 import argparse
22 import shutil
23 from pathlib import Path
25 from findtests import TestFinder
26 from testenv import TestEnv
27 from testrunner import TestRunner
29 def get_default_path(follow_link=False):
30     """
31     Try to automagically figure out the path we are running from.
32     """
33     # called from the build tree?
34     if os.path.islink(sys.argv[0]):
35         if follow_link:
36             return os.path.dirname(os.readlink(sys.argv[0]))
37         else:
38             return os.path.dirname(os.path.abspath(sys.argv[0]))
39     else:  # or source tree?
40         return os.getcwd()
42 def make_argparser() -> argparse.ArgumentParser:
43     p = argparse.ArgumentParser(
44         description="Test run options",
45         formatter_class=argparse.ArgumentDefaultsHelpFormatter)
47     p.add_argument('-n', '--dry-run', action='store_true',
48                    help='show me, do not run tests')
49     p.add_argument('-j', dest='jobs', type=int, default=1,
50                    help='run tests in multiple parallel jobs')
52     p.add_argument('-d', dest='debug', action='store_true', help='debug')
53     p.add_argument('-p', dest='print', action='store_true',
54                    help='redirects qemu\'s stdout and stderr to '
55                         'the test output')
56     p.add_argument('-gdb', action='store_true',
57                    help="start gdbserver with $GDB_OPTIONS options "
58                         "('localhost:12345' if $GDB_OPTIONS is empty)")
59     p.add_argument('-valgrind', action='store_true',
60                    help='use valgrind, sets VALGRIND_QEMU environment '
61                         'variable')
63     p.add_argument('-misalign', action='store_true',
64                    help='misalign memory allocations')
65     p.add_argument('--color', choices=['on', 'off', 'auto'],
66                    default='auto', help="use terminal colors. The default "
67                    "'auto' value means use colors if terminal stdout detected")
68     p.add_argument('-tap', action='store_true',
69                    help='produce TAP output')
71     g_env = p.add_argument_group('test environment options')
72     mg = g_env.add_mutually_exclusive_group()
73     # We don't set default for cachemode, as we need to distinguish default
74     # from user input later.
75     mg.add_argument('-nocache', dest='cachemode', action='store_const',
76                     const='none', help='set cache mode "none" (O_DIRECT), '
77                     'sets CACHEMODE environment variable')
78     mg.add_argument('-c', dest='cachemode',
79                     help='sets CACHEMODE environment variable')
81     g_env.add_argument('-i', dest='aiomode', default='threads',
82                        help='sets AIOMODE environment variable')
84     p.set_defaults(imgfmt='raw', imgproto='file')
86     format_list = ['raw', 'bochs', 'cloop', 'parallels', 'qcow', 'qcow2',
87                    'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg', 'vvfat']
88     g_fmt = p.add_argument_group(
89         '  image format options',
90         'The following options set the IMGFMT environment variable. '
91         'At most one choice is allowed, default is "raw"')
92     mg = g_fmt.add_mutually_exclusive_group()
93     for fmt in format_list:
94         mg.add_argument('-' + fmt, dest='imgfmt', action='store_const',
95                         const=fmt, help=f'test {fmt}')
97     protocol_list = ['file', 'rbd', 'nbd', 'ssh', 'nfs', 'fuse']
98     g_prt = p.add_argument_group(
99         '  image protocol options',
100         'The following options set the IMGPROTO environment variable. '
101         'At most one choice is allowed, default is "file"')
102     mg = g_prt.add_mutually_exclusive_group()
103     for prt in protocol_list:
104         mg.add_argument('-' + prt, dest='imgproto', action='store_const',
105                         const=prt, help=f'test {prt}')
107     g_bash = p.add_argument_group('bash tests options',
108                                   'The following options are ignored by '
109                                   'python tests.')
110     # TODO: make support for the following options in iotests.py
111     g_bash.add_argument('-o', dest='imgopts',
112                         help='options to pass to qemu-img create/convert, '
113                         'sets IMGOPTS environment variable')
115     g_sel = p.add_argument_group('test selecting options',
116                                  'The following options specify test set '
117                                  'to run.')
118     g_sel.add_argument('-g', '--groups', metavar='group1,...',
119                        help='include tests from these groups')
120     g_sel.add_argument('-x', '--exclude-groups', metavar='group1,...',
121                        help='exclude tests from these groups')
122     g_sel.add_argument('--start-from', metavar='TEST',
123                        help='Start from specified test: make sorted sequence '
124                        'of tests as usual and then drop tests from the first '
125                        'one to TEST (not inclusive). This may be used to '
126                        'rerun failed ./check command, starting from the '
127                        'middle of the process.')
128     g_sel.add_argument('tests', metavar='TEST_FILES', nargs='*',
129                        help='tests to run, or "--" followed by a command')
130     g_sel.add_argument('--build-dir', default=get_default_path(),
131                        help='Path to iotests build directory')
132     g_sel.add_argument('--source-dir',
133                        default=get_default_path(follow_link=True),
134                        help='Path to iotests build directory')
136     return p
139 if __name__ == '__main__':
140     args = make_argparser().parse_args()
142     env = TestEnv(source_dir=args.source_dir,
143                   build_dir=args.build_dir,
144                   imgfmt=args.imgfmt, imgproto=args.imgproto,
145                   aiomode=args.aiomode, cachemode=args.cachemode,
146                   imgopts=args.imgopts, misalign=args.misalign,
147                   debug=args.debug, valgrind=args.valgrind,
148                   gdb=args.gdb, qprint=args.print,
149                   dry_run=args.dry_run)
151     if len(sys.argv) > 1 and sys.argv[-len(args.tests)-1] == '--':
152         if not args.tests:
153             sys.exit("missing command after '--'")
154         cmd = args.tests
155         env.print_env()
156         exec_pathstr = shutil.which(cmd[0])
157         if exec_pathstr is None:
158             sys.exit('command not found: ' + cmd[0])
159         exec_path = Path(exec_pathstr).resolve()
160         cmd[0] = str(exec_path)
161         full_env = env.prepare_subprocess(cmd)
162         os.chdir(exec_path.parent)
163         os.execve(cmd[0], cmd, full_env)
165     testfinder = TestFinder(test_dir=env.source_iotests)
167     groups = args.groups.split(',') if args.groups else None
168     x_groups = args.exclude_groups.split(',') if args.exclude_groups else None
170     group_local = os.path.join(env.source_iotests, 'group.local')
171     if os.path.isfile(group_local):
172         try:
173             testfinder.add_group_file(group_local)
174         except ValueError as e:
175             sys.exit(f"Failed to parse group file '{group_local}': {e}")
177     try:
178         tests = testfinder.find_tests(groups=groups, exclude_groups=x_groups,
179                                       tests=args.tests,
180                                       start_from=args.start_from)
181         if not tests:
182             raise ValueError('No tests selected')
183     except ValueError as e:
184         sys.exit(str(e))
186     if args.dry_run:
187         with env:
188             print('\n'.join([os.path.basename(t) for t in tests]))
189     else:
190         with TestRunner(env, tap=args.tap,
191                         color=args.color) as tr:
192             paths = [os.path.join(env.source_iotests, t) for t in tests]
193             ok = tr.run_tests(paths, args.jobs)
194             if not ok:
195                 sys.exit(1)