1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <folly/experimental/coro/BlockingWait.h>
20 #include <folly/experimental/coro/Coroutine.h>
21 #include <folly/experimental/coro/Task.h>
22 #include <folly/experimental/exception_tracer/SmartExceptionTracer.h>
23 #include <folly/portability/GTest.h>
24 
25 #if FOLLY_HAS_COROUTINES
26 
27 /**
28  * This is based on the GTEST_TEST_ macro from gtest-internal.h. It seems that
29  * gtest doesn't yet support coro tests, so this macro adds a way to define a
30  * test case written as a coroutine using folly::coro::Task. It will be called
31  * using folly::coro::blockingWait().
32  *
33  * Note that you cannot use ASSERT macros in coro tests. See below for
34  * CO_ASSERT_*.
35  */
36 #define CO_TEST_(test_suite_name, test_name, parent_class, parent_id)          \
37   static_assert(                                                               \
38       sizeof(GTEST_STRINGIFY_(test_suite_name)) > 1,                           \
39       "test_suite_name must not be empty");                                    \
40   static_assert(                                                               \
41       sizeof(GTEST_STRINGIFY_(test_name)) > 1, "test_name must not be empty"); \
42   class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                     \
43       : public parent_class {                                                  \
44    public:                                                                     \
45     GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() = default;            \
46     ~GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() override = default;  \
47     GTEST_DISALLOW_COPY_AND_ASSIGN_(                                           \
48         GTEST_TEST_CLASS_NAME_(test_suite_name, test_name));                   \
49     GTEST_DISALLOW_MOVE_AND_ASSIGN_(                                           \
50         GTEST_TEST_CLASS_NAME_(test_suite_name, test_name));                   \
51                                                                                \
52    private:                                                                    \
53     void TestBody() override;                                                  \
54     folly::coro::Task<void> co_TestBody();                                     \
55     static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;      \
56   };                                                                           \
57                                                                                \
58   ::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(                           \
59       test_suite_name, test_name)::test_info_ =                                \
60       ::testing::internal::MakeAndRegisterTestInfo(                            \
61           #test_suite_name,                                                    \
62           #test_name,                                                          \
63           nullptr,                                                             \
64           nullptr,                                                             \
65           ::testing::internal::CodeLocation(__FILE__, __LINE__),               \
66           (parent_id),                                                         \
67           ::testing::internal::SuiteApiResolver<                               \
68               parent_class>::GetSetUpCaseOrSuite(__FILE__, __LINE__),          \
69           ::testing::internal::SuiteApiResolver<                               \
70               parent_class>::GetTearDownCaseOrSuite(__FILE__, __LINE__),       \
71           new ::testing::internal::TestFactoryImpl<GTEST_TEST_CLASS_NAME_(     \
72               test_suite_name, test_name)>);                                   \
73   void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() {        \
74     try {                                                                      \
75       folly::coro::blockingWait(co_TestBody());                                \
76     } catch (const std::exception& ex) {                                       \
77       GTEST_LOG_(ERROR) << ex.what() << ", async stack trace: "                \
78                         << folly::exception_tracer::getAsyncTrace(ex);         \
79       throw;                                                                   \
80     }                                                                          \
81   }                                                                            \
82   folly::coro::Task<void> GTEST_TEST_CLASS_NAME_(                              \
83       test_suite_name, test_name)::co_TestBody()
84 
85 /**
86  * TEST() for coro tests.
87  */
88 #define CO_TEST(test_case_name, test_name) \
89   CO_TEST_(                                \
90       test_case_name,                      \
91       test_name,                           \
92       ::testing::Test,                     \
93       ::testing::internal::GetTestTypeId())
94 
95 /**
96  * TEST_F() for coro tests.
97  */
98 #define CO_TEST_F(test_fixture, test_name) \
99   CO_TEST_(                                \
100       test_fixture,                        \
101       test_name,                           \
102       test_fixture,                        \
103       ::testing::internal::GetTypeId<test_fixture>())
104 
105 #define CO_TEST_P(test_suite_name, test_name)                                  \
106   class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)                     \
107       : public test_suite_name {                                               \
108    public:                                                                     \
109     GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {}                    \
110     void TestBody() override;                                                  \
111     folly::coro::Task<void> co_TestBody();                                     \
112                                                                                \
113    private:                                                                    \
114     static int AddToRegistry() {                                               \
115       ::testing::UnitTest::GetInstance()                                       \
116           ->parameterized_test_registry()                                      \
117           .GetTestSuitePatternHolder<test_suite_name>(                         \
118               GTEST_STRINGIFY_(test_suite_name),                               \
119               ::testing::internal::CodeLocation(__FILE__, __LINE__))           \
120           ->AddTestPattern(                                                    \
121               GTEST_STRINGIFY_(test_suite_name),                               \
122               GTEST_STRINGIFY_(test_name),                                     \
123               new ::testing::internal::TestMetaFactory<GTEST_TEST_CLASS_NAME_( \
124                   test_suite_name, test_name)>(),                              \
125               ::testing::internal::CodeLocation(__FILE__, __LINE__));          \
126       return 0;                                                                \
127     }                                                                          \
128     static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_;               \
129     GTEST_DISALLOW_COPY_AND_ASSIGN_(                                           \
130         GTEST_TEST_CLASS_NAME_(test_suite_name, test_name));                   \
131   };                                                                           \
132   int GTEST_TEST_CLASS_NAME_(                                                  \
133       test_suite_name, test_name)::gtest_registering_dummy_ =                  \
134       GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry();     \
135   void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody() {        \
136     try {                                                                      \
137       folly::coro::blockingWait(co_TestBody());                                \
138     } catch (const std::exception& ex) {                                       \
139       GTEST_LOG_(ERROR) << ex.what() << ", async stack trace: "                \
140                         << folly::exception_tracer::getAsyncTrace(ex);         \
141       throw;                                                                   \
142     }                                                                          \
143   }                                                                            \
144   folly::coro::Task<void> GTEST_TEST_CLASS_NAME_(                              \
145       test_suite_name, test_name)::co_TestBody()
146 
147 #define CO_TYPED_TEST(CaseName, TestName)                                     \
148   static_assert(                                                              \
149       sizeof(GTEST_STRINGIFY_(TestName)) > 1, "test-name must not be empty"); \
150   template <typename gtest_TypeParam_>                                        \
151   class GTEST_TEST_CLASS_NAME_(CaseName, TestName)                            \
152       : public CaseName<gtest_TypeParam_> {                                   \
153    private:                                                                   \
154     typedef CaseName<gtest_TypeParam_> TestFixture;                           \
155     typedef gtest_TypeParam_ TypeParam;                                       \
156     void TestBody() override;                                                 \
157     folly::coro::Task<void> co_TestBody();                                    \
158   };                                                                          \
159   static bool gtest_##CaseName##_##TestName##_registered_                     \
160       GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest<   \
161           CaseName,                                                           \
162           ::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(            \
163               CaseName, TestName)>,                                           \
164           GTEST_TYPE_PARAMS_(CaseName)>::                                     \
165           Register(                                                           \
166               "",                                                             \
167               ::testing::internal::CodeLocation(__FILE__, __LINE__),          \
168               GTEST_STRINGIFY_(CaseName),                                     \
169               GTEST_STRINGIFY_(TestName),                                     \
170               0,                                                              \
171               ::testing::internal::GenerateNames<                             \
172                   GTEST_NAME_GENERATOR_(CaseName),                            \
173                   GTEST_TYPE_PARAMS_(CaseName)>());                           \
174   template <typename gtest_TypeParam_>                                        \
175   void GTEST_TEST_CLASS_NAME_(                                                \
176       CaseName, TestName)<gtest_TypeParam_>::TestBody() {                     \
177     try {                                                                     \
178       folly::coro::blockingWait(co_TestBody());                               \
179     } catch (const std::exception& ex) {                                      \
180       GTEST_LOG_(ERROR) << ex.what() << ", async stack trace: "               \
181                         << folly::exception_tracer::getAsyncTrace(ex);        \
182       throw;                                                                  \
183     }                                                                         \
184   }                                                                           \
185   template <typename gtest_TypeParam_>                                        \
186   folly::coro::Task<void> GTEST_TEST_CLASS_NAME_(                             \
187       CaseName, TestName)<gtest_TypeParam_>::co_TestBody()
188 
189 /**
190  * Coroutine versions of GTests's Assertion predicate macros. Use these in place
191  * of ASSERT_* in CO_TEST or coroutine functions.
192  */
193 #define CO_GTEST_FATAL_FAILURE_(message) \
194   co_return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure)
195 
196 #define CO_ASSERT_PRED_FORMAT1(pred_format, v1) \
197   GTEST_PRED_FORMAT1_(pred_format, v1, CO_GTEST_FATAL_FAILURE_)
198 #define CO_ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
199   GTEST_PRED_FORMAT2_(pred_format, v1, v2, CO_GTEST_FATAL_FAILURE_)
200 
201 #define CO_ASSERT_TRUE(condition) \
202   GTEST_TEST_BOOLEAN_(            \
203       (condition), #condition, false, true, CO_GTEST_FATAL_FAILURE_)
204 #define CO_ASSERT_FALSE(condition) \
205   GTEST_TEST_BOOLEAN_(             \
206       !(condition), #condition, true, false, CO_GTEST_FATAL_FAILURE_)
207 
208 #if defined(GTEST_IS_NULL_LITERAL_)
209 #define CO_ASSERT_EQ(val1, val2)                                            \
210   CO_ASSERT_PRED_FORMAT2(                                                   \
211       ::testing::internal::EqHelper<GTEST_IS_NULL_LITERAL_(val1)>::Compare, \
212       val1,                                                                 \
213       val2)
214 #else
215 #define CO_ASSERT_EQ(val1, val2) \
216   CO_ASSERT_PRED_FORMAT2(::testing::internal::EqHelper::Compare, val1, val2)
217 #endif
218 
219 #define CO_ASSERT_NE(val1, val2) \
220   CO_ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2)
221 #define CO_ASSERT_LE(val1, val2) \
222   CO_ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2)
223 #define CO_ASSERT_LT(val1, val2) \
224   CO_ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2)
225 #define CO_ASSERT_GE(val1, val2) \
226   CO_ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2)
227 #define CO_ASSERT_GT(val1, val2) \
228   CO_ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2)
229 
230 /**
231  * coroutine version of FAIL() which is defined as GTEST_FAIL()
232  * GTEST_FATAL_FAILURE_("Failed")
233  */
234 #define CO_FAIL() CO_GTEST_FATAL_FAILURE_("Failed")
235 
236 /**
237  * Coroutine version of SKIP() which is defined as GTEST_SKIP()
238  */
239 #define CO_SKIP(message) \
240   co_return GTEST_MESSAGE_(message, ::testing::TestPartResult::kSkip)
241 
242 #endif // FOLLY_HAS_COROUTINES
243