a9e21786da
The 'check' script can be invoked in "dry run" mode, in which case it merely does test discovery and prints out all their names. Despite only doing test discovery it still validates that the various QEMU binaries can be found. This makes it impossible todo test discovery prior to building QEMU. This is a desirable feature to support, because it will let meson discover tests. Fortunately the code in the TestEnv constructor is ordered in a way that makes this fairly trivial to achieve. We can just short circuit the constructor after the basic directory paths have been set. Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> Acked-by: Hanna Czenczek <hreitz@redhat.com> Tested-by: Thomas Huth <thuth@redhat.com> Message-Id: <20230303160727.3977246-3-berrange@redhat.com> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20230315174331.2959-20-alex.bennee@linaro.org>
195 lines
8.2 KiB
Python
Executable File
195 lines
8.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
#
|
|
# Configure environment and run group of tests in it.
|
|
#
|
|
# Copyright (c) 2020-2021 Virtuozzo International GmbH
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it would be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
import os
|
|
import sys
|
|
import argparse
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
from findtests import TestFinder
|
|
from testenv import TestEnv
|
|
from testrunner import TestRunner
|
|
|
|
def get_default_path(follow_link=False):
|
|
"""
|
|
Try to automagically figure out the path we are running from.
|
|
"""
|
|
# called from the build tree?
|
|
if os.path.islink(sys.argv[0]):
|
|
if follow_link:
|
|
return os.path.dirname(os.readlink(sys.argv[0]))
|
|
else:
|
|
return os.path.dirname(os.path.abspath(sys.argv[0]))
|
|
else: # or source tree?
|
|
return os.getcwd()
|
|
|
|
def make_argparser() -> argparse.ArgumentParser:
|
|
p = argparse.ArgumentParser(
|
|
description="Test run options",
|
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
|
|
p.add_argument('-n', '--dry-run', action='store_true',
|
|
help='show me, do not run tests')
|
|
p.add_argument('-j', dest='jobs', type=int, default=1,
|
|
help='run tests in multiple parallel jobs')
|
|
|
|
p.add_argument('-d', dest='debug', action='store_true', help='debug')
|
|
p.add_argument('-p', dest='print', action='store_true',
|
|
help='redirects qemu\'s stdout and stderr to '
|
|
'the test output')
|
|
p.add_argument('-gdb', action='store_true',
|
|
help="start gdbserver with $GDB_OPTIONS options "
|
|
"('localhost:12345' if $GDB_OPTIONS is empty)")
|
|
p.add_argument('-valgrind', action='store_true',
|
|
help='use valgrind, sets VALGRIND_QEMU environment '
|
|
'variable')
|
|
|
|
p.add_argument('-misalign', action='store_true',
|
|
help='misalign memory allocations')
|
|
p.add_argument('--color', choices=['on', 'off', 'auto'],
|
|
default='auto', help="use terminal colors. The default "
|
|
"'auto' value means use colors if terminal stdout detected")
|
|
p.add_argument('-tap', action='store_true',
|
|
help='produce TAP output')
|
|
|
|
g_env = p.add_argument_group('test environment options')
|
|
mg = g_env.add_mutually_exclusive_group()
|
|
# We don't set default for cachemode, as we need to distinguish default
|
|
# from user input later.
|
|
mg.add_argument('-nocache', dest='cachemode', action='store_const',
|
|
const='none', help='set cache mode "none" (O_DIRECT), '
|
|
'sets CACHEMODE environment variable')
|
|
mg.add_argument('-c', dest='cachemode',
|
|
help='sets CACHEMODE environment variable')
|
|
|
|
g_env.add_argument('-i', dest='aiomode', default='threads',
|
|
help='sets AIOMODE environment variable')
|
|
|
|
p.set_defaults(imgfmt='raw', imgproto='file')
|
|
|
|
format_list = ['raw', 'bochs', 'cloop', 'parallels', 'qcow', 'qcow2',
|
|
'qed', 'vdi', 'vpc', 'vhdx', 'vmdk', 'luks', 'dmg']
|
|
g_fmt = p.add_argument_group(
|
|
' image format options',
|
|
'The following options set the IMGFMT environment variable. '
|
|
'At most one choice is allowed, default is "raw"')
|
|
mg = g_fmt.add_mutually_exclusive_group()
|
|
for fmt in format_list:
|
|
mg.add_argument('-' + fmt, dest='imgfmt', action='store_const',
|
|
const=fmt, help=f'test {fmt}')
|
|
|
|
protocol_list = ['file', 'rbd', 'nbd', 'ssh', 'nfs', 'fuse']
|
|
g_prt = p.add_argument_group(
|
|
' image protocol options',
|
|
'The following options set the IMGPROTO environment variable. '
|
|
'At most one choice is allowed, default is "file"')
|
|
mg = g_prt.add_mutually_exclusive_group()
|
|
for prt in protocol_list:
|
|
mg.add_argument('-' + prt, dest='imgproto', action='store_const',
|
|
const=prt, help=f'test {prt}')
|
|
|
|
g_bash = p.add_argument_group('bash tests options',
|
|
'The following options are ignored by '
|
|
'python tests.')
|
|
# TODO: make support for the following options in iotests.py
|
|
g_bash.add_argument('-o', dest='imgopts',
|
|
help='options to pass to qemu-img create/convert, '
|
|
'sets IMGOPTS environment variable')
|
|
|
|
g_sel = p.add_argument_group('test selecting options',
|
|
'The following options specify test set '
|
|
'to run.')
|
|
g_sel.add_argument('-g', '--groups', metavar='group1,...',
|
|
help='include tests from these groups')
|
|
g_sel.add_argument('-x', '--exclude-groups', metavar='group1,...',
|
|
help='exclude tests from these groups')
|
|
g_sel.add_argument('--start-from', metavar='TEST',
|
|
help='Start from specified test: make sorted sequence '
|
|
'of tests as usual and then drop tests from the first '
|
|
'one to TEST (not inclusive). This may be used to '
|
|
'rerun failed ./check command, starting from the '
|
|
'middle of the process.')
|
|
g_sel.add_argument('tests', metavar='TEST_FILES', nargs='*',
|
|
help='tests to run, or "--" followed by a command')
|
|
g_sel.add_argument('--build-dir', default=get_default_path(),
|
|
help='Path to iotests build directory')
|
|
g_sel.add_argument('--source-dir',
|
|
default=get_default_path(follow_link=True),
|
|
help='Path to iotests build directory')
|
|
|
|
return p
|
|
|
|
|
|
if __name__ == '__main__':
|
|
args = make_argparser().parse_args()
|
|
|
|
env = TestEnv(source_dir=args.source_dir,
|
|
build_dir=args.build_dir,
|
|
imgfmt=args.imgfmt, imgproto=args.imgproto,
|
|
aiomode=args.aiomode, cachemode=args.cachemode,
|
|
imgopts=args.imgopts, misalign=args.misalign,
|
|
debug=args.debug, valgrind=args.valgrind,
|
|
gdb=args.gdb, qprint=args.print,
|
|
dry_run=args.dry_run)
|
|
|
|
if len(sys.argv) > 1 and sys.argv[-len(args.tests)-1] == '--':
|
|
if not args.tests:
|
|
sys.exit("missing command after '--'")
|
|
cmd = args.tests
|
|
env.print_env()
|
|
exec_pathstr = shutil.which(cmd[0])
|
|
if exec_pathstr is None:
|
|
sys.exit('command not found: ' + cmd[0])
|
|
exec_path = Path(exec_pathstr).resolve()
|
|
cmd[0] = str(exec_path)
|
|
full_env = env.prepare_subprocess(cmd)
|
|
os.chdir(exec_path.parent)
|
|
os.execve(cmd[0], cmd, full_env)
|
|
|
|
testfinder = TestFinder(test_dir=env.source_iotests)
|
|
|
|
groups = args.groups.split(',') if args.groups else None
|
|
x_groups = args.exclude_groups.split(',') if args.exclude_groups else None
|
|
|
|
group_local = os.path.join(env.source_iotests, 'group.local')
|
|
if os.path.isfile(group_local):
|
|
try:
|
|
testfinder.add_group_file(group_local)
|
|
except ValueError as e:
|
|
sys.exit(f"Failed to parse group file '{group_local}': {e}")
|
|
|
|
try:
|
|
tests = testfinder.find_tests(groups=groups, exclude_groups=x_groups,
|
|
tests=args.tests,
|
|
start_from=args.start_from)
|
|
if not tests:
|
|
raise ValueError('No tests selected')
|
|
except ValueError as e:
|
|
sys.exit(str(e))
|
|
|
|
if args.dry_run:
|
|
print('\n'.join(tests))
|
|
else:
|
|
with TestRunner(env, tap=args.tap,
|
|
color=args.color) as tr:
|
|
paths = [os.path.join(env.source_iotests, t) for t in tests]
|
|
ok = tr.run_tests(paths, args.jobs)
|
|
if not ok:
|
|
sys.exit(1)
|