Add support for writing Gtest tests in Rust
Introduce a #[gtest(TestSuite, TestName)] macro, which is a Rust
equivalent to the C++ TEST(TestSuite, TestName) macro. The test's name
will be TestSuite.TestName, just as with the C++ macro.
The #[gtest] macro can be placed on any Rust fn, which will make it run
as a test inside Gtest. It should be used in binary test targets that
call RUN_ALL_TESTS() from C++ code, which is the Gtest main entrypoint.
Since Gtest tests all run on the main thread, Rust tests can not report
failures by causing panics. So we also introduce expect_eq!() and
friends as macros that print details about failures and report the
failure, along with the file path and line number, to Gtest.
Example output with a failure:
[==========] Running 6 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 6 tests from RustValues
[ RUN ] RustValues.SetList
[ OK ] RustValues.SetList (0 ms)
[ RUN ] RustValues.ReuseSlot
base/values_unittest.rs:91: Failure
Failed
Expected: 1 + 1 == 3
Actual: 2 vs 3
Stack trace:
#0 0x56325659ea43 rust_gtest_interop::rust_gtest_add_failure_at() [../../testing/rust_gtest_interop/rust_gtest_interop.cc:39:3]
#1 0x56325659f206 rust_gtest_interop_rs::__private::add_failure_at::h81802d5132f81496 [../../testing/rust_gtest_interop/rust_gtest_interop.rs:42:23]
#2 0x56325659dee0 run_test_testing_rust_gtest_interop_gtest_attribute_rs_RustValues_ReuseSlot_test_reuse_slot [../../base/values_unittest.rs:91:5]
[ FAILED ] RustValues.ReuseSlot (24 ms)
[ RUN ] RustValues.StartsNone
[ OK ] RustValues.StartsNone (0 ms)
[ RUN ] RustValues.SetDict
[ OK ] RustValues.SetDict (0 ms)
[ RUN ] RustValues.SetSimpleOptionalValues
[ OK ] RustValues.SetSimpleOptionalValues (0 ms)
[ RUN ] RustValues.Dealloc
[ OK ] RustValues.Dealloc (0 ms)
[----------] 6 tests from RustValues (25 ms total)
[----------] Global test environment tear-down
[==========] 6 tests from 1 test suite ran. (25 ms total)
Tests can return either () or a Result<(), E>. If they return a
different type, they will fail.
A Result<i32> return type:
#[gtest(Test, WithNonVoidResultType)]
fn test() -> std::io::Result<i32> {
expect_true!(true);
Ok(1)
}
[ RUN ] Test.WithNonVoidResultType
testing/rust_gtest_interop/rust_gtest_interop_unittest.rs:62: Failure
Failed
Invalid test return type. Must be `()` or `std::result::Result`. If you have a different Result type, try `.into()`.
[ FAILED ] Test.WithNonVoidResultType (19 ms)
A bool return type:
#[gtest(Test, WithNonVoidType)]
fn test() -> bool {
expect_true!(true);
true
}
[ RUN ] Test.WithNonVoidType
testing/rust_gtest_interop/rust_gtest_interop_unittest.rs:68: Failure
Failed
Invalid test return type. Must be `()` or `std::result::Result`. If you have a different Result type, try `.into()`.
[ FAILED ] Test.WithNonVoidType (10 ms)
If the test returns a Result and it evaluates to Err, the test will also
fail:
#[gtest(Test, WithError)]
fn test() -> std::result::Result<(), Box<dyn std::error::Error>> {
expect_true!(true);
Err("uhoh".into())
}
[ RUN ] Test.WithError
testing/rust_gtest_interop/rust_gtest_interop_unittest.rs:74: Failure
Failed
Test returned Err: uhoh
[ FAILED ] Test.WithError (8 ms)
Bug: 1293979
Change-Id: I496626719d80c251c7fe2ad4755b5367f720772e
Cq-Include-Trybots: luci.chromium.try:linux-rust-x64-rel,android-rust-arm-rel
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3449134
Reviewed-by: John Chen <[email protected]>
Reviewed-by: Nico Weber <[email protected]>
Reviewed-by: Ćukasz Anforowicz <[email protected]>
Commit-Queue: danakj <[email protected]>
Cr-Commit-Position: refs/heads/main@{#974627}
diff --git a/BUILD.gn b/BUILD.gn
index 20fc4f1..054be18 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -865,6 +865,9 @@
"//mojo/public/rust:tests",
]
}
+ if (enable_rust) {
+ deps += [ "//testing/rust_gtest_interop:rust_gtest_interop_unittests" ]
+ }
}
group("rust_build_tests") {