aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Pakkanen <jpakkane@gmail.com>2021-01-06 00:05:48 +0000
committerGitHub <noreply@github.com>2021-01-06 00:05:48 +0000
commitc9d9dacdbc9fad31f00f871b24ec6b99a611ff5e (patch)
tree07ea0773d50c41e3e87081e1be6095a7788da9df
parentf9dd75f213b1c3a7ab397133b6a157ddba511d90 (diff)
parent827fa95de1c619b51457c5a04297b6ce9adc2177 (diff)
downloadmeson-c9d9dacdbc9fad31f00f871b24ec6b99a611ff5e.zip
meson-c9d9dacdbc9fad31f00f871b24ec6b99a611ff5e.tar.gz
meson-c9d9dacdbc9fad31f00f871b24ec6b99a611ff5e.tar.bz2
Merge pull request #7860 from dcbaker/wip/2020-10/rust-module
Add a rust module
-rw-r--r--CODEOWNERS1
-rw-r--r--docs/markdown/Reference-manual.md1
-rw-r--r--docs/markdown/Rust-module.md35
-rw-r--r--docs/markdown/_Sidebar.md1
-rw-r--r--docs/markdown/snippets/rust_test_format_support.md4
-rw-r--r--docs/markdown/snippets/unstable-rust-module.md4
-rw-r--r--docs/sitemap.txt11
-rw-r--r--docs/theme/extra/templates/navbar_links.html45
-rw-r--r--mesonbuild/backend/backends.py5
-rw-r--r--mesonbuild/interpreter.py19
-rw-r--r--mesonbuild/modules/unstable_rust.py137
-rw-r--r--mesonbuild/mtest.py73
-rwxr-xr-xrun_mypy.py1
-rw-r--r--test cases/rust/9 unit tests/meson.build43
-rw-r--r--test cases/rust/9 unit tests/test.rs24
-rw-r--r--test cases/rust/9 unit tests/test2.rs11
16 files changed, 364 insertions, 51 deletions
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
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/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/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/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 <span class="caret"></span>
</a>
<ul class="dropdown-menu" id="modules-menu">
- @for tup in ( \
- ("CMake-module.html","CMake"), \
- ("Cuda-module.html","CUDA"), \
- ("Dlang-module.html","Dlang"), \
- ("Fs-module.html","Filesystem"), \
- ("Gnome-module.html","GNOME"), \
- ("Hotdoc-module.html","Hotdoc"), \
- ("i18n-module.html","i18n"), \
- ("Icestorm-module.html","Icestorm"), \
- ("Keyval-module.html","Keyval"), \
- ("Pkgconfig-module.html","Pkgconfig"), \
- ("Python-module.html","Python"), \
- ("Python-3-module.html","Python 3"), \
- ("Qt4-module.html","Qt4"), \
- ("Qt5-module.html","Qt5"), \
- ("RPM-module.html","RPM"), \
- ("SourceSet-module.html","SourceSet"), \
- ("Windows-module.html","Windows")):
- <li>
- <a href="@tup[0]">@tup[1]</a>
- </li>
- @end
+ @for tup in [ \
+ ("CMake-module.html","CMake"), \
+ ("Cuda-module.html","CUDA"), \
+ ("Dlang-module.html","Dlang"), \
+ ("Fs-module.html","Filesystem"), \
+ ("Gnome-module.html","GNOME"), \
+ ("Hotdoc-module.html","Hotdoc"), \
+ ("Icestorm-module.html","Icestorm"), \
+ ("Keyval-module.html","Keyval"), \
+ ("Pkgconfig-module.html","Pkgconfig"), \
+ ("Python-3-module.html","Python 3"), \
+ ("Python-module.html","Python"), \
+ ("Qt4-module.html","Qt4"), \
+ ("Qt5-module.html","Qt5"), \
+ ("RPM-module.html","RPM"), \
+ ("Rust-module.html","Rust"), \
+ ("SourceSet-module.html","SourceSet"), \
+ ("Windows-module.html","Windows"), \
+ ("i18n-module.html","i18n")]:
+ <li>
+ <a href="@tup[0]">@tup[1]</a>
+ </li>
+ @end
</ul>
</li>
\
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..6e04235 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)):
@@ -4120,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]
@@ -4159,8 +4161,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', '')):
@@ -4174,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'])
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/mesonbuild/mtest.py b/mesonbuild/mtest.py
index 7e3ebea..9db271e 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:
@@ -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
@@ -675,7 +697,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 +735,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 +761,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:
@@ -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:
@@ -756,13 +793,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 +943,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,12 +1100,14 @@ 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)
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/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
new file mode 100644
index 0000000..b649abb
--- /dev/null
+++ b/test cases/rust/9 unit tests/meson.build
@@ -0,0 +1,43 @@
+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'],
+)
+
+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/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);
+ }
+}
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<String> = env::args().collect();
+ let first = args[1].parse::<i32>().expect("Invliad value for first argument.");
+ let second = args[2].parse::<i32>().expect("Invliad value for second argument.");
+
+ let new = test::add(first, second);
+ println!("New value: {}", new);
+}