Skip to content

Commit f57213f

Browse files
berrangehuth
authored andcommitted
tests/functional: enable pre-emptive caching of assets
Many tests need to access assets stored on remote sites. We don't want to download these during test execution when run by meson, since this risks hitting test timeouts when data transfers are slow. Add support for pre-emptive caching of assets by setting the env var QEMU_TEST_PRECACHE to point to a timestamp file. When this is set, instead of running the test, the assets will be downloaded and saved to the cache, then the timestamp file created. A meson custom target is created as a dependency of each test suite to trigger the pre-emptive caching logic before the test runs. When run in caching mode, it will locate assets by looking for class level variables with a name prefix "ASSET_", and type "Asset". At the ninja level ninja test --suite functional will speculatively download any assets that are not already cached, so it is advisable to set a timeout multiplier. QEMU_TEST_NO_DOWNLOAD=1 ninja test --suite functional will fail the test if a required asset is not already cached ninja precache-functional will download and cache all assets required by the functional tests At the make level, precaching is always done by make check-functional Signed-off-by: Daniel P. Berrangé <[email protected]> Tested-by: Richard Henderson <[email protected]> [thuth: Remove the duplicated "path = os.path.basename(...)" line] Message-ID: <[email protected]> Signed-off-by: Thomas Huth <[email protected]>
1 parent 9903217 commit f57213f

File tree

4 files changed

+74
-3
lines changed

4 files changed

+74
-3
lines changed

tests/Makefile.include

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,8 @@ $(FUNCTIONAL_TARGETS):
161161

162162
.PHONY: check-functional
163163
check-functional:
164-
@$(MAKE) SPEED=thorough check-func check-func-quick
164+
@$(NINJA) precache-functional
165+
@QEMU_TEST_NO_DOWNLOAD=1 $(MAKE) SPEED=thorough check-func check-func-quick
165166

166167
# Consolidated targets
167168

tests/functional/meson.build

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ tests_x86_64_system_quick = [
3939
tests_x86_64_system_thorough = [
4040
]
4141

42+
precache_all = []
4243
foreach speed : ['quick', 'thorough']
4344
foreach dir : target_dirs
4445

@@ -78,15 +79,43 @@ foreach speed : ['quick', 'thorough']
7879
meson.current_source_dir())
7980

8081
foreach test : target_tests
81-
test('func-@0@/@1@'.format(target_base, test),
82+
testname = '@0@-@1@'.format(target_base, test)
83+
testfile = 'test_' + test + '.py'
84+
testpath = meson.current_source_dir() / testfile
85+
teststamp = testname + '.tstamp'
86+
test_precache_env = environment()
87+
test_precache_env.set('QEMU_TEST_PRECACHE', meson.current_build_dir() / teststamp)
88+
test_precache_env.set('PYTHONPATH', meson.project_source_root() / 'python:' +
89+
meson.current_source_dir())
90+
precache = custom_target('func-precache-' + testname,
91+
output: teststamp,
92+
command: [python, testpath],
93+
depend_files: files(testpath),
94+
build_by_default: false,
95+
env: test_precache_env)
96+
precache_all += precache
97+
98+
# Ideally we would add 'precache' to 'depends' here, such that
99+
# 'build_by_default: false' lets the pre-caching automatically
100+
# run immediately before the test runs. In practice this is
101+
# broken in meson, with it running the pre-caching in the normal
102+
# compile phase https://github.com/mesonbuild/meson/issues/2518
103+
# If the above bug ever gets fixed, when QEMU changes the min
104+
# meson version, add the 'depends' and remove the custom
105+
# 'run_target' logic below & in Makefile.include
106+
test('func-' + testname,
82107
python,
83108
depends: [test_deps, test_emulator, emulator_modules],
84109
env: test_env,
85-
args: [meson.current_source_dir() / 'test_' + test + '.py'],
110+
args: [testpath],
86111
protocol: 'tap',
87112
timeout: test_timeouts.get(test, 60),
88113
priority: test_timeouts.get(test, 60),
89114
suite: suites)
90115
endforeach
91116
endforeach
92117
endforeach
118+
119+
run_target('precache-functional',
120+
depends: precache_all,
121+
command: ['true'])

tests/functional/qemu_test/asset.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
import logging
1010
import os
1111
import subprocess
12+
import sys
13+
import unittest
1214
import urllib.request
1315
from pathlib import Path
1416
from shutil import copyfileobj
@@ -62,6 +64,9 @@ def fetch(self):
6264
self.cache_file, self.url)
6365
return str(self.cache_file)
6466

67+
if os.environ.get("QEMU_TEST_NO_DOWNLOAD", False):
68+
raise Exception("Asset cache is invalid and downloads disabled")
69+
6570
self.log.info("Downloading %s to %s...", self.url, self.cache_file)
6671
tmp_cache_file = self.cache_file.with_suffix(".download")
6772

@@ -95,3 +100,32 @@ def fetch(self):
95100

96101
self.log.info("Cached %s at %s" % (self.url, self.cache_file))
97102
return str(self.cache_file)
103+
104+
def precache_test(test):
105+
log = logging.getLogger('qemu-test')
106+
log.setLevel(logging.DEBUG)
107+
handler = logging.StreamHandler(sys.stdout)
108+
handler.setLevel(logging.DEBUG)
109+
formatter = logging.Formatter(
110+
'%(asctime)s - %(name)s - %(levelname)s - %(message)s')
111+
handler.setFormatter(formatter)
112+
log.addHandler(handler)
113+
for name, asset in vars(test.__class__).items():
114+
if name.startswith("ASSET_") and type(asset) == Asset:
115+
log.info("Attempting to cache '%s'" % asset)
116+
asset.fetch()
117+
log.removeHandler(handler)
118+
119+
def precache_suite(suite):
120+
for test in suite:
121+
if isinstance(test, unittest.TestSuite):
122+
Asset.precache_suite(test)
123+
elif isinstance(test, unittest.TestCase):
124+
Asset.precache_test(test)
125+
126+
def precache_suites(path, cacheTstamp):
127+
loader = unittest.loader.defaultTestLoader
128+
tests = loader.loadTestsFromNames([path], None)
129+
130+
with open(cacheTstamp, "w") as fh:
131+
Asset.precache_suite(tests)

tests/functional/qemu_test/testcase.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from qemu.machine import QEMUMachine
2222
from qemu.utils import kvm_available, tcg_available
2323

24+
from .asset import Asset
2425
from .cmd import run_cmd
2526
from .config import BUILD_DIR
2627

@@ -58,6 +59,12 @@ def tearDown(self):
5859

5960
def main():
6061
path = os.path.basename(sys.argv[0])[:-3]
62+
63+
cache = os.environ.get("QEMU_TEST_PRECACHE", None)
64+
if cache is not None:
65+
Asset.precache_suites(path, cache)
66+
return
67+
6168
tr = pycotap.TAPTestRunner(message_log = pycotap.LogMode.LogToError,
6269
test_output_log = pycotap.LogMode.LogToError)
6370
unittest.main(module = None, testRunner = tr, argv=["__dummy__", path])

0 commit comments

Comments
 (0)