qemu/tests/qemu-iotests/check
Daniel P. Berrangé a9e21786da iotests: allow test discovery before building
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>
2023-03-22 15:08:26 +00:00

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)