#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "common/utils/Coroutine.h" #include "common/utils/FaultInjection.h" namespace hf3fs::test { namespace { TEST(TestFaultInjection, Basic) { static constexpr size_t kLoops = 100000; auto fi = FaultInjection::get(); ASSERT_EQ(fi, nullptr); for (size_t i = 0; i < kLoops; i++) { auto inject = FAULT_INJECTION(); ASSERT_FALSE(inject); } FAULT_INJECTION_SET(1, -1); // 1% probility and unlimited times ASSERT_EQ(FaultInjection::get()->getProbability(), 1); ASSERT_EQ(FaultInjectionFactor::get(), 1); size_t cnt = 0; for (size_t i = 0; i < kLoops; i++) { if (FAULT_INJECTION()) { cnt++; } } ASSERT_NE(cnt, 0); std::cout << "Inject " << cnt << " faults during " << kLoops << " loops, " << (double)cnt / kLoops * 100 << "%, expect 1%" << std::endl; { // disable fault injection in current scope FAULT_INJECTION_SET(0, -1); size_t cnt = 0; for (size_t i = 0; i < kLoops; i++) { if (FAULT_INJECTION()) { cnt++; } } ASSERT_EQ(cnt, 0); } ASSERT_EQ(FaultInjection::get()->getProbability(), 1); ASSERT_EQ(FaultInjectionFactor::get(), 1); // test set factor scope. { FAULT_INJECTION_SET_FACTOR(2); ASSERT_EQ(FaultInjectionFactor::get(), 2); { FAULT_INJECTION_SET_FACTOR(3); ASSERT_EQ(FaultInjectionFactor::get(), 3); } ASSERT_EQ(FaultInjectionFactor::get(), 2); FAULT_INJECTION_SET_FACTOR(4); ASSERT_EQ(FaultInjectionFactor::get(), 4); } ASSERT_EQ(FaultInjectionFactor::get(), 1); { FAULT_INJECTION_SET_FACTOR(10); ASSERT_EQ(FaultInjectionFactor::get(), 10); size_t cnt = 0; for (size_t i = 0; i < kLoops; i++) { if (FAULT_INJECTION()) { cnt++; } } ASSERT_NE(cnt, 0); std::cout << "Set factor of current scope to 10, inject " << cnt << " faults during " << kLoops << " loops, " << (double)cnt / kLoops * 100 << "%, expect 0.1%" << std::endl; } ASSERT_EQ(FaultInjectionFactor::get(), 1); { size_t cnt = 0; FAULT_INJECTION_SET_FACTOR(100); // should be ignored for (size_t i = 0; i < kLoops; i++) { if (FAULT_INJECTION_WITH_FACTOR(5)) { cnt++; } } ASSERT_NE(cnt, 0); std::cout << "Inject with factor 5, inject " << cnt << " faults during " << kLoops << " loops, " << (double)cnt / kLoops * 100 << "%, expect 0.2%" << std::endl; } { FAULT_INJECTION_SET(10, 10); size_t cnt = 0; for (size_t i = 0; i < kLoops; i++) { if (FAULT_INJECTION()) { cnt++; } } ASSERT_EQ(cnt, 10); } } TEST(TestFaultInjection, Coroutine) { ASSERT_EQ(FaultInjection::get(), nullptr); folly::coro::blockingWait([]() -> folly::coro::Task { folly::RequestContext::create(); FAULT_INJECTION_SET(10, -1); co_return; }()); ASSERT_EQ(FaultInjection::get(), nullptr); FAULT_INJECTION_SET(10, -1); auto fi = FaultInjection::get(); ASSERT_NE(fi, nullptr); folly::coro::blockingWait([]() -> folly::coro::Task { folly::RequestContext::create(); FAULT_INJECTION_SET(10, -1); co_return; }()); ASSERT_EQ(FaultInjection::get(), fi); { folly::CPUThreadPoolExecutor executor(4); folly::coro::co_invoke([fi]() -> folly::coro::Task { CO_ASSERT_EQ(FaultInjection::get(), fi); co_return; }) .scheduleOn(&executor) .start() .wait(); executor.join(); } { FAULT_INJECTION_SET(10, 1000); folly::CPUThreadPoolExecutor executor(4); std::vector> futures; for (int i = 0; i < 10; i++) { auto future = folly::coro::co_invoke([&]() -> folly::coro::Task { size_t cnts = 0; for (int i = 0; i < 100000; i++) { if (FAULT_INJECTION()) { cnts++; } if (folly::Random::oneIn(100)) co_await folly::coro::co_reschedule_on_current_executor_t(); } co_return cnts; }) .scheduleOn(&executor) .start(); futures.push_back(std::move(future)); } auto results = folly::collectAll(futures.begin(), futures.end()).wait().result().value(); size_t cnts = 0; for (auto &result : results) { cnts += result.value(); } executor.join(); ASSERT_EQ(cnts, 1000); } } TEST(TestFaultInjection, Coroutine1) { folly::CPUThreadPoolExecutor exec(1); auto coro1 = [](FaultInjection *ptr) -> CoTask { CO_ASSERT_EQ(FaultInjection::get(), ptr); co_return; }; auto coro2 = [&]() -> CoTask { CO_ASSERT_EQ(FaultInjection::get(), nullptr); FAULT_INJECTION_SET(10, 1); co_await coro1(FaultInjection::get()).scheduleOn(&exec).start(); co_return; }; coro2().scheduleOn(&exec).start().wait(); sleep(1); } TEST(TestFaultInjection, Coroutine2) { folly::CPUThreadPoolExecutor exec(1); auto coro1 = []() -> CoTask { CO_ASSERT_EQ(folly::RequestContext::try_get(), nullptr); CO_ASSERT_EQ(FaultInjection::get(), nullptr); folly::RequestContext::create(); FAULT_INJECTION_SET(10, 1); CO_ASSERT_NE(folly::RequestContext::try_get(), nullptr); CO_ASSERT_NE(FaultInjection::get(), nullptr); co_return; }; auto coro2 = [&]() -> CoTask { CO_ASSERT_EQ(folly::RequestContext::try_get(), nullptr); CO_ASSERT_EQ(FaultInjection::get(), nullptr); co_await coro1().scheduleOn(&exec).start(); CO_ASSERT_EQ(folly::RequestContext::try_get(), nullptr); CO_ASSERT_EQ(FaultInjection::get(), nullptr); co_return; }; coro2().scheduleOn(&exec).start().wait(); } } // namespace } // namespace hf3fs::test