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