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}
15 files changed
tree: 6587353b464bedb6f3995693547129239d885b38
  1. android_webview/
  2. apps/
  3. ash/
  4. base/
  5. build/
  6. build_overrides/
  7. buildtools/
  8. cc/
  9. chrome/
  10. chromecast/
  11. chromeos/
  12. cloud_print/
  13. codelabs/
  14. components/
  15. content/
  16. courgette/
  17. crypto/
  18. dbus/
  19. device/
  20. docs/
  21. extensions/
  22. fuchsia/
  23. gin/
  24. google_apis/
  25. google_update/
  26. gpu/
  27. headless/
  28. infra/
  29. ios/
  30. ipc/
  31. media/
  32. mojo/
  33. native_client_sdk/
  34. net/
  35. pdf/
  36. ppapi/
  37. printing/
  38. remoting/
  39. rlz/
  40. sandbox/
  41. services/
  42. skia/
  43. sql/
  44. storage/
  45. styleguide/
  46. testing/
  47. third_party/
  48. tools/
  49. ui/
  50. url/
  51. weblayer/
  52. .clang-format
  53. .clang-tidy
  54. .eslintrc.js
  55. .git-blame-ignore-revs
  56. .gitattributes
  57. .gitignore
  58. .gn
  59. .mailmap
  60. .rustfmt.toml
  61. .vpython
  62. .vpython3
  63. .yapfignore
  64. AUTHORS
  65. BUILD.gn
  66. CODE_OF_CONDUCT.md
  67. codereview.settings
  68. DEPS
  69. DIR_METADATA
  70. ENG_REVIEW_OWNERS
  71. LICENSE
  72. LICENSE.chromium_os
  73. OWNERS
  74. PRESUBMIT.py
  75. PRESUBMIT_test.py
  76. PRESUBMIT_test_mocks.py
  77. README.md
  78. WATCHLISTS
README.md

Logo Chromium

Chromium is an open-source browser project that aims to build a safer, faster, and more stable way for all users to experience the web.

The project's web site is https://www.chromium.org.

To check out the source code locally, don't use git clone! Instead, follow the instructions on how to get the code.

Documentation in the source is rooted in docs/README.md.

Learn how to Get Around the Chromium Source Code Directory Structure .

For historical reasons, there are some small top level directories. Now the guidance is that new top level directories are for product (e.g. Chrome, Android WebView, Ash). Even if these products have multiple executables, the code should be in subdirectories of the product.

If you found a bug, please file it at https://crbug.com/new.