From 07ff9c61fed420af33f9d1a561512ff2c6cd21d2 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 16 Oct 2020 12:11:15 -0700 Subject: mtest: Handle subtest results as a dict for non tap tests we want to associate names with the tests, to that end store them as a dict. For TAP tests, we'll store the "name" as an integer string that coresponds to the order that the tests were run in. --- mesonbuild/mtest.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 7e3ebea..c31ad1a 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -595,17 +595,17 @@ class JunitBuilder(TestLogger): 'testsuite', name=suitename, tests=str(len(test.results)), - errors=str(sum(1 for r in test.results if r in + errors=str(sum(1 for r in test.results.values() if r in {TestResult.INTERRUPT, TestResult.ERROR})), - failures=str(sum(1 for r in test.results if r in + failures=str(sum(1 for r in test.results.values() if r in {TestResult.FAIL, TestResult.UNEXPECTEDPASS, TestResult.TIMEOUT})), - skipped=str(sum(1 for r in test.results if r is TestResult.SKIP)), + skipped=str(sum(1 for r in test.results.values() if r is TestResult.SKIP)), ) - for i, result in enumerate(test.results): + for i, result in test.results.items(): # Both name and classname are required. Set them both to the # number of the test in a TAP test, as TAP doesn't give names. - testcase = et.SubElement(suite, 'testcase', name=str(i), classname=str(i)) + testcase = et.SubElement(suite, 'testcase', name=i, classname=i) if result is TestResult.SKIP: et.SubElement(testcase, 'skipped') elif result is TestResult.ERROR: @@ -675,7 +675,7 @@ class TestRun: self.test = test self._num = None # type: T.Optional[int] self.name = name - self.results = list() # type: T.List[TestResult] + self.results: T.Dict[str, TestResult] = {} self.returncode = 0 self.starttime = None # type: T.Optional[float] self.duration = None # type: T.Optional[float] @@ -713,23 +713,23 @@ class TestRun: res = TestResult.EXPECTEDFAIL if bool(returncode) else TestResult.UNEXPECTEDPASS else: res = TestResult.FAIL if bool(returncode) else TestResult.OK - self.complete(res, [], returncode, stdo, stde, cmd, **kwargs) + self.complete(res, {}, returncode, stdo, stde, cmd, **kwargs) def complete_tap(self, returncode: int, stdo: str, stde: str, cmd: T.List[str]) -> None: res = None # type: T.Optional[TestResult] - results = [] # type: T.List[TestResult] + results = {} # type: T.Dict[str, TestResult] failed = False - for i in TAPParser(io.StringIO(stdo)).parse(): + for n, i in enumerate(TAPParser(io.StringIO(stdo)).parse()): if isinstance(i, TAPParser.Bailout): - results.append(TestResult.ERROR) + results[str(n)] = TestResult.ERROR failed = True elif isinstance(i, TAPParser.Test): - results.append(i.result) + results[str(n)] = i.result if i.result not in {TestResult.OK, TestResult.EXPECTEDFAIL, TestResult.SKIP}: failed = True elif isinstance(i, TAPParser.Error): - results.append(TestResult.ERROR) + results[str(n)] = TestResult.ERROR stde += '\nTAP parsing error: ' + i.message failed = True @@ -739,7 +739,7 @@ class TestRun: if res is None: # Now determine the overall result of the test based on the outcome of the subcases - if all(t is TestResult.SKIP for t in results): + if all(t is TestResult.SKIP for t in results.values()): # This includes the case where num_tests is zero res = TestResult.SKIP elif self.should_fail: @@ -756,13 +756,13 @@ class TestRun: self._num = TestRun.TEST_NUM return self._num - def complete(self, res: TestResult, results: T.List[TestResult], + def complete(self, res: TestResult, results: T.Dict[str, TestResult], returncode: int, stdo: T.Optional[str], stde: T.Optional[str], cmd: T.List[str], *, junit: T.Optional[et.ElementTree] = None) -> None: assert isinstance(res, TestResult) self.res = res - self.results = results + self.results = results # May be empty self.returncode = returncode self.duration = time.time() - self.starttime self.stdo = stdo @@ -906,7 +906,7 @@ class SingleTestRunner: self.runobj.start() if cmd is None: skip_stdout = 'Not run because can not execute cross compiled binaries.' - self.runobj.complete(TestResult.SKIP, [], GNU_SKIP_RETURNCODE, skip_stdout, None, None) + self.runobj.complete(TestResult.SKIP, {}, GNU_SKIP_RETURNCODE, skip_stdout, None, None) else: wrap = TestHarness.get_wrapper(self.options) if self.options.gdb: @@ -1063,7 +1063,7 @@ class SingleTestRunner: stdo = "" stde = additional_error if result: - self.runobj.complete(result, [], returncode, stdo, stde, cmd) + self.runobj.complete(result, {}, returncode, stdo, stde, cmd) else: if self.test.protocol is TestProtocol.EXITCODE: self.runobj.complete_exitcode(returncode, stdo, stde, cmd) -- cgit v1.1 From d89ec98b4763cda13da0ae22515c27f4dfe5c1b9 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Fri, 16 Oct 2020 12:37:30 -0700 Subject: mtest: Add support for rust unit tests Rust has it's own built in unit test format, which is invoked by compiling a rust executable with the `--test` flag to rustc. The tests are then run by simply invoking that binary. They output a custom test format, which this patch adds parsing support for. This means that we can report each subtest in the junit we generate correctly, which should be helpful for orchestration systems like gitlab and jenkins which can parse junit XML. --- docs/markdown/Reference-manual.md | 1 + docs/markdown/snippets/rust_test_format_support.md | 4 +++ mesonbuild/backend/backends.py | 5 +++ mesonbuild/interpreter.py | 4 +-- mesonbuild/mtest.py | 39 ++++++++++++++++++++++ test cases/rust/9 unit tests/meson.build | 32 ++++++++++++++++++ test cases/rust/9 unit tests/test.rs | 24 +++++++++++++ 7 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 docs/markdown/snippets/rust_test_format_support.md create mode 100644 test cases/rust/9 unit tests/meson.build create mode 100644 test cases/rust/9 unit tests/test.rs diff --git a/docs/markdown/Reference-manual.md b/docs/markdown/Reference-manual.md index 924047c..525c3da 100644 --- a/docs/markdown/Reference-manual.md +++ b/docs/markdown/Reference-manual.md @@ -1745,6 +1745,7 @@ test(..., env: nomalloc, ...) to record the outcome of the test). - `tap`: [Test Anything Protocol](https://www.testanything.org/). - `gtest` *(since 0.55.0)*: for Google Tests. + - `rust` *(since 0.56.0)*: for native rust tests - `priority` *(since 0.52.0)*:specifies the priority of a test. Tests with a higher priority are *started* before tests with a lower priority. diff --git a/docs/markdown/snippets/rust_test_format_support.md b/docs/markdown/snippets/rust_test_format_support.md new file mode 100644 index 0000000..69e9aa1 --- /dev/null +++ b/docs/markdown/snippets/rust_test_format_support.md @@ -0,0 +1,4 @@ +## Meson test() now accepts `protocol : 'rust'` + +This allows native rust tests to be run and parsed by meson, simply set the +protocol to `rust` and meson takes care of the rest. diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index ec3aca6..9bb870c 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -46,6 +46,7 @@ class TestProtocol(enum.Enum): EXITCODE = 0 TAP = 1 GTEST = 2 + RUST = 3 @classmethod def from_str(cls, string: str) -> 'TestProtocol': @@ -55,6 +56,8 @@ class TestProtocol(enum.Enum): return cls.TAP elif string == 'gtest': return cls.GTEST + elif string == 'rust': + return cls.RUST raise MesonException('unknown test format {}'.format(string)) def __str__(self) -> str: @@ -62,6 +65,8 @@ class TestProtocol(enum.Enum): return 'exitcode' elif self is self.GTEST: return 'gtest' + elif self is self.RUST: + return 'rust' return 'tap' diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index c20c205..0c6bb99 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -4159,8 +4159,8 @@ This will become a hard error in the future.''' % kwargs['input'], location=self if not isinstance(timeout, int): raise InterpreterException('Timeout must be an integer.') protocol = kwargs.get('protocol', 'exitcode') - if protocol not in {'exitcode', 'tap', 'gtest'}: - raise InterpreterException('Protocol must be "exitcode", "tap", or "gtest".') + if protocol not in {'exitcode', 'tap', 'gtest', 'rust'}: + raise InterpreterException('Protocol must be one of "exitcode", "tap", "gtest", or "rust".') suite = [] prj = self.subproject if self.is_subproject() else self.build.project_name for s in mesonlib.stringlistify(kwargs.get('suite', '')): diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index c31ad1a..9db271e 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -666,6 +666,28 @@ class JunitBuilder(TestLogger): tree.write(f, encoding='utf-8', xml_declaration=True) +def parse_rust_test(stdout: str) -> T.Dict[str, TestResult]: + """Parse the output of rust tests.""" + res = {} # type; T.Dict[str, TestResult] + + def parse_res(res: str) -> TestResult: + if res == 'ok': + return TestResult.OK + elif res == 'ignored': + return TestResult.SKIP + elif res == 'FAILED': + return TestResult.FAIL + raise MesonException('Unsupported output from rust test: {}'.format(res)) + + for line in stdout.splitlines(): + if line.startswith('test ') and not line.startswith('test result'): + _, name, _, result = line.split(' ') + name = name.replace('::', '.') + res[name] = parse_res(result) + + return res + + class TestRun: TEST_NUM = 0 @@ -749,6 +771,21 @@ class TestRun: self.complete(res, results, returncode, stdo, stde, cmd) + def complete_rust(self, returncode: int, stdo: str, stde: str, cmd: T.List[str]) -> None: + results = parse_rust_test(stdo) + + failed = TestResult.FAIL in results.values() + # Now determine the overall result of the test based on the outcome of the subcases + if all(t is TestResult.SKIP for t in results.values()): + # This includes the case where num_tests is zero + res = TestResult.SKIP + elif self.should_fail: + res = TestResult.EXPECTEDFAIL if failed else TestResult.UNEXPECTEDPASS + else: + res = TestResult.FAIL if failed else TestResult.OK + + self.complete(res, results, returncode, stdo, stde, cmd) + @property def num(self) -> int: if self._num is None: @@ -1069,6 +1106,8 @@ class SingleTestRunner: self.runobj.complete_exitcode(returncode, stdo, stde, cmd) elif self.test.protocol is TestProtocol.GTEST: self.runobj.complete_gtest(returncode, stdo, stde, cmd) + elif self.test.protocol is TestProtocol.RUST: + return self.runobj.complete_rust(returncode, stdo, stde, cmd) else: if self.options.verbose: print(stdo, end='') diff --git a/test cases/rust/9 unit tests/meson.build b/test cases/rust/9 unit tests/meson.build new file mode 100644 index 0000000..44fd6b6 --- /dev/null +++ b/test cases/rust/9 unit tests/meson.build @@ -0,0 +1,32 @@ +project('rust unit tests', 'rust') + +t = executable( + 'rust_test', + ['test.rs'], + rust_args : ['--test'], +) + +test( + 'rust test (should fail)', + t, + protocol : 'rust', + suite : ['foo'], + should_fail : true, +) + +test( + 'rust test (should pass)', + t, + args : ['--skip', 'test_add_intentional_fail'], + protocol : 'rust', + suite : ['foo'], +) + + +test( + 'rust test (should skip)', + t, + args : ['--skip', 'test_add'], + protocol : 'rust', + suite : ['foo'], +) diff --git a/test cases/rust/9 unit tests/test.rs b/test cases/rust/9 unit tests/test.rs new file mode 100644 index 0000000..0225be5 --- /dev/null +++ b/test cases/rust/9 unit tests/test.rs @@ -0,0 +1,24 @@ +pub fn add(a: i32, b: i32) -> i32 { + return a + b; +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_add() { + assert_eq!(add(1, 2), 3); + } + + #[test] + fn test_add_intentional_fail() { + assert_eq!(add(1, 2), 5); + } + + #[test] + #[ignore] + fn test_add_intentional_fail2() { + assert_eq!(add(1, 7), 5); + } +} -- cgit v1.1 From 8d19beccb23819719b15ec4ff0c643b3486ac899 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 21 Oct 2020 16:00:33 -0700 Subject: interpreter: allow modules to create tests --- mesonbuild/interpreter.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 0c6bb99..7b8a7f8 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -2593,6 +2593,8 @@ class Interpreter(InterpreterBase): self.process_new_values(v.sources[0]) elif isinstance(v, InstallDir): self.build.install_dirs.append(v) + elif isinstance(v, Test): + self.build.tests.append(v) elif hasattr(v, 'held_object'): pass elif isinstance(v, (int, str, bool, Disabler)): -- cgit v1.1 From b2c2549b93a8001d8a6d9d6da1ce756645e59160 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 21 Oct 2020 16:01:00 -0700 Subject: interpreter: split code that creates a Test instance For modules to make use of, as they're not allowed to modify the Build instance directly. --- mesonbuild/interpreter.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mesonbuild/interpreter.py b/mesonbuild/interpreter.py index 7b8a7f8..6e04235 100644 --- a/mesonbuild/interpreter.py +++ b/mesonbuild/interpreter.py @@ -4122,7 +4122,7 @@ This will become a hard error in the future.''' % kwargs['input'], location=self env = env.held_object return env - def add_test(self, node, args, kwargs, is_base_test): + def make_test(self, node: mparser.BaseNode, args: T.List, kwargs: T.Dict[str, T.Any]): if len(args) != 2: raise InterpreterException('test expects 2 arguments, {} given'.format(len(args))) name = args[0] @@ -4176,14 +4176,17 @@ This will become a hard error in the future.''' % kwargs['input'], location=self priority = kwargs.get('priority', 0) if not isinstance(priority, int): raise InterpreterException('Keyword argument priority must be an integer.') - t = Test(name, prj, suite, exe.held_object, depends, par, cmd_args, - env, should_fail, timeout, workdir, protocol, priority) + return Test(name, prj, suite, exe.held_object, depends, par, cmd_args, + env, should_fail, timeout, workdir, protocol, priority) + + def add_test(self, node: mparser.BaseNode, args: T.List, kwargs: T.Dict[str, T.Any], is_base_test: bool): + t = self.make_test(node, args, kwargs) if is_base_test: self.build.tests.append(t) - mlog.debug('Adding test', mlog.bold(name, True)) + mlog.debug('Adding test', mlog.bold(t.name, True)) else: self.build.benchmarks.append(t) - mlog.debug('Adding benchmark', mlog.bold(name, True)) + mlog.debug('Adding benchmark', mlog.bold(t.name, True)) @FeatureNewKwargs('install_headers', '0.47.0', ['install_mode']) @permittedKwargs(permitted_kwargs['install_headers']) -- cgit v1.1 From 3d80a88bd3c3dc8f9e20bbda485b0b436fd79fb3 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Wed, 21 Oct 2020 16:07:31 -0700 Subject: modules: Add an unstable-rust module Like other language specific modules this module is module for holding rust specific helpers. This commit adds a test() function, which simplifies using rust's internal unittest mechanism. Rust tests are generally placed in the same code files as they are testing, in contrast to languages like C/C++ and python which generally place the tests in separate translation units. For meson this is somewhat problematic from a repetition point of view, as the only changes are generally adding --test, and possibly some dependencies. The rustmod.test() method provides a mechanism to remove the repatition: it takes a rust target, copies it, and then addes the `--test` option, then creates a Test() target with the `rust` protocol. You can pass additional dependencies via the `dependencies` keyword. This all makes for a nice, DRY, test definition. --- docs/markdown/Rust-module.md | 35 +++++++ docs/markdown/_Sidebar.md | 1 + docs/markdown/snippets/unstable-rust-module.md | 4 + docs/sitemap.txt | 11 +- docs/theme/extra/templates/navbar_links.html | 45 ++++---- mesonbuild/modules/unstable_rust.py | 137 +++++++++++++++++++++++++ run_mypy.py | 1 + test cases/rust/9 unit tests/meson.build | 11 ++ test cases/rust/9 unit tests/test2.rs | 11 ++ 9 files changed, 229 insertions(+), 27 deletions(-) create mode 100644 docs/markdown/Rust-module.md create mode 100644 docs/markdown/snippets/unstable-rust-module.md create mode 100644 mesonbuild/modules/unstable_rust.py create mode 100644 test cases/rust/9 unit tests/test2.rs diff --git a/docs/markdown/Rust-module.md b/docs/markdown/Rust-module.md new file mode 100644 index 0000000..0fdba94 --- /dev/null +++ b/docs/markdown/Rust-module.md @@ -0,0 +1,35 @@ +--- +short-description: Rust language integration module +authors: + - name: Dylan Baker + email: dylan@pnwbakers.com + years: [2020] +... + +# Unstable Rust module + +*(new in 0.57.0)* + +**Note** Unstable modules make no backwards compatible API guarantees. + +The rust module provides helper to integrate rust code into meson. The goal +is to make using rust in meson more pleasant, while still remaining mesonic, +this means that it attempts to make rust work more like meson, rather than +meson work more like rust. + +## Functions + +### test(name: string, target: library | executable, dependencies: []Dependency) + +This function creates a new rust unittest target from an existing rust based +target, which may be a library or executable. It does this by copying the +sources and arguments passed to the original target and adding the `--test` +argument to the compilation, then creates a new test target which calls that +executable, using the rust test protocol. + +This accepts all of the keyword arguments as the +[`test`](Reference-manual.md#test) function except `protocol`, it will set +that automatically. + +Additional, test only dependencies may be passed via the dependencies +argument. diff --git a/docs/markdown/_Sidebar.md b/docs/markdown/_Sidebar.md index 2637d68..0ca1762 100644 --- a/docs/markdown/_Sidebar.md +++ b/docs/markdown/_Sidebar.md @@ -12,3 +12,4 @@ * [gnome](Gnome-module.md) * [i18n](i18n-module.md) * [pkgconfig](Pkgconfig-module.md) +* [rust](Rust-module.md) diff --git a/docs/markdown/snippets/unstable-rust-module.md b/docs/markdown/snippets/unstable-rust-module.md new file mode 100644 index 0000000..15a7ecb --- /dev/null +++ b/docs/markdown/snippets/unstable-rust-module.md @@ -0,0 +1,4 @@ +## Untable Rust module + +A new unstable module has been added to make using rust with meson easier. +Currently it adds a single function to ease defining rust tests. diff --git a/docs/sitemap.txt b/docs/sitemap.txt index 4cae9fe..3164440 100644 --- a/docs/sitemap.txt +++ b/docs/sitemap.txt @@ -34,24 +34,25 @@ index.md Disabler.md Modules.md CMake-module.md + Cuda-module.md Dlang-module.md + External-Project-module.md Fs-module.md Gnome-module.md Hotdoc-module.md - i18n-module.md Icestorm-module.md + Keyval-module.md Pkgconfig-module.md - Python-module.md Python-3-module.md + Python-module.md Qt4-module.md Qt5-module.md RPM-module.md + Rust-module.md Simd-module.md SourceSet-module.md Windows-module.md - Cuda-module.md - Keyval-module.md - External-Project-module.md + i18n-module.md Java.md Vala.md D.md diff --git a/docs/theme/extra/templates/navbar_links.html b/docs/theme/extra/templates/navbar_links.html index 832bd2c..8df082f 100644 --- a/docs/theme/extra/templates/navbar_links.html +++ b/docs/theme/extra/templates/navbar_links.html @@ -5,28 +5,29 @@ Modules \ diff --git a/mesonbuild/modules/unstable_rust.py b/mesonbuild/modules/unstable_rust.py new file mode 100644 index 0000000..72b5217 --- /dev/null +++ b/mesonbuild/modules/unstable_rust.py @@ -0,0 +1,137 @@ +# Copyright © 2020 Intel Corporation + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at + +# http://www.apache.org/licenses/LICENSE-2.0 + +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import typing as T + +from . import ExtensionModule, ModuleReturnValue +from .. import mlog +from ..build import BuildTarget, Executable, InvalidArguments +from ..dependencies import Dependency, ExternalLibrary +from ..interpreter import ExecutableHolder, permitted_kwargs +from ..interpreterbase import InterpreterException, permittedKwargs, FeatureNew +from ..mesonlib import stringlistify, unholder, listify + +if T.TYPE_CHECKING: + from ..interpreter import ModuleState, Interpreter + + +class RustModule(ExtensionModule): + + """A module that holds helper functions for rust.""" + + @FeatureNew('rust module', '0.57.0') + def __init__(self, interpreter: 'Interpreter') -> None: + super().__init__(interpreter) + + @permittedKwargs(permitted_kwargs['test'] | {'dependencies'} ^ {'protocol'}) + def test(self, state: 'ModuleState', args: T.List, kwargs: T.Dict[str, T.Any]) -> ModuleReturnValue: + """Generate a rust test target from a given rust target. + + Rust puts it's unitests inside it's main source files, unlike most + languages that put them in external files. This means that normally + you have to define two seperate targets with basically the same + arguments to get tests: + + ```meson + rust_lib_sources = [...] + rust_lib = static_library( + 'rust_lib', + rust_lib_sources, + ) + + rust_lib_test = executable( + 'rust_lib_test', + rust_lib_sources, + rust_args : ['--test'], + ) + + test( + 'rust_lib_test', + rust_lib_test, + protocol : 'rust', + ) + ``` + + This is all fine, but not very DRY. This method makes it much easier + to define rust tests: + + ```meson + rust = import('unstable-rust') + + rust_lib = static_library( + 'rust_lib', + [sources], + ) + + rust.test('rust_lib_test', rust_lib) + ``` + """ + if len(args) != 2: + raise InterpreterException('rustmod.test() takes exactly 2 positional arguments') + name: str = args[0] + if not isinstance(name, str): + raise InterpreterException('First positional argument to rustmod.test() must be a string') + base_target: BuildTarget = unholder(args[1]) + if not isinstance(base_target, BuildTarget): + raise InterpreterException('Second positional argument to rustmod.test() must be a library or executable') + if not base_target.get_using_rustc(): + raise InterpreterException('Second positional argument to rustmod.test() must be a rust based target') + extra_args = stringlistify(kwargs.get('args', [])) + + # Delete any arguments we don't want passed + if '--test' in extra_args: + mlog.warning('Do not add --test to rustmod.test arguments') + extra_args.remove('--test') + if '--format' in extra_args: + mlog.warning('Do not add --format to rustmod.test arguments') + i = extra_args.index('--format') + # Also delete the argument to --format + del extra_args[i + 1] + del extra_args[i] + for i, a in enumerate(extra_args): + if a.startswith('--format='): + del extra_args[i] + break + + dependencies = unholder(listify(kwargs.get('dependencies', []))) + for d in dependencies: + if not isinstance(d, (Dependency, ExternalLibrary)): + raise InvalidArguments('dependencies must be a dependency or external library') + + kwargs['args'] = extra_args + ['--test', '--format', 'pretty'] + kwargs['protocol'] = 'rust' + + new_target_kwargs = base_target.kwargs.copy() + # Don't mutate the shallow copied list, instead replace it with a new + # one + new_target_kwargs['rust_args'] = new_target_kwargs.get('rust_args', []) + ['--test'] + new_target_kwargs['install'] = False + new_target_kwargs['dependencies'] = new_target_kwargs.get('dependencies', []) + dependencies + + new_target = Executable( + name, base_target.subdir, state.subproject, + base_target.for_machine, base_target.sources, + base_target.objects, base_target.environment, + new_target_kwargs + ) + + e = ExecutableHolder(new_target, self.interpreter) + test = self.interpreter.make_test( + self.interpreter.current_node, [name, e], kwargs) + + return ModuleReturnValue([], [e, test]) + + +def initialize(*args: T.List, **kwargs: T.Dict) -> RustModule: + return RustModule(*args, **kwargs) # type: ignore diff --git a/run_mypy.py b/run_mypy.py index 01fa9ff..888403c 100755 --- a/run_mypy.py +++ b/run_mypy.py @@ -29,6 +29,7 @@ modules = [ 'mesonbuild/mintro.py', 'mesonbuild/mlog.py', 'mesonbuild/modules/fs.py', + 'mesonbuild/modules/unstable_rust.py', 'mesonbuild/mparser.py', 'mesonbuild/msetup.py', 'mesonbuild/mtest.py', diff --git a/test cases/rust/9 unit tests/meson.build b/test cases/rust/9 unit tests/meson.build index 44fd6b6..b649abb 100644 --- a/test cases/rust/9 unit tests/meson.build +++ b/test cases/rust/9 unit tests/meson.build @@ -30,3 +30,14 @@ test( protocol : 'rust', suite : ['foo'], ) + +exe = executable('rust_exe', ['test2.rs', 'test.rs']) + +rust = import('unstable-rust') +rust.test('rust_test_from_exe', exe, should_fail : true) + +lib = static_library('rust_static', ['test.rs']) +rust.test('rust_test_from_static', lib, args: ['--skip', 'test_add_intentional_fail']) + +lib = shared_library('rust_shared', ['test.rs']) +rust.test('rust_test_from_shared', lib, args: ['--skip', 'test_add_intentional_fail']) diff --git a/test cases/rust/9 unit tests/test2.rs b/test cases/rust/9 unit tests/test2.rs new file mode 100644 index 0000000..9623c7c --- /dev/null +++ b/test cases/rust/9 unit tests/test2.rs @@ -0,0 +1,11 @@ +mod test; +use std::env; + +fn main() { + let args: Vec = env::args().collect(); + let first = args[1].parse::().expect("Invliad value for first argument."); + let second = args[2].parse::().expect("Invliad value for second argument."); + + let new = test::add(first, second); + println!("New value: {}", new); +} -- cgit v1.1 From 827fa95de1c619b51457c5a04297b6ce9adc2177 Mon Sep 17 00:00:00 2001 From: Dylan Baker Date: Mon, 9 Nov 2020 10:34:06 -0800 Subject: update codeowners with the rust module --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) diff --git a/CODEOWNERS b/CODEOWNERS index 8ac91ad..b051ece 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -2,6 +2,7 @@ /mesonbuild/modules/pkgconfig.py @xclaesse /mesonbuild/modules/cmake.py @mensinda /mesonbuild/modules/unstable_external_project.py @xclaesse +/mesonbuild/modules/unstable_rust.py @dcbaker /mesonbuild/ast/ @mensinda /mesonbuild/cmake/ @mensinda /mesonbuild/compilers/ @dcbaker -- cgit v1.1