check (7428B)
1 #!/usr/bin/env python3 2 # 3 # Configure environment and run group of tests in it. 4 # 5 # Copyright (c) 2020-2021 Virtuozzo International GmbH 6 # 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 17 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 19 import os 20 import sys 21 import argparse 22 import shutil 23 from pathlib import Path 24 25 from findtests import TestFinder 26 from testenv import TestEnv 27 from testrunner import TestRunner 28 29 30 def make_argparser() -> argparse.ArgumentParser: 31 p = argparse.ArgumentParser(description="Test run options") 32 33 p.add_argument('-n', '--dry-run', action='store_true', 34 help='show me, do not run tests') 35 p.add_argument('-j', dest='jobs', type=int, default=1, 36 help='run tests in multiple parallel jobs') 37 38 p.add_argument('-d', dest='debug', action='store_true', help='debug') 39 p.add_argument('-p', dest='print', action='store_true', 40 help='redirects qemu\'s stdout and stderr to ' 41 'the test output') 42 p.add_argument('-gdb', action='store_true', 43 help="start gdbserver with $GDB_OPTIONS options " 44 "('localhost:12345' if $GDB_OPTIONS is empty)") 45 p.add_argument('-valgrind', action='store_true', 46 help='use valgrind, sets VALGRIND_QEMU environment ' 47 'variable') 48 49 p.add_argument('-misalign', action='store_true', 50 help='misalign memory allocations') 51 p.add_argument('--color', choices=['on', 'off', 'auto'], 52 default='auto', help="use terminal colors. The default " 53 "'auto' value means use colors if terminal stdout detected") 54 p.add_argument('-tap', action='store_true', 55 help='produce TAP output') 56 57 g_env = p.add_argument_group('test environment options') 58 mg = g_env.add_mutually_exclusive_group() 59 # We don't set default for cachemode, as we need to distinguish default 60 # from user input later. 61 mg.add_argument('-nocache', dest='cachemode', action='store_const', 62 const='none', help='set cache mode "none" (O_DIRECT), ' 63 'sets CACHEMODE environment variable') 64 mg.add_argument('-c', dest='cachemode', 65 help='sets CACHEMODE environment variable') 66 67 g_env.add_argument('-i', dest='aiomode', default='threads', 68 help='sets AIOMODE environment variable') 69 70 p.set_defaults(imgfmt='raw', imgproto='file') 71 72 format_list = ['raw', 'bochs', 'cloop', 'parallels', 'qcow', 'qcow2', 73 'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg'] 74 g_fmt = p.add_argument_group( 75 ' image format options', 76 'The following options set the IMGFMT environment variable. ' 77 'At most one choice is allowed, default is "raw"') 78 mg = g_fmt.add_mutually_exclusive_group() 79 for fmt in format_list: 80 mg.add_argument('-' + fmt, dest='imgfmt', action='store_const', 81 const=fmt, help=f'test {fmt}') 82 83 protocol_list = ['file', 'rbd', 'nbd', 'ssh', 'nfs', 'fuse'] 84 g_prt = p.add_argument_group( 85 ' image protocol options', 86 'The following options set the IMGPROTO environment variable. ' 87 'At most one choice is allowed, default is "file"') 88 mg = g_prt.add_mutually_exclusive_group() 89 for prt in protocol_list: 90 mg.add_argument('-' + prt, dest='imgproto', action='store_const', 91 const=prt, help=f'test {prt}') 92 93 g_bash = p.add_argument_group('bash tests options', 94 'The following options are ignored by ' 95 'python tests.') 96 # TODO: make support for the following options in iotests.py 97 g_bash.add_argument('-o', dest='imgopts', 98 help='options to pass to qemu-img create/convert, ' 99 'sets IMGOPTS environment variable') 100 101 g_sel = p.add_argument_group('test selecting options', 102 'The following options specify test set ' 103 'to run.') 104 g_sel.add_argument('-g', '--groups', metavar='group1,...', 105 help='include tests from these groups') 106 g_sel.add_argument('-x', '--exclude-groups', metavar='group1,...', 107 help='exclude tests from these groups') 108 g_sel.add_argument('--start-from', metavar='TEST', 109 help='Start from specified test: make sorted sequence ' 110 'of tests as usual and then drop tests from the first ' 111 'one to TEST (not inclusive). This may be used to ' 112 'rerun failed ./check command, starting from the ' 113 'middle of the process.') 114 g_sel.add_argument('tests', metavar='TEST_FILES', nargs='*', 115 help='tests to run, or "--" followed by a command') 116 117 return p 118 119 120 if __name__ == '__main__': 121 args = make_argparser().parse_args() 122 123 env = TestEnv(imgfmt=args.imgfmt, imgproto=args.imgproto, 124 aiomode=args.aiomode, cachemode=args.cachemode, 125 imgopts=args.imgopts, misalign=args.misalign, 126 debug=args.debug, valgrind=args.valgrind, 127 gdb=args.gdb, qprint=args.print) 128 129 if len(sys.argv) > 1 and sys.argv[-len(args.tests)-1] == '--': 130 if not args.tests: 131 sys.exit("missing command after '--'") 132 cmd = args.tests 133 env.print_env() 134 exec_pathstr = shutil.which(cmd[0]) 135 if exec_pathstr is None: 136 sys.exit('command not found: ' + cmd[0]) 137 exec_path = Path(exec_pathstr).resolve() 138 cmd[0] = str(exec_path) 139 full_env = env.prepare_subprocess(cmd) 140 os.chdir(exec_path.parent) 141 os.execve(cmd[0], cmd, full_env) 142 143 testfinder = TestFinder(test_dir=env.source_iotests) 144 145 groups = args.groups.split(',') if args.groups else None 146 x_groups = args.exclude_groups.split(',') if args.exclude_groups else None 147 148 group_local = os.path.join(env.source_iotests, 'group.local') 149 if os.path.isfile(group_local): 150 try: 151 testfinder.add_group_file(group_local) 152 except ValueError as e: 153 sys.exit(f"Failed to parse group file '{group_local}': {e}") 154 155 try: 156 tests = testfinder.find_tests(groups=groups, exclude_groups=x_groups, 157 tests=args.tests, 158 start_from=args.start_from) 159 if not tests: 160 raise ValueError('No tests selected') 161 except ValueError as e: 162 sys.exit(e) 163 164 if args.dry_run: 165 print('\n'.join(tests)) 166 else: 167 with TestRunner(env, tap=args.tap, 168 color=args.color) as tr: 169 paths = [os.path.join(env.source_iotests, t) for t in tests] 170 ok = tr.run_tests(paths, args.jobs) 171 if not ok: 172 sys.exit(1)