aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2020-05-19 00:23:19 +0300
committerGitHub <noreply@github.com>2020-05-19 00:23:19 +0300
commit589a6249f0ffb0295c3f15233d1b6b3af9e4de16 (patch)
tree7abdbafcc8773a63e92741f88984001e4b8e43d8
parentb4b1a2c5a145c1459fc4563a289e164e23bd6a02 (diff)
parentaf787874a8e4eab8222382128ccfb5549b31c801 (diff)
downloadmeson-589a6249f0ffb0295c3f15233d1b6b3af9e4de16.zip
meson-589a6249f0ffb0295c3f15233d1b6b3af9e4de16.tar.gz
meson-589a6249f0ffb0295c3f15233d1b6b3af9e4de16.tar.bz2
Merge pull request #5986 from dcbaker/fix-tests-with-cross-binary-arguments
Fix tests with cross binary arguments
-rw-r--r--docs/markdown/Reference-manual.md7
-rw-r--r--docs/markdown/snippets/exe_wrapper_for_cross_built_tests.md9
-rw-r--r--mesonbuild/backend/backends.py23
-rw-r--r--mesonbuild/mtest.py43
-rwxr-xr-xrun_unittests.py198
-rwxr-xr-xtest cases/unit/72 cross test passed/exewrapper.py24
-rw-r--r--test cases/unit/72 cross test passed/meson.build19
-rw-r--r--test cases/unit/72 cross test passed/meson_options.txt5
-rw-r--r--test cases/unit/72 cross test passed/script.py7
-rw-r--r--test cases/unit/72 cross test passed/src/main.c6
-rw-r--r--test cases/unit/73 summary/meson.build (renamed from test cases/unit/72 summary/meson.build)0
-rw-r--r--test cases/unit/73 summary/subprojects/sub/meson.build (renamed from test cases/unit/72 summary/subprojects/sub/meson.build)0
-rw-r--r--test cases/unit/73 summary/subprojects/sub2/meson.build (renamed from test cases/unit/72 summary/subprojects/sub2/meson.build)0
-rw-r--r--test cases/unit/74 wrap file url/meson.build (renamed from test cases/unit/73 wrap file url/meson.build)0
-rw-r--r--test cases/unit/74 wrap file url/subprojects/foo-patch.tar.xz (renamed from test cases/unit/73 wrap file url/subprojects/foo-patch.tar.xz)bin228 -> 228 bytes
-rw-r--r--test cases/unit/74 wrap file url/subprojects/foo.tar.xz (renamed from test cases/unit/73 wrap file url/subprojects/foo.tar.xz)bin216 -> 216 bytes
-rw-r--r--test cases/unit/75 dep files/foo.c (renamed from test cases/unit/74 dep files/foo.c)0
-rw-r--r--test cases/unit/75 dep files/meson.build (renamed from test cases/unit/74 dep files/meson.build)0
-rw-r--r--test cases/unit/76 subdir libdir/meson.build (renamed from test cases/unit/75 subdir libdir/meson.build)0
-rw-r--r--test cases/unit/76 subdir libdir/subprojects/flub/meson.build (renamed from test cases/unit/75 subdir libdir/subprojects/flub/meson.build)0
20 files changed, 254 insertions, 87 deletions
diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md
index 1bd5ff0..9b5d657 100644
--- a/docs/markdown/Reference-manual.md
+++ b/docs/markdown/Reference-manual.md
@@ -1613,6 +1613,13 @@ object](#build-target-object) returned by
object](#external-program-object) returned by
[`find_program()`](#find_program).
+*Since 0.55.0* When cross compiling, if an exe_wrapper is needed and defined
+the environment variable `MESON_EXE_WRAPPER` will be set to the string value
+of that wrapper (implementation detail: using `mesonlib.join_args`). Test
+scripts may use this to run cross built binaries. If your test needs
+`MESON_EXE_WRAPPER` in cross build situations it is your responsibility to
+return code 77 to tell the harness to report "skip"
+
By default, environment variable
[`MALLOC_PERTURB_`](http://man7.org/linux/man-pages/man3/mallopt.3.html)
is automatically set by `meson test` to a random value between 1..255.
diff --git a/docs/markdown/snippets/exe_wrapper_for_cross_built_tests.md b/docs/markdown/snippets/exe_wrapper_for_cross_built_tests.md
new file mode 100644
index 0000000..ebdd8a7
--- /dev/null
+++ b/docs/markdown/snippets/exe_wrapper_for_cross_built_tests.md
@@ -0,0 +1,9 @@
+## Test scripts are given the exe wrapper if needed
+
+Meson will now set the `MESON_EXE_WRAPPER` as the properly wrapped and joined
+representation. For Unix-like OSes this means python's shelx.join, on Windows
+an implementation that attempts to properly quote windows argument is used.
+This allow wrapper scripts to run test binaries, instead of just skipping.
+
+for example, if the wrapper is `['emulator', '--script']`, it will be passed
+as `MESON_EXE_WRAPPER="emulator --script"`.
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py
index 3d12651..840c9a3 100644
--- a/mesonbuild/backend/backends.py
+++ b/mesonbuild/backend/backends.py
@@ -119,7 +119,8 @@ class TestSerialisation:
needs_exe_wrapper: bool, is_parallel: bool, cmd_args: T.List[str],
env: build.EnvironmentVariables, should_fail: bool,
timeout: T.Optional[int], workdir: T.Optional[str],
- extra_paths: T.List[str], protocol: TestProtocol, priority: int):
+ extra_paths: T.List[str], protocol: TestProtocol, priority: int,
+ cmd_is_built: bool):
self.name = name
self.project_name = project
self.suite = suite
@@ -138,6 +139,8 @@ class TestSerialisation:
self.protocol = protocol
self.priority = priority
self.needs_exe_wrapper = needs_exe_wrapper
+ self.cmd_is_built = cmd_is_built
+
def get_backend_from_name(backend: str, build: T.Optional[build.Build] = None, interpreter: T.Optional['Interpreter'] = None) -> T.Optional['Backend']:
if backend == 'ninja':
@@ -788,6 +791,15 @@ class Backend:
# E.g. an external verifier or simulator program run on a generated executable.
# Can always be run without a wrapper.
test_for_machine = MachineChoice.BUILD
+
+ # we allow passing compiled executables to tests, which may be cross built.
+ # We need to consider these as well when considering whether the target is cross or not.
+ for a in t.cmd_args:
+ if isinstance(a, build.BuildTarget):
+ if a.for_machine is MachineChoice.HOST:
+ test_for_machine = MachineChoice.HOST
+ break
+
is_cross = self.environment.is_cross_build(test_for_machine)
if is_cross and self.environment.need_exe_wrapper():
exe_wrapper = self.environment.get_exe_wrapper()
@@ -801,6 +813,7 @@ class Backend:
extra_paths = self.determine_windows_extra_paths(exe, extra_bdeps)
else:
extra_paths = []
+
cmd_args = []
for a in unholder(t.cmd_args):
if isinstance(a, build.BuildTarget):
@@ -810,6 +823,11 @@ class Backend:
cmd_args.append(a)
elif isinstance(a, str):
cmd_args.append(a)
+ elif isinstance(a, build.Executable):
+ p = self.construct_target_rel_path(a, t.workdir)
+ if p == a.get_filename():
+ p = './' + p
+ cmd_args.append(p)
elif isinstance(a, build.Target):
cmd_args.append(self.construct_target_rel_path(a, t.workdir))
else:
@@ -818,7 +836,8 @@ class Backend:
exe_wrapper, self.environment.need_exe_wrapper(),
t.is_parallel, cmd_args, t.env,
t.should_fail, t.timeout, t.workdir,
- extra_paths, t.protocol, t.priority)
+ extra_paths, t.protocol, t.priority,
+ isinstance(exe, build.Executable))
arr.append(ts)
return arr
diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py
index 4592c90..4aafe62 100644
--- a/mesonbuild/mtest.py
+++ b/mesonbuild/mtest.py
@@ -42,7 +42,7 @@ from . import build
from . import environment
from . import mlog
from .dependencies import ExternalProgram
-from .mesonlib import MesonException, get_wine_shortpath, split_args
+from .mesonlib import MesonException, get_wine_shortpath, split_args, join_args
from .backend.backends import TestProtocol
if T.TYPE_CHECKING:
@@ -311,7 +311,7 @@ class TAPParser:
yield self.Version(version=version)
continue
- if len(line) == 0:
+ if not line:
continue
yield self.Error('unexpected input at line {}'.format((lineno,)))
@@ -609,20 +609,20 @@ class SingleTestRunner:
return ['java', '-jar'] + self.test.fname
elif not self.test.is_cross_built and run_with_mono(self.test.fname[0]):
return ['mono'] + self.test.fname
- else:
- if self.test.is_cross_built and self.test.needs_exe_wrapper:
- if self.test.exe_runner is None:
- # Can not run test on cross compiled executable
- # because there is no execute wrapper.
- return None
- else:
- if not self.test.exe_runner.found():
- msg = 'The exe_wrapper defined in the cross file {!r} was not ' \
- 'found. Please check the command and/or add it to PATH.'
- raise TestException(msg.format(self.test.exe_runner.name))
- return self.test.exe_runner.get_command() + self.test.fname
- else:
- return self.test.fname
+ elif self.test.cmd_is_built and self.test.needs_exe_wrapper:
+ if self.test.exe_runner is None:
+ # Can not run test on cross compiled executable
+ # because there is no execute wrapper.
+ return None
+ elif self.test.cmd_is_built:
+ # If the command is not built (ie, its a python script),
+ # then we don't check for the exe-wrapper
+ if not self.test.exe_runner.found():
+ msg = ('The exe_wrapper defined in the cross file {!r} was not '
+ 'found. Please check the command and/or add it to PATH.')
+ raise TestException(msg.format(self.test.exe_runner.name))
+ return self.test.exe_runner.get_command() + self.test.fname
+ return self.test.fname
def run(self) -> TestRun:
cmd = self._get_cmd()
@@ -638,7 +638,7 @@ class SingleTestRunner:
def _run_cmd(self, cmd: T.List[str]) -> TestRun:
starttime = time.time()
- if len(self.test.extra_paths) > 0:
+ if self.test.extra_paths:
self.env['PATH'] = os.pathsep.join(self.test.extra_paths + ['']) + self.env['PATH']
winecmd = []
for c in cmd:
@@ -867,6 +867,9 @@ class TestHarness:
env = os.environ.copy()
test_env = test.env.get_env(env)
env.update(test_env)
+ if (test.is_cross_built and test.needs_exe_wrapper and
+ test.exe_runner and test.exe_runner.found()):
+ env['MESON_EXE_WRAPPER'] = join_args(test.exe_runner.get_command())
return SingleTestRunner(test, test_env, env, options)
def process_test_result(self, result: TestRun) -> None:
@@ -941,7 +944,7 @@ class TestHarness:
self.junit.write()
def print_collected_logs(self) -> None:
- if len(self.collected_logs) > 0:
+ if self.collected_logs:
if len(self.collected_logs) > 10:
print('\nThe output from 10 first failed tests:\n')
else:
@@ -1023,7 +1026,7 @@ class TestHarness:
print('No tests defined.')
return []
- if len(self.options.include_suites) or len(self.options.exclude_suites):
+ if self.options.include_suites or self.options.exclude_suites:
tests = []
for tst in self.tests:
if self.test_suitable(tst):
@@ -1085,7 +1088,7 @@ class TestHarness:
if len(self.suites) > 1 and test.suite:
rv = TestHarness.split_suite_string(test.suite[0])[0]
s = "+".join(TestHarness.split_suite_string(s)[1] for s in test.suite)
- if len(s):
+ if s:
rv += ":"
return rv + s + " / " + test.name
else:
diff --git a/run_unittests.py b/run_unittests.py
index c04b825..7e5dffc 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -40,6 +40,7 @@ from contextlib import contextmanager
from glob import glob
from pathlib import (PurePath, Path)
from distutils.dir_util import copy_tree
+import typing
import mesonbuild.mlog
import mesonbuild.depfile
@@ -3426,67 +3427,6 @@ int main(int argc, char **argv) {
f.write('public class Foo { public static void main() {} }')
self._run(self.meson_command + ['init', '-b'], workdir=tmpdir)
- # The test uses mocking and thus requires that
- # the current process is the one to run the Meson steps.
- # If we are using an external test executable (most commonly
- # in Debian autopkgtests) then the mocking won't work.
- @unittest.skipIf('MESON_EXE' in os.environ, 'MESON_EXE is defined, can not use mocking.')
- def test_cross_file_system_paths(self):
- if is_windows():
- raise unittest.SkipTest('system crossfile paths not defined for Windows (yet)')
- if is_sunos():
- cc = 'gcc'
- else:
- cc = 'cc'
-
- testdir = os.path.join(self.common_test_dir, '1 trivial')
- cross_content = textwrap.dedent("""\
- [binaries]
- c = '/usr/bin/{}'
- ar = '/usr/bin/ar'
- strip = '/usr/bin/ar'
-
- [properties]
-
- [host_machine]
- system = 'linux'
- cpu_family = 'x86'
- cpu = 'i686'
- endian = 'little'
- """.format(cc))
-
- with tempfile.TemporaryDirectory() as d:
- dir_ = os.path.join(d, 'meson', 'cross')
- os.makedirs(dir_)
- with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
- f.write(cross_content)
- name = os.path.basename(f.name)
-
- with mock.patch.dict(os.environ, {'XDG_DATA_HOME': d}):
- self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
- self.wipe()
-
- with mock.patch.dict(os.environ, {'XDG_DATA_DIRS': d}):
- os.environ.pop('XDG_DATA_HOME', None)
- self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
- self.wipe()
-
- with tempfile.TemporaryDirectory() as d:
- dir_ = os.path.join(d, '.local', 'share', 'meson', 'cross')
- os.makedirs(dir_)
- with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
- f.write(cross_content)
- name = os.path.basename(f.name)
-
- # If XDG_DATA_HOME is set in the environment running the
- # tests this test will fail, os mock the environment, pop
- # it, then test
- with mock.patch.dict(os.environ):
- os.environ.pop('XDG_DATA_HOME', None)
- with mock.patch('mesonbuild.coredata.os.path.expanduser', lambda x: x.replace('~', d)):
- self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
- self.wipe()
-
def test_compiler_run_command(self):
'''
The test checks that the compiler object can be passed to
@@ -4542,7 +4482,7 @@ recommended as it is not supported on some platforms''')
self._run(self.mconf_command + [self.builddir])
def test_summary(self):
- testdir = os.path.join(self.unit_test_dir, '72 summary')
+ testdir = os.path.join(self.unit_test_dir, '73 summary')
out = self.init(testdir)
expected = textwrap.dedent(r'''
Some Subproject 2.0
@@ -4596,7 +4536,7 @@ recommended as it is not supported on some platforms''')
self.assertPathDoesNotExist(os.path.join(self.builddir, prog))
def test_spurious_reconfigure_built_dep_file(self):
- testdir = os.path.join(self.unit_test_dir, '74 dep files')
+ testdir = os.path.join(self.unit_test_dir, '75 dep files')
# Regression test: Spurious reconfigure was happening when build
# directory is inside source directory.
@@ -6702,7 +6642,7 @@ c = ['{0}']
return hashlib.sha256(f.read()).hexdigest()
def test_wrap_with_file_url(self):
- testdir = os.path.join(self.unit_test_dir, '73 wrap file url')
+ testdir = os.path.join(self.unit_test_dir, '74 wrap file url')
source_filename = os.path.join(testdir, 'subprojects', 'foo.tar.xz')
patch_filename = os.path.join(testdir, 'subprojects', 'foo-patch.tar.xz')
wrap_filename = os.path.join(testdir, 'subprojects', 'foo.wrap')
@@ -6793,7 +6733,7 @@ class LinuxCrossArmTests(BaseLinuxCrossTests):
def test_cross_libdir_subproject(self):
# Guard against a regression where calling "subproject"
# would reset the value of libdir to its default value.
- testdir = os.path.join(self.unit_test_dir, '75 subdir libdir')
+ testdir = os.path.join(self.unit_test_dir, '76 subdir libdir')
self.init(testdir, extra_args=['--libdir=fuf'])
for i in self.introspect('--buildoptions'):
if i['name'] == 'libdir':
@@ -7618,6 +7558,134 @@ class CrossFileTests(BasePlatformTests):
This is mainly aimed to testing overrides from cross files.
"""
+ def _cross_file_generator(self, *, needs_exe_wrapper: bool = False,
+ exe_wrapper: typing.Optional[typing.List[str]] = None) -> str:
+ if is_windows():
+ raise unittest.SkipTest('Cannot run this test on non-mingw/non-cygwin windows')
+ if is_sunos():
+ cc = 'gcc'
+ else:
+ cc = 'cc'
+
+ return textwrap.dedent("""\
+ [binaries]
+ c = '/usr/bin/{}'
+ ar = '/usr/bin/ar'
+ strip = '/usr/bin/ar'
+ {}
+
+ [properties]
+ needs_exe_wrapper = {}
+
+ [host_machine]
+ system = 'linux'
+ cpu_family = 'x86'
+ cpu = 'i686'
+ endian = 'little'
+ """.format(cc,
+ 'exe_wrapper = {}'.format(str(exe_wrapper)) if exe_wrapper is not None else '',
+ needs_exe_wrapper))
+
+ def _stub_exe_wrapper(self) -> str:
+ return textwrap.dedent('''\
+ #!/usr/bin/env python3
+ import subprocess
+ import sys
+
+ sys.exit(subprocess.run(sys.argv[1:]).returncode)
+ ''')
+
+ def test_needs_exe_wrapper_true(self):
+ testdir = os.path.join(self.unit_test_dir, '72 cross test passed')
+ with tempfile.TemporaryDirectory() as d:
+ p = Path(d) / 'crossfile'
+ with p.open('wt') as f:
+ f.write(self._cross_file_generator(needs_exe_wrapper=True))
+ self.init(testdir, extra_args=['--cross-file=' + str(p)])
+ out = self.run_target('test')
+ self.assertRegex(out, r'Skipped:\s*1\s*\n')
+
+ def test_needs_exe_wrapper_false(self):
+ testdir = os.path.join(self.unit_test_dir, '72 cross test passed')
+ with tempfile.TemporaryDirectory() as d:
+ p = Path(d) / 'crossfile'
+ with p.open('wt') as f:
+ f.write(self._cross_file_generator(needs_exe_wrapper=False))
+ self.init(testdir, extra_args=['--cross-file=' + str(p)])
+ out = self.run_target('test')
+ self.assertNotRegex(out, r'Skipped:\s*1\n')
+
+ def test_needs_exe_wrapper_true_wrapper(self):
+ testdir = os.path.join(self.unit_test_dir, '72 cross test passed')
+ with tempfile.TemporaryDirectory() as d:
+ s = Path(d) / 'wrapper.py'
+ with s.open('wt') as f:
+ f.write(self._stub_exe_wrapper())
+ s.chmod(0o774)
+ p = Path(d) / 'crossfile'
+ with p.open('wt') as f:
+ f.write(self._cross_file_generator(
+ needs_exe_wrapper=True,
+ exe_wrapper=[str(s)]))
+
+ self.init(testdir, extra_args=['--cross-file=' + str(p), '-Dexpect=true'])
+ out = self.run_target('test')
+ self.assertRegex(out, r'Ok:\s*3\s*\n')
+
+ def test_cross_exe_passed_no_wrapper(self):
+ testdir = os.path.join(self.unit_test_dir, '72 cross test passed')
+ with tempfile.TemporaryDirectory() as d:
+ p = Path(d) / 'crossfile'
+ with p.open('wt') as f:
+ f.write(self._cross_file_generator(needs_exe_wrapper=True))
+
+ self.init(testdir, extra_args=['--cross-file=' + str(p)])
+ self.build()
+ out = self.run_target('test')
+ self.assertRegex(out, r'Skipped:\s*1\s*\n')
+
+ # The test uses mocking and thus requires that the current process is the
+ # one to run the Meson steps. If we are using an external test executable
+ # (most commonly in Debian autopkgtests) then the mocking won't work.
+ @unittest.skipIf('MESON_EXE' in os.environ, 'MESON_EXE is defined, can not use mocking.')
+ def test_cross_file_system_paths(self):
+ if is_windows():
+ raise unittest.SkipTest('system crossfile paths not defined for Windows (yet)')
+
+ testdir = os.path.join(self.common_test_dir, '1 trivial')
+ cross_content = self._cross_file_generator()
+ with tempfile.TemporaryDirectory() as d:
+ dir_ = os.path.join(d, 'meson', 'cross')
+ os.makedirs(dir_)
+ with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
+ f.write(cross_content)
+ name = os.path.basename(f.name)
+
+ with mock.patch.dict(os.environ, {'XDG_DATA_HOME': d}):
+ self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
+ self.wipe()
+
+ with mock.patch.dict(os.environ, {'XDG_DATA_DIRS': d}):
+ os.environ.pop('XDG_DATA_HOME', None)
+ self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
+ self.wipe()
+
+ with tempfile.TemporaryDirectory() as d:
+ dir_ = os.path.join(d, '.local', 'share', 'meson', 'cross')
+ os.makedirs(dir_)
+ with tempfile.NamedTemporaryFile('w', dir=dir_, delete=False) as f:
+ f.write(cross_content)
+ name = os.path.basename(f.name)
+
+ # If XDG_DATA_HOME is set in the environment running the
+ # tests this test will fail, os mock the environment, pop
+ # it, then test
+ with mock.patch.dict(os.environ):
+ os.environ.pop('XDG_DATA_HOME', None)
+ with mock.patch('mesonbuild.coredata.os.path.expanduser', lambda x: x.replace('~', d)):
+ self.init(testdir, extra_args=['--cross-file=' + name], inprocess=True)
+ self.wipe()
+
def test_cross_file_dirs(self):
testcase = os.path.join(self.unit_test_dir, '60 native file override')
self.init(testcase, default_args=False,
diff --git a/test cases/unit/72 cross test passed/exewrapper.py b/test cases/unit/72 cross test passed/exewrapper.py
new file mode 100755
index 0000000..2c15ed6
--- /dev/null
+++ b/test cases/unit/72 cross test passed/exewrapper.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+# Test that the MESON_EXE_WRAPPER environment variable is set
+
+import argparse
+import os
+import sys
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('binary') # unused, but needed for test behavior
+ parser.add_argument('--expected', action='store_true')
+ args = parser.parse_args()
+
+ defined = 'MESON_EXE_WRAPPER' in os.environ
+
+ if args.expected != defined:
+ print(os.environ, file=sys.stderr)
+ return 1
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/test cases/unit/72 cross test passed/meson.build b/test cases/unit/72 cross test passed/meson.build
new file mode 100644
index 0000000..4deb74b
--- /dev/null
+++ b/test cases/unit/72 cross test passed/meson.build
@@ -0,0 +1,19 @@
+project(
+ 'cross test passed',
+ 'c',
+ version : '>= 0.51'
+)
+
+e = executable('exec', 'src/main.c')
+
+py = import('python').find_installation()
+
+test('root', e)
+test('main', py, args : [meson.current_source_dir() / 'script.py', e])
+
+wrapper_args = []
+if get_option('expect')
+ wrapper_args += '--expected'
+endif
+
+test('exe_wrapper in env', py, args : [meson.current_source_dir() / 'exewrapper.py', e, wrapper_args])
diff --git a/test cases/unit/72 cross test passed/meson_options.txt b/test cases/unit/72 cross test passed/meson_options.txt
new file mode 100644
index 0000000..084c776
--- /dev/null
+++ b/test cases/unit/72 cross test passed/meson_options.txt
@@ -0,0 +1,5 @@
+option(
+ 'expect',
+ type : 'boolean',
+ value : false,
+)
diff --git a/test cases/unit/72 cross test passed/script.py b/test cases/unit/72 cross test passed/script.py
new file mode 100644
index 0000000..257cd30
--- /dev/null
+++ b/test cases/unit/72 cross test passed/script.py
@@ -0,0 +1,7 @@
+#!/usr/bin/env python3
+
+import subprocess
+import sys
+
+if __name__ == "__main__":
+ sys.exit(subprocess.run(sys.argv[1:]).returncode)
diff --git a/test cases/unit/72 cross test passed/src/main.c b/test cases/unit/72 cross test passed/src/main.c
new file mode 100644
index 0000000..490b4a6
--- /dev/null
+++ b/test cases/unit/72 cross test passed/src/main.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char const *argv[])
+{
+ return 0;
+}
diff --git a/test cases/unit/72 summary/meson.build b/test cases/unit/73 summary/meson.build
index df4540d..df4540d 100644
--- a/test cases/unit/72 summary/meson.build
+++ b/test cases/unit/73 summary/meson.build
diff --git a/test cases/unit/72 summary/subprojects/sub/meson.build b/test cases/unit/73 summary/subprojects/sub/meson.build
index e7d7833..e7d7833 100644
--- a/test cases/unit/72 summary/subprojects/sub/meson.build
+++ b/test cases/unit/73 summary/subprojects/sub/meson.build
diff --git a/test cases/unit/72 summary/subprojects/sub2/meson.build b/test cases/unit/73 summary/subprojects/sub2/meson.build
index 86b9cfd..86b9cfd 100644
--- a/test cases/unit/72 summary/subprojects/sub2/meson.build
+++ b/test cases/unit/73 summary/subprojects/sub2/meson.build
diff --git a/test cases/unit/73 wrap file url/meson.build b/test cases/unit/74 wrap file url/meson.build
index 3bd3b25..3bd3b25 100644
--- a/test cases/unit/73 wrap file url/meson.build
+++ b/test cases/unit/74 wrap file url/meson.build
diff --git a/test cases/unit/73 wrap file url/subprojects/foo-patch.tar.xz b/test cases/unit/74 wrap file url/subprojects/foo-patch.tar.xz
index fdb026c..fdb026c 100644
--- a/test cases/unit/73 wrap file url/subprojects/foo-patch.tar.xz
+++ b/test cases/unit/74 wrap file url/subprojects/foo-patch.tar.xz
Binary files differ
diff --git a/test cases/unit/73 wrap file url/subprojects/foo.tar.xz b/test cases/unit/74 wrap file url/subprojects/foo.tar.xz
index 2ed6ab4..2ed6ab4 100644
--- a/test cases/unit/73 wrap file url/subprojects/foo.tar.xz
+++ b/test cases/unit/74 wrap file url/subprojects/foo.tar.xz
Binary files differ
diff --git a/test cases/unit/74 dep files/foo.c b/test cases/unit/75 dep files/foo.c
index e69de29..e69de29 100644
--- a/test cases/unit/74 dep files/foo.c
+++ b/test cases/unit/75 dep files/foo.c
diff --git a/test cases/unit/74 dep files/meson.build b/test cases/unit/75 dep files/meson.build
index 4829f56..4829f56 100644
--- a/test cases/unit/74 dep files/meson.build
+++ b/test cases/unit/75 dep files/meson.build
diff --git a/test cases/unit/75 subdir libdir/meson.build b/test cases/unit/76 subdir libdir/meson.build
index 5099c91..5099c91 100644
--- a/test cases/unit/75 subdir libdir/meson.build
+++ b/test cases/unit/76 subdir libdir/meson.build
diff --git a/test cases/unit/75 subdir libdir/subprojects/flub/meson.build b/test cases/unit/76 subdir libdir/subprojects/flub/meson.build
index 7bfd2c5..7bfd2c5 100644
--- a/test cases/unit/75 subdir libdir/subprojects/flub/meson.build
+++ b/test cases/unit/76 subdir libdir/subprojects/flub/meson.build