blob: 56d88ca4236b97b03ed461ed85b14f3e4f962fab [file] [log] [blame]
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +03001#!/usr/bin/env python3
Christoph Hellwig6bf19c92009-06-22 18:29:05 +02002#
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +03003# Configure environment and run group of tests in it.
4#
5# Copyright (c) 2020-2021 Virtuozzo International GmbH
Christoph Hellwig6bf19c92009-06-22 18:29:05 +02006#
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.
10#
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.
15#
16# You should have received a copy of the GNU General Public License
Christoph Hellwige8c212d2009-07-16 19:26:54 +020017# along with this program. If not, see <http://www.gnu.org/licenses/>.
Christoph Hellwig6bf19c92009-06-22 18:29:05 +020018
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030019import os
20import sys
21import argparse
Paolo Bonzini480b75e2021-05-03 13:01:09 +020022import shutil
23from pathlib import Path
24
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030025from findtests import TestFinder
26from testenv import TestEnv
27from testrunner import TestRunner
Christoph Hellwig6bf19c92009-06-22 18:29:05 +020028
Daniel P. Berrangé0c8076b2023-03-15 17:43:17 +000029def 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()
Max Reitze8f86242014-05-24 23:24:55 +020041
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030042def make_argparser() -> argparse.ArgumentParser:
Daniel P. Berrangé0c8076b2023-03-15 17:43:17 +000043 p = argparse.ArgumentParser(
44 description="Test run options",
45 formatter_class=argparse.ArgumentDefaultsHelpFormatter)
Max Reitze8f86242014-05-24 23:24:55 +020046
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030047 p.add_argument('-n', '--dry-run', action='store_true',
48 help='show me, do not run tests')
Vladimir Sementsov-Ogievskiy722f87d2021-12-03 13:22:23 +010049 p.add_argument('-j', dest='jobs', type=int, default=1,
50 help='run tests in multiple parallel jobs')
Max Reitze8f86242014-05-24 23:24:55 +020051
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030052 p.add_argument('-d', dest='debug', action='store_true', help='debug')
Emanuele Giuseppe Espositoeb7a91d2021-08-09 11:01:13 +020053 p.add_argument('-p', dest='print', action='store_true',
Emanuele Giuseppe Esposito87e4d4a2021-10-08 02:28:20 -040054 help='redirects qemu\'s stdout and stderr to '
55 'the test output')
Emanuele Giuseppe Espositocfb9b0b2021-08-09 11:01:03 +020056 p.add_argument('-gdb', action='store_true',
Emanuele Giuseppe Esposito87e4d4a2021-10-08 02:28:20 -040057 help="start gdbserver with $GDB_OPTIONS options "
58 "('localhost:12345' if $GDB_OPTIONS is empty)")
Emanuele Giuseppe Espositoa9b4c6b2021-08-09 11:01:08 +020059 p.add_argument('-valgrind', action='store_true',
Emanuele Giuseppe Esposito87e4d4a2021-10-08 02:28:20 -040060 help='use valgrind, sets VALGRIND_QEMU environment '
61 'variable')
Emanuele Giuseppe Espositoa9b4c6b2021-08-09 11:01:08 +020062
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030063 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")
Paolo Bonzinid3168592022-01-07 13:18:11 +010068 p.add_argument('-tap', action='store_true',
69 help='produce TAP output')
Max Reitz7fed1a42014-05-24 23:24:57 +020070
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030071 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')
Christoph Hellwig6bf19c92009-06-22 18:29:05 +020080
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030081 g_env.add_argument('-i', dest='aiomode', default='threads',
82 help='sets AIOMODE environment variable')
Paolo Bonzini09d653e2017-09-12 16:44:59 +020083
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030084 p.set_defaults(imgfmt='raw', imgproto='file')
Paolo Bonzini09d653e2017-09-12 16:44:59 +020085
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030086 format_list = ['raw', 'bochs', 'cloop', 'parallels', 'qcow', 'qcow2',
87 'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg']
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}')
Alex Bennée70ff5b02019-05-03 15:39:04 +010096
Markus Armbruster09ec8512021-05-01 09:57:47 +020097 protocol_list = ['file', 'rbd', 'nbd', 'ssh', 'nfs', 'fuse']
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +030098 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}')
Alex Bennée70ff5b02019-05-03 15:39:04 +0100106
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300107 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')
Paolo Bonzini09d653e2017-09-12 16:44:59 +0200114
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300115 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='*',
Paolo Bonzini480b75e2021-05-03 13:01:09 +0200129 help='tests to run, or "--" followed by a command')
Daniel P. Berrangé0c8076b2023-03-15 17:43:17 +0000130 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')
Paolo Bonzini09d653e2017-09-12 16:44:59 +0200135
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300136 return p
Paolo Bonzini09d653e2017-09-12 16:44:59 +0200137
Paolo Bonzini09d653e2017-09-12 16:44:59 +0200138
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300139if __name__ == '__main__':
140 args = make_argparser().parse_args()
Paolo Bonzini09d653e2017-09-12 16:44:59 +0200141
Daniel P. Berrangé0c8076b2023-03-15 17:43:17 +0000142 env = TestEnv(source_dir=args.source_dir,
143 build_dir=args.build_dir,
144 imgfmt=args.imgfmt, imgproto=args.imgproto,
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300145 aiomode=args.aiomode, cachemode=args.cachemode,
146 imgopts=args.imgopts, misalign=args.misalign,
Emanuele Giuseppe Espositocfb9b0b2021-08-09 11:01:03 +0200147 debug=args.debug, valgrind=args.valgrind,
Daniel P. Berrangéa9e21782023-03-15 17:43:18 +0000148 gdb=args.gdb, qprint=args.print,
149 dry_run=args.dry_run)
Paolo Bonzini09d653e2017-09-12 16:44:59 +0200150
Paolo Bonzini480b75e2021-05-03 13:01:09 +0200151 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)
164
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300165 testfinder = TestFinder(test_dir=env.source_iotests)
Eric Blake88037142017-10-05 14:02:45 -0500166
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300167 groups = args.groups.split(',') if args.groups else None
168 x_groups = args.exclude_groups.split(',') if args.exclude_groups else None
Paolo Bonzini09d653e2017-09-12 16:44:59 +0200169
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300170 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}")
Paolo Bonzini09d653e2017-09-12 16:44:59 +0200176
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300177 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:
John Snow5bcf18b2022-12-02 19:52:33 -0500184 sys.exit(str(e))
Paolo Bonzini09d653e2017-09-12 16:44:59 +0200185
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300186 if args.dry_run:
Daniel P. Berrangéc645bac2024-02-05 15:40:19 +0000187 with env:
188 print('\n'.join([os.path.basename(t) for t in tests]))
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300189 else:
Paolo Bonzinid3168592022-01-07 13:18:11 +0100190 with TestRunner(env, tap=args.tap,
Vladimir Sementsov-Ogievskiyf2030802021-01-25 21:50:55 +0300191 color=args.color) as tr:
Vladimir Sementsov-Ogievskiy3ae50942021-02-01 11:50:41 +0300192 paths = [os.path.join(env.source_iotests, t) for t in tests]
Vladimir Sementsov-Ogievskiy722f87d2021-12-03 13:22:23 +0100193 ok = tr.run_tests(paths, args.jobs)
Vladimir Sementsov-Ogievskiy3ae50942021-02-01 11:50:41 +0300194 if not ok:
195 sys.exit(1)