Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add should_error kwarg to test() #13559

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion docs/markdown/Unit-tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,12 @@ In addition, sometimes a test fails set up so that it should fail even
if it is marked as an expected failure. The GNU standard approach in
this case is to exit the program with error code 99. Again, Meson will
detect this and report these tests as `ERROR`, ignoring the setting of
`should_fail`. This behavior was added in version 0.50.0.
`should_fail`. This behavior was added in version 0.50.0. In version
1.6.0 `should_fail` has been deprecated and renamed to `expected_fail`.

In version 1.6.0, `success_returncode` has been introduced. This makes
it possible to positively test for non-zero return codes. An example
of this would be to test if failure injection is detected in a test.

## Testing tool

Expand Down
18 changes: 18 additions & 0 deletions docs/yaml/functions/benchmark.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,29 @@ kwargs:

should_fail:
type: bool
deprecated: 1.6.0
default: false
description: |
when true the test is considered passed if the
executable returns a non-zero return value (i.e. reports an error)

expected_fail:
type: bool
since: 1.6.0
default: false
description: |
when true the test is considered passed if the
executable returns a non-zero return value (i.e. reports an error)

success_returncode:
type: int
since: 1.6.0
default: 0
description: |
the test is considered passed if the
executable returns the specified returncode,
only has effect when protocol is set to exitcode

suite:
type: str | list[str]
description: |
Expand Down
5 changes: 3 additions & 2 deletions mesonbuild/backend/backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,8 @@ class TestSerialisation:
is_parallel: bool
cmd_args: T.List[str]
env: mesonlib.EnvironmentVariables
should_fail: bool
expected_fail: bool
success_returncode: T.Optional[int]
timeout: T.Optional[int]
workdir: T.Optional[str]
extra_paths: T.List[str]
Expand Down Expand Up @@ -1284,7 +1285,7 @@ def create_test_serialisation(self, tests: T.List['Test']) -> T.List[TestSeriali
ts = TestSerialisation(t.get_name(), t.project_name, t.suite, cmd, is_cross,
exe_wrapper, self.environment.need_exe_wrapper(),
t.is_parallel, cmd_args, t_env,
t.should_fail, t.timeout, t.workdir,
t.expected_fail, t.success_returncode, t.timeout, t.workdir,
extra_paths, t.protocol, t.priority,
isinstance(exe, (build.Target, build.CustomTargetIndex)),
isinstance(exe, build.Executable),
Expand Down
11 changes: 10 additions & 1 deletion mesonbuild/interpreter/interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -2242,6 +2242,14 @@ def make_test(self, node: mparser.BaseNode,
if kwargs['timeout'] <= 0:
FeatureNew.single_use('test() timeout <= 0', '0.57.0', self.subproject, location=node)

expected_fail = False
if kwargs['should_fail'] is not None and kwargs['expected_fail'] is not None:
raise InvalidArguments('Tried to use both \'should_fail\' and \'expected_fail\'')
elif kwargs['should_fail'] is not None:
expected_fail = kwargs['should_fail']
elif kwargs['expected_fail'] is not None:
expected_fail = kwargs['expected_fail']

prj = self.subproject if self.is_subproject() else self.build.project_name

suite: T.List[str] = []
Expand All @@ -2258,7 +2266,8 @@ def make_test(self, node: mparser.BaseNode,
kwargs.get('is_parallel', False),
kwargs['args'],
env,
kwargs['should_fail'],
expected_fail,
kwargs['success_returncode'],
kwargs['timeout'],
kwargs['workdir'],
kwargs['protocol'],
Expand Down
5 changes: 3 additions & 2 deletions mesonbuild/interpreter/interpreterobjects.py
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ def __init__(self, name: str, project: str, suite: T.List[str],
is_parallel: bool,
cmd_args: T.List[T.Union[str, mesonlib.File, build.Target, ExternalProgram]],
env: mesonlib.EnvironmentVariables,
should_fail: bool, timeout: int, workdir: T.Optional[str], protocol: str,
expected_fail: bool, success_returncode: int, timeout: int, workdir: T.Optional[str], protocol: str,
priority: int, verbose: bool):
super().__init__()
self.name = name
Expand All @@ -766,7 +766,8 @@ def __init__(self, name: str, project: str, suite: T.List[str],
self.is_parallel = is_parallel
self.cmd_args = cmd_args
self.env = env
self.should_fail = should_fail
self.expected_fail = expected_fail
self.success_returncode = success_returncode
self.timeout = timeout
self.workdir = workdir
self.protocol = TestProtocol.from_str(protocol)
Expand Down
4 changes: 3 additions & 1 deletion mesonbuild/interpreter/kwargs.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ class BaseTest(TypedDict):
"""Shared base for the Rust module."""

args: T.List[T.Union[str, File, build.Target, ExternalProgram]]
should_fail: bool
should_fail: T.Optional[bool]
expected_fail: T.Optional[bool]
success_returncode: T.Optional[int]
timeout: int
workdir: T.Optional[str]
depends: T.List[T.Union[build.CustomTarget, build.BuildTarget]]
Expand Down
4 changes: 3 additions & 1 deletion mesonbuild/interpreter/type_checking.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,9 @@ def link_whole_validator(values: T.List[T.Union[StaticLibrary, CustomTarget, Cus
TEST_KWS: T.List[KwargInfo] = [
KwargInfo('args', ContainerTypeInfo(list, (str, File, BuildTarget, CustomTarget, CustomTargetIndex, ExternalProgram)),
listify=True, default=[]),
KwargInfo('should_fail', bool, default=False),
KwargInfo('should_fail', (bool, NoneType), deprecated='1.6.0', deprecated_message='Use expected_fail instead of should_fail'),
KwargInfo('expected_fail', (bool, NoneType), since='1.6.0'),
KwargInfo('success_returncode', (int, NoneType), since='1.6.0'),
KwargInfo('timeout', int, default=30),
KwargInfo('workdir', (str, NoneType), default=None,
validator=lambda x: 'must be an absolute path' if not os.path.isabs(x) else None),
Expand Down
10 changes: 8 additions & 2 deletions mesonbuild/mtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,8 @@ def __init__(self, test: TestSerialisation, test_env: T.Dict[str, str],
self.additional_error = ''
self.cmd: T.Optional[T.List[str]] = None
self.env = test_env
self.should_fail = test.should_fail
self.expected_fail = test.expected_fail
self.success_returncode = test.success_returncode
self.project = test.project_name
self.junit: T.Optional[et.ElementTree] = None
self.is_parallel = is_parallel
Expand Down Expand Up @@ -982,7 +983,7 @@ def _complete(self) -> None:
if self.res == TestResult.RUNNING:
self.res = TestResult.OK
assert isinstance(self.res, TestResult)
if self.should_fail and self.res in (TestResult.OK, TestResult.FAIL):
if self.expected_fail and self.res in (TestResult.OK, TestResult.FAIL):
self.res = TestResult.UNEXPECTEDPASS if self.res is TestResult.OK else TestResult.EXPECTEDFAIL
if self.stdo and not self.stdo.endswith('\n'):
self.stdo += '\n'
Expand Down Expand Up @@ -1038,6 +1039,11 @@ class TestRunExitCode(TestRun):
def complete(self) -> None:
if self.res != TestResult.RUNNING:
pass
elif self.success_returncode is not None:
if self.returncode == self.success_returncode:
self.res = TestResult.OK
else:
self.res = TestResult.FAIL
elif self.returncode == GNU_SKIP_RETURNCODE:
self.res = TestResult.SKIP
elif self.returncode == GNU_ERROR_RETURNCODE:
Expand Down
3 changes: 3 additions & 0 deletions test cases/common/278 success returncode/failing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int main(void) {
return 1;
}
4 changes: 4 additions & 0 deletions test cases/common/278 success returncode/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project('success returncode', 'c')

exe = executable('prog', 'failing.c')
test('errorcode', exe, success_returncode: 1)
3 changes: 2 additions & 1 deletion test cases/common/68 should fail/meson.build
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
project('should fail', 'c')

exe = executable('prog', 'failing.c')
test('failing', exe, should_fail : true)
test('should-failing', exe, should_fail : true)
test('expected-failing', exe, expected_fail : true)
3 changes: 3 additions & 0 deletions test cases/failing test/7 success returncode/failing.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
int main(void) {
return 0;
}
4 changes: 4 additions & 0 deletions test cases/failing test/7 success returncode/meson.build
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
project('success returncode', 'c')

exe = executable('prog', 'failing.c')
test('errorcode', exe, success_returncode: 1)
Loading