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