userver: userver/utest/test_case_macros.hpp Source File
Loading...
Searching...
No Matches
test_case_macros.hpp
1#pragma once
2
3#include <cstddef>
4#include <functional>
5#include <memory>
6#include <optional>
7#include <string>
8
9#include <gtest/gtest.h>
10
11USERVER_NAMESPACE_BEGIN
12
13namespace utest::impl {
14
15class EnrichedTestBase {
16public:
17 virtual ~EnrichedTestBase() = default;
18 virtual void SetUp() = 0;
19 virtual void TearDown() = 0;
20 virtual void TestBody() = 0;
21
22 std::size_t GetThreadCount() const { return utest_thread_count_; }
23 void SetThreadCount(std::size_t count) { utest_thread_count_ = count; }
24
25private:
26 std::size_t utest_thread_count_ = 1;
27};
28
29enum class DeathTestsEnabled : bool {};
30
31// The work horse of the test suite. Takes a test class returned by 'factory'
32// in a 'std::unique_ptr' and runs the usual test cycle (mimicking gtest),
33// all in a coroutine environment.
34void DoRunTest(std::size_t thread_count, DeathTestsEnabled, std::function<std::unique_ptr<EnrichedTestBase>()> factory);
35
36void RunSetUpTestSuite(void (*set_up_test_suite)());
37void RunTearDownTestSuite(void (*tear_down_test_suite)());
38
39// Inherits from the user's fixture (or '::testing::Test') and provides some
40// niceties to the test body ('GetThreadCount') while making the test methods
41// public ('SetUp', 'TearDown'). The fixture is further inherited from
42// (and "enriched") in inline-created test classes
43// (IMPL_UTEST_HIDE_ENRICHED_FROM_IDE).
44template <typename UserFixture>
45// NOLINTNEXTLINE(fuchsia-multiple-inheritance)
46class EnrichedFixture : public UserFixture, public EnrichedTestBase {
47protected:
48 void SetUp() override { return UserFixture::SetUp(); }
49 void TearDown() override { return UserFixture::TearDown(); }
50
51private:
52 using EnrichedTestBase::SetThreadCount;
53 using EnrichedTestBase::TestBody;
54};
55
56template <typename Base, typename UserFixture>
57class WithStaticMethods : public Base {
58public:
59 static void SetUpTestSuite() { RunSetUpTestSuite(&UserFixture::SetUpTestSuite); }
60
61 static void TearDownTestSuite() { RunTearDownTestSuite(&UserFixture::TearDownTestSuite); }
62};
63
64// 'TestLauncher' and 'TestLauncherParametric' take the enriched user's test
65// class and run it in a coroutine environment via 'DoRunTest'.
66template <typename UserFixture>
67class TestLauncher : public WithStaticMethods<::testing::Test, UserFixture> {
68public:
69 // Called from UTEST_F, TYPED_UTEST and TYPED_UTEST_P macros
70 template <typename EnrichedTest>
71 static void RunTest(std::size_t thread_count, bool death_tests_enabled) {
72 utest::impl::DoRunTest(thread_count, DeathTestsEnabled{death_tests_enabled}, [] {
73 return std::make_unique<EnrichedTest>();
74 });
75 }
76};
77
78template <typename UserFixture>
79class TestLauncherParametric
80 : public WithStaticMethods<::testing::TestWithParam<typename UserFixture::ParamType>, UserFixture> {
81public:
82 // Called from the UTEST_P macro
83 template <typename EnrichedTest>
84 static void RunTest(std::size_t thread_count, bool death_tests_enabled) {
85 using ParamType = typename UserFixture::ParamType;
86 const auto& parameter = ::testing::TestWithParam<ParamType>::GetParam();
87
88 // It seems impossible to seamlessly proxy 'ParamType' from the launcher to
89 // the enriched fixture without using gtest internals.
90 auto factory = std::make_unique<testing::internal::ParameterizedTestFactory<EnrichedTest>>(parameter);
91
92 utest::impl::DoRunTest(thread_count, DeathTestsEnabled{death_tests_enabled}, [&] {
93 return std::unique_ptr<EnrichedTest>{dynamic_cast<EnrichedTest*>(factory->CreateTest())};
94 });
95 }
96};
97
98template <template <typename> typename UserFixture, typename T>
99using TestLauncherTyped = TestLauncher<UserFixture<T>>;
100
101// For TYPED_TEST_SUITE and INSTANTIATE_TYPED_TEST_SUITE_P
102struct DefaultNameGenerator final {
103 template <typename T>
104 static std::string GetName(int i) {
105 return std::to_string(i);
106 }
107};
108
109constexpr bool CheckTestSuiteNameSuffix(std::string_view str, std::string_view suffix) {
110 return str.size() >= suffix.size() && str.substr(str.size() - suffix.size()) == suffix;
111}
112
113} // namespace utest::impl
114
115USERVER_NAMESPACE_END
116
117// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
118#define IMPL_UTEST_NON_PARENTHESIZED(identifier) identifier
119
120// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
121#define IMPL_UTEST_NAMESPACE_NAME(test_suite_name) test_suite_name##_##Utest
122
123// Enriched fixtures are nested into IMPL_UTEST_HIDE_ENRICHED_FROM_IDE namespace
124// so that IDEs don't find them and don't show in "Run a single Test".
125// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
126#define IMPL_UTEST_HIDE_ENRICHED_FROM_IDE(test_suite_name, test_name) test_suite_name##_##test_name##_##Utest
127
128// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
129#define IMPL_UTEST_USER_FIXTURE(test_suite_name) test_suite_name##_##UtestFixture
130
131// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
132#define IMPL_UTEST_MAKE_USER_FIXTURE_ALIAS(test_suite_name)
133 using IMPL_UTEST_USER_FIXTURE(test_suite_name) = IMPL_UTEST_NON_PARENTHESIZED(test_suite_name)
134
135// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
136#define IMPL_UTEST_MAKE_USER_FIXTURE_ALIAS_TYPED(test_suite_name)
137 template <typename UtestTypeParamImpl>
138 using IMPL_UTEST_USER_FIXTURE(test_suite_name) = IMPL_UTEST_NON_PARENTHESIZED(test_suite_name)<UtestTypeParamImpl>
139
140// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
141#define IMPL_UTEST_HIDE_USER_FIXTURE_BY_TEST_LAUNCHER(test_suite_name, test_launcher_template)
142 using IMPL_UTEST_NON_PARENTHESIZED(test_suite_name) =
143 IMPL_UTEST_NON_PARENTHESIZED(test_launcher_template)<IMPL_UTEST_USER_FIXTURE(test_suite_name)>;
144
145// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
146#define IMPL_UTEST_HIDE_USER_FIXTURE_BY_TEST_LAUNCHER_TYPED(test_suite_name)
147 template <typename UtestTypeParamImpl>
148 using IMPL_UTEST_NON_PARENTHESIZED(test_suite_name) = USERVER_NAMESPACE::utest::impl::
149 TestLauncherTyped<IMPL_UTEST_USER_FIXTURE(test_suite_name), UtestTypeParamImpl>;
150
151// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
152#define IMPL_UTEST_TEST(test_suite_name, test_name, thread_count, death_tests_enabled)
153 struct IMPL_UTEST_HIDE_ENRICHED_FROM_IDE(test_suite_name, test_name) final {
154 class EnrichedTest final : public USERVER_NAMESPACE::utest::impl::EnrichedFixture<::testing::Test> {
155 void TestBody() override;
156 };
157 };
158 TEST(test_suite_name, test_name) {
159 using EnrichedTest = IMPL_UTEST_HIDE_ENRICHED_FROM_IDE(test_suite_name, test_name)::EnrichedTest;
160 USERVER_NAMESPACE::utest::impl::TestLauncher<::testing::Test>::RunTest<EnrichedTest>(
161 thread_count, death_tests_enabled
162 );
163 }
164 void IMPL_UTEST_HIDE_ENRICHED_FROM_IDE(test_suite_name, test_name)::EnrichedTest::TestBody()
165
166// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
167#define IMPL_UTEST_ANY_BEGIN(test_suite_name, test_name, test_launcher_template)
168 IMPL_UTEST_MAKE_USER_FIXTURE_ALIAS(test_suite_name);
169 struct IMPL_UTEST_HIDE_ENRICHED_FROM_IDE(test_suite_name, test_name) final {
170 class EnrichedTest final
171 : public USERVER_NAMESPACE::utest::impl::EnrichedFixture<IMPL_UTEST_USER_FIXTURE(test_suite_name)> {
172 void TestBody() override;
173 };
174 };
175 /* The 'namespace' trick is used to make gtest use our 'test_launcher' \
176 * instead of 'test_suite_name' fixture */
177 namespace IMPL_UTEST_NAMESPACE_NAME(test_suite_name) {
178 IMPL_UTEST_HIDE_USER_FIXTURE_BY_TEST_LAUNCHER(test_suite_name, test_launcher_template)
179
180// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
181#define IMPL_UTEST_ANY_END(test_suite_name, test_name, thread_count, death_tests_enabled)
182 /* test header goes here */ {
183 using EnrichedTest = IMPL_UTEST_HIDE_ENRICHED_FROM_IDE(test_suite_name, test_name)::EnrichedTest;
184 this->RunTest<EnrichedTest>(thread_count, death_tests_enabled);
185 }
186 } /* namespace */
187 void IMPL_UTEST_HIDE_ENRICHED_FROM_IDE(test_suite_name, test_name)::EnrichedTest::TestBody()
188
189// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
190#define IMPL_UTEST_TEST_F(test_suite_name, test_name, thread_count, death_tests_enabled)
191 IMPL_UTEST_ANY_BEGIN(test_suite_name, test_name, USERVER_NAMESPACE::utest::impl::TestLauncher)
192 TEST_F(test_suite_name, test_name)
193 IMPL_UTEST_ANY_END(test_suite_name, test_name, thread_count, death_tests_enabled)
194
195// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
196#define IMPL_UTEST_TEST_P(test_suite_name, test_name, thread_count, death_tests_enabled)
197 IMPL_UTEST_ANY_BEGIN(test_suite_name, test_name, USERVER_NAMESPACE::utest::impl::TestLauncherParametric)
198 TEST_P(test_suite_name, test_name)
199 IMPL_UTEST_ANY_END(test_suite_name, test_name, thread_count, death_tests_enabled)
200
201// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
202#define IMPL_UTEST_TYPED_ANY_BEGIN(test_suite_name, test_name)
204 struct IMPL_UTEST_HIDE_ENRICHED_FROM_IDE(test_suite_name, test_name) final {
205 template <typename UtestTypeParamImpl>
206 using UtestUserFixture = IMPL_UTEST_USER_FIXTURE(test_suite_name)<UtestTypeParamImpl>;
207
208 template <typename UtestTypeParamImpl>
209 class EnrichedTest
210 : public USERVER_NAMESPACE::utest::impl::EnrichedFixture<UtestUserFixture<UtestTypeParamImpl>> {
211 using TypeParam = UtestTypeParamImpl;
212 using TestFixture = IMPL_UTEST_NON_PARENTHESIZED(test_suite_name)<TypeParam>;
213 using USERVER_NAMESPACE::utest::impl::EnrichedTestBase::GetThreadCount;
214 void TestBody() override;
215 };
216 };
217 /* The 'namespace' trick is used to make gtest use our 'TestLauncher' \
218 * instead of 'test_suite_name' fixture */
219 namespace IMPL_UTEST_NAMESPACE_NAME(test_suite_name) {
220
221// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
222#define IMPL_UTEST_TYPED_ANY_END(test_suite_name, test_name, thread_count, death_tests_enabled)
223 /* test header goes here */ {
224 using EnrichedTest = IMPL_UTEST_HIDE_ENRICHED_FROM_IDE(test_suite_name, test_name)::EnrichedTest<TypeParam>;
225 this->template RunTest<EnrichedTest>(thread_count, death_tests_enabled);
226 }
227 } /* namespace */
228 template <typename UtestTypeParamImpl>
229 void IMPL_UTEST_HIDE_ENRICHED_FROM_IDE(test_suite_name, test_name)::EnrichedTest<UtestTypeParamImpl>::TestBody()
230
231// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
232#define IMPL_UTEST_TYPED_TEST(test_suite_name, test_name, thread_count, death_tests_enabled)
233 IMPL_UTEST_TYPED_ANY_BEGIN(test_suite_name, test_name)
234 TYPED_TEST(test_suite_name, test_name)
235 IMPL_UTEST_TYPED_ANY_END(test_suite_name, test_name, thread_count, death_tests_enabled)
236
237// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
238#define IMPL_UTEST_TYPED_TEST_P(test_suite_name, test_name, thread_count, death_tests_enabled)
239 IMPL_UTEST_TYPED_ANY_BEGIN(test_suite_name, test_name)
240 TYPED_TEST_P(test_suite_name, test_name)
241 IMPL_UTEST_TYPED_ANY_END(test_suite_name, test_name, thread_count, death_tests_enabled)