1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef TESTING_RUST_GTEST_INTEROP_RUST_GTEST_INTEROP_H_
6 #define TESTING_RUST_GTEST_INTEROP_RUST_GTEST_INTEROP_H_
7
8 #include <stdint.h>
9 #include <type_traits>
10
11 namespace testing {
12 class Test;
13 }
14
15 // Macro to make an extern "C" function which acts as a Gtest factory for a
16 // testing::Test subclass T. Invoke this macro once for each subclass of
17 // testing::Test that should be used as a TestSuite class from a Rust test,
18 // which can be specified with `#[gtest_suite(T)]`.
19 //
20 // The function generated by the macro is used by the
21 // rust_gtest_interop::TestSuite trait implementation to connect a Rust test to
22 // the C++ class.
23 #define RUST_GTEST_TEST_SUITE_FACTORY(T) \
24 extern "C" T* RustGtestFactory_##T(void (*f)(T*)) { \
25 return rust_gtest_interop::rust_gtest_factory_for_subclass<T>(f); \
26 }
27
28 namespace rust_gtest_interop {
29
30 // A simple C++ test fixture used for Rust unit tests. It provides nothing
31 // except the test body which calls the Rust function. The Subclass must be
32 // `testing::Test`, or a subclass thereof.
33 template <class Subclass>
34 class RustTest : public Subclass {
35 public:
RustTest(void (& test_fn)(Subclass *))36 explicit RustTest(void (&test_fn)(Subclass*)) : test_fn_(test_fn) {
37 static_assert(std::is_convertible_v<Subclass*, testing::Test*>,
38 "RustTest's Subclass parameter must be a testing::Test or a "
39 "subclass of it");
40 }
TestBody()41 void TestBody() override { test_fn_(this); }
42
43 private:
44 // Not a `raw_ref<T>` because this is a function reference.
45 void (&test_fn_)(Subclass*);
46 };
47
48 // The TestSuite factory function which will construct a testing::Test subclass
49 // that runs the given function pointer as the test body. The factory function
50 // exists to allow choosing different subclasses of testing::Test.
51 using GtestFactoryFunction = testing::Test* (*)(void (*body)(testing::Test*));
52
53 // Templated implementation of GtestFactoryFunction. Rust can't use templated
54 // methods currently, so other fully-typed functions need to exist which can
55 // make use of this function, via the RUST_GTEST_TEST_SUITE_FACTORY() macro.
56 template <class Subclass>
rust_gtest_factory_for_subclass(void (* body)(Subclass *))57 Subclass* rust_gtest_factory_for_subclass(void (*body)(Subclass*)) {
58 return new RustTest<Subclass>(*body);
59 }
60
61 // Returns a factory that will run the test function. Used for any Rust tests
62 // that don't need a specific C++ testing::Test subclass.
63 extern "C" testing::Test* rust_gtest_default_factory(
64 void (*body)(testing::Test*));
65
66 // Register a test to be run via GTest. This must be called before main(), as
67 // there's no calls from C++ into Rust to collect tests. Any function given to
68 // this function will be included in the set of tests run by the RUN_ALL_TESTS()
69 // invocation.
70 //
71 // This function is meant to be called from Rust, for any test functions
72 // decorated by a #[gtest(Suite, Test)] macro, which is provided to Rust by this
73 // same GN target.
74 //
75 // The `test_function` signature here says it receives a `Test*`, though in fact
76 // the function would be specialized to receive its precise subclass of `Test*`.
77 // It is required that the type returned by the GtestFactoryFunction and the
78 // type received by the `test_function` are the same type. The type is downcast
79 // in Rust when the test function body is run.
80 //
81 // SAFETY: This function makes copies of the strings so the pointers do not need
82 // to outlive the function call.
83 extern "C" void rust_gtest_add_test(GtestFactoryFunction gtest_factory,
84 void (*test_function)(testing::Test*),
85 const char* test_suite_name,
86 const char* test_name,
87 const char* file,
88 int32_t line);
89
90 // Report a test failure at a given file and line tuple, with a provided
91 // message.
92 //
93 // This function is meant to be called from Rust tests, via expect_eq!() and
94 // similar macros. It's present in a header for generating bindings from Rust.
95 //
96 // We use `unsigned char` and `int32_t` because CXX does not support
97 // std::os::raw::c_char or std::os::raw::c_int. See
98 // https://github.com/dtolnay/cxx/issues/1015.
99 //
100 // SAFETY: This function makes copies of the strings so the pointers do not need
101 // to outlive the function call.
102 extern "C" void rust_gtest_add_failure_at(const char* file,
103 int32_t line,
104 const char* message);
105
106 } // namespace rust_gtest_interop
107
108 #endif // TESTING_RUST_GTEST_INTEROP_RUST_GTEST_INTEROP_H_
109