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 void (&test_fn_)(Subclass*);
45 };
46
47 // The TestSuite factory function which will construct a testing::Test subclass
48 // that runs the given function pointer as the test body. The factory function
49 // exists to allow choosing different subclasses of testing::Test.
50 using GtestFactoryFunction = testing::Test* (*)(void (*body)(testing::Test*));
51
52 // Templated implementation of GtestFactoryFunction. Rust can't use templated
53 // methods currently, so other fully-typed functions need to exist which can
54 // make use of this function, via the RUST_GTEST_TEST_SUITE_FACTORY() macro.
55 template <class Subclass>
rust_gtest_factory_for_subclass(void (* body)(Subclass *))56 Subclass* rust_gtest_factory_for_subclass(void (*body)(Subclass*)) {
57 return new RustTest<Subclass>(*body);
58 }
59
60 // Returns a factory that will run the test function. Used for any Rust tests
61 // that don't need a specific C++ testing::Test subclass.
62 testing::Test* rust_gtest_default_factory(void (*body)());
63
64 // Register a test to be run via GTest. This must be called before main(), as
65 // there's no calls from C++ into Rust to collect tests. Any function given to
66 // this function will be included in the set of tests run by the RUN_ALL_TESTS()
67 // invocation.
68 //
69 // This function is meant to be called from Rust, for any test functions
70 // decorated by a #[gtest(Suite, Test)] macro, which is provided to Rust by this
71 // same GN target.
72 //
73 // The `test_function` signature here says it receives a `Test*`, though in fact
74 // the function would be specialized to receive its precise subclass of `Test*`.
75 // It is required that the type returned by the GtestFactoryFunction and the
76 // type received by the `test_function` are the same type. The type is downcast
77 // in Rust when the test function body is run.
78 //
79 // SAFETY: This function makes copies of the strings so the pointers do not need
80 // to outlive the function call.
81 void rust_gtest_add_test(GtestFactoryFunction gtest_factory,
82 void (*test_function)(testing::Test*),
83 const char* test_suite_name,
84 const char* test_name,
85 const char* file,
86 int32_t line);
87
88 // Report a test failure at a given file and line tuple, with a provided
89 // message.
90 //
91 // This function is meant to be called from Rust tests, via expect_eq!() and
92 // similar macros. It's present in a header for generating bindings from Rust.
93 //
94 // We use `unsigned char` and `int32_t` because CXX does not support
95 // std::os::raw::c_char or std::os::raw::c_int. See
96 // https://github.com/dtolnay/cxx/issues/1015.
97 //
98 // SAFETY: This function makes copies of the strings so the pointers do not need
99 // to outlive the function call.
100 void rust_gtest_add_failure_at(const char* file,
101 int32_t line,
102 const char* message);
103
104 } // namespace rust_gtest_interop
105
106 #endif // TESTING_RUST_GTEST_INTEROP_RUST_GTEST_INTEROP_H_
107