README.md
1# Rust integration into C++ Gtest targets.
2
3This directory contains the tools for writing gtest-based tests in Rust and
4integrating them into Chromium's C++ gtest binaries. The tools are all
5accessible through the `rust_gtest_interop` target which is automatically
6included in test targets that depend on `//testing/gtest`.
7
8## To add rust unittests to a C++ Gtest target
9
10A typical Gtest target is defined in a BUILD.gn file, with something like this:
11
12`BUILD.gn`:
13```gn
14test("some_unittests") {
15 sources = [
16 "a_cpp_file.cc",
17 "another_cpp_file.cc",
18 ]
19 deps = [
20 "//testing/gtest",
21 ]
22}
23```
24
25To add a Rust file to the test suite, simply add it to the `sources`. Unlike
26other Rust crates, the `crate_root` is not specified, since it is generated from
27the sources list.
28
29`BUILD.gn`:
30```gn
31test("some_unittests") {
32 sources = [
33 "a_cpp_file.cc",
34 "another_cpp_file.cc",
35 "a_rust_file.rs",
36 ]
37 deps = [
38 "//testing/gtest",
39 ]
40}
41```
42
43Transitively depending on a `rust_static_library` will include its tests
44(similarly to tests authored in C++), although in that case the
45`rust_static_library` should explicitly declare its dependency on
46`//testing/rust_gtest_interop` and set `testonly` as well as
47`is_gtest_unittests`.
48
49```gn
50rust_static_library("my_rust_lib_unittests") {
51 testonly = true
52 is_gtest_unittests = true
53 crate_root = "my_rust_lib_unittest.rs"
54 sources = [ "my_rust_lib_unittest.rs" ]
55 deps = [
56 ":my_rust_lib",
57 "//testing/rust_gtest_interop",
58 ]
59}
60
61test("some_unittests") {
62 ...
63 deps += [ ":my_rust_lib_unittests" ]
64}
65```
66
67## To write a Gtest unit test in Rust
68
69To write a unit test, you simply write a function an decorate it with the
70`#[gtest]` macro. The macro takes 2 arguments, which are the test suite name and
71the test name, just like the C++ `TEST()` macro.
72
73The `#[gtest]` macro is provided by the `rust_gtest_interop` crate, and is
74exported in the `prelude` module. Typically a unit test file would start with
75`use rust_gtest_interop::prelude::*;` which includes all of the available
76gtest macros. This is similar to writing `#include
77"testing/gtest/include/gtest/gtest.h"` in C++.
78
79A Rust test:
80```rs
81use rust_gtest_interop::prelude::*; // Provides all the gtest macros.
82
83#[gtest(MyTestSuite, MyTestOfThing)]
84fn test() {
85 ...
86}
87```
88
89A C++ test:
90```cpp
91#include "testing/gtest/include/gtest/gtest.h" // Provides all the gtest macros.
92
93TEST(MyTestSuite, MyTestOfThing) {
94 ...
95}
96```
97
98### Expectations
99
100We have access to many of the same EXPECT macros in Rust that are familiar to
101C++ Gtest users, though they are used with Rust's macro syntax.
102
103The macros currently available are:
104```rs
105expect_true!(is_friday());
106expect_false!(is_saturday());
107
108expect_eq!(2, 1 + 1); // A == B
109expect_ne!(3, 1 + 2); // A != B
110
111expect_lt!(1 * 1, 1 * 2); // A < B
112expect_gt!(4 * 1, 1 * 2); // A > B
113expect_le!(2 * 1, 1 * 2); // A <= B
114expect_ge!(3 * 1, 2 * 3); // A >= B
115```
116
117### Returning a Result
118
119A C++ test always returns void and Rust tests usually do as well. But if your
120test calls a function that returns `Result`, it is convenient to make use of the
121[`?` operator](https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator)
122instead of checking the Result value explicitly. Thus a test can either return:
123
1241. `()` aka void.
1251. `std::result::Result<(), E>` for any `E` that can be converted to a
126 `std::error::Error`. (Or in Rust parlance, for any `E` for which there is
127 `Into<std::error::Error>`). Common error types are `std::io::Error` or
128 `String`.
129
130If the test with a `std::result::Result` return type returns `Result::Err`, the
131test will fail and display the error.
132
133In this example, the test will fail if it can not read from `file.txt`, or if it
134does not contain `"hello world"`:
135```rs
136#[gtest(TestingIO, ReadFile)]
137fn test() -> std::io::Result {
138 let s = std::fs::read_to_string("file.txt")?;
139 expect_eq!(s, "hello world");
140 Ok(())
141}
142```
143
144### Shared helper utilities
145
146Sometimes tests across different test files want to share helper utilities. Such
147helpers should be placed in a separate GN target, typically named with a
148`_test_support` suffix, such as `starship_test_support` for the
149`starship_unittests`. And would also usually be found in a `test/` subdirectory.
150
151#### Example
152The `starship_unittests` test() target would include any unit test files, such as
153`starship_unittest.rs`. And the `starship_test_support` `rust_static_library()`
154GN target would include the files in the `test/` subdirectory, such as
155`starship_test_helper.rs` and `starship_test_things.rs`.
156```
157src/
158 starship/
159 starship_unittest.rs
160 test/
161 starship_test_helper.rs
162 starship_test_things.rs
163```
164