123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050 |
- #pragma once
- #include <library/cpp/dbg_output/dump.h>
- #include <util/generic/bt_exception.h>
- #include <util/generic/hash.h>
- #include <util/generic/intrlist.h>
- #include <util/generic/map.h>
- #include <util/generic/ptr.h>
- #include <util/generic/scope.h>
- #include <util/generic/set.h>
- #include <util/generic/typetraits.h>
- #include <util/generic/vector.h>
- #include <util/generic/yexception.h>
- #include <util/string/builder.h>
- #include <util/string/cast.h>
- #include <util/string/printf.h>
- #include <util/system/defaults.h>
- #include <util/system/type_name.h>
- #include <util/system/spinlock.h>
- #include <util/system/src_location.h>
- #include <util/system/rusage.h>
- #include <cmath>
- #include <cstdio>
- #include <functional>
- extern bool CheckExceptionMessage(const char*, TString&);
- namespace NUnitTest {
- class TTestBase;
- namespace NPrivate {
- void RaiseError(const char* what, const TString& msg, bool fatalFailure);
- void SetUnittestThread(bool);
- void SetCurrentTest(TTestBase*);
- TTestBase* GetCurrentTest();
- }
- extern bool ShouldColorizeDiff;
- TString ColoredDiff(TStringBuf s1, TStringBuf s2, const TString& delims = TString(), bool reverse = false);
- TString GetFormatTag(const char* name);
- TString GetResetTag();
- // Raise error handler
- // Used for testing library/cpp/testing/unittest macroses
- // and unittest helpers.
- // For all other unittests standard handler is used
- using TRaiseErrorHandler = std::function<void(const char*, const TString&, bool)>;
- void SetRaiseErrorHandler(TRaiseErrorHandler handler);
- inline void ClearRaiseErrorHandler() {
- SetRaiseErrorHandler(TRaiseErrorHandler());
- }
- class TAssertException: public yexception {
- };
- class ITestSuiteProcessor;
- struct TTestContext {
- TTestContext()
- : Processor(nullptr)
- {
- }
- explicit TTestContext(ITestSuiteProcessor* processor)
- : Processor(processor)
- {
- }
- using TMetrics = THashMap<TString, double>;
- TMetrics Metrics;
- ITestSuiteProcessor* Processor;
- };
- class ITestSuiteProcessor {
- public:
- struct TUnit {
- const TString name;
- };
- struct TTest {
- const TUnit* unit;
- const char* name;
- };
- struct TError {
- const TTest* test;
- const char* msg;
- TString BackTrace;
- TTestContext* Context;
- };
- struct TFinish {
- const TTest* test;
- TTestContext* Context;
- bool Success;
- };
- ITestSuiteProcessor();
- virtual ~ITestSuiteProcessor();
- void Start();
- void End();
- void UnitStart(const TUnit& unit);
- void UnitStop(const TUnit& unit);
- void Error(const TError& descr);
- void BeforeTest(const TTest& test);
- void Finish(const TFinish& descr);
- unsigned GoodTests() const noexcept;
- unsigned FailTests() const noexcept;
- unsigned GoodTestsInCurrentUnit() const noexcept;
- unsigned FailTestsInCurrentUnit() const noexcept;
- // Should execute test suite?
- virtual bool CheckAccess(TString /*name*/, size_t /*num*/);
- // Should execute a test whitin suite?
- virtual bool CheckAccessTest(TString /*suite*/, const char* /*name*/);
- virtual void Run(std::function<void()> f, const TString& /*suite*/, const char* /*name*/, bool /*forceFork*/);
- // This process is forked for current test
- virtual bool GetIsForked() const;
- // --fork-tests is set (warning: this may be false, but never the less test will be forked if called inside UNIT_FORKED_TEST)
- virtual bool GetForkTests() const;
- virtual void SetForkTestsParams(bool forkTests, bool isForked);
- private:
- virtual void OnStart();
- virtual void OnEnd();
- virtual void OnUnitStart(const TUnit* /*unit*/);
- virtual void OnUnitStop(const TUnit* /*unit*/);
- virtual void OnError(const TError* /*error*/);
- virtual void OnFinish(const TFinish* /*finish*/);
- virtual void OnBeforeTest(const TTest* /*test*/);
- void AddTestError(const TTest& test);
- void AddTestFinish(const TTest& test);
- private:
- TMap<TString, size_t> TestErrors_;
- TMap<TString, size_t> CurTestErrors_;
- bool IsForked_ = false;
- bool ForkTests_ = false;
- };
- class TTestBase;
- class TTestFactory;
- class ITestBaseFactory: public TIntrusiveListItem<ITestBaseFactory> {
- public:
- ITestBaseFactory();
- virtual ~ITestBaseFactory();
- // name of test suite
- virtual TString Name() const noexcept = 0;
- virtual TTestBase* ConstructTest() = 0;
- private:
- void Register() noexcept;
- };
- class TTestBase {
- friend class TTestFactory;
- TRusage rusage;
- public:
- TTestBase() noexcept;
- virtual ~TTestBase();
- virtual TString TypeId() const;
- virtual TString Name() const noexcept = 0;
- virtual void Execute() = 0;
- virtual void SetUp();
- virtual void TearDown();
- virtual void GlobalSuiteSetUp() {}
- virtual void GlobalSuiteTearDown() {}
- void AddError(const char* msg, const TString& backtrace = TString(), TTestContext* context = nullptr);
- void AddError(const char* msg, TTestContext* context);
- void RunAfterTest(std::function<void()> f); // function like atexit to run after current unit test
- protected:
- bool CheckAccessTest(const char* test);
- void BeforeTest(const char* func);
- void Finish(const char* func, TTestContext* context);
- void AtStart();
- void AtEnd();
- void Run(std::function<void()> f, const TString& suite, const char* name, bool forceFork);
- class TCleanUp {
- public:
- explicit TCleanUp(TTestBase* base);
- ~TCleanUp();
- private:
- TTestBase* Base_;
- };
- void BeforeTest();
- void AfterTest();
- bool GetIsForked() const;
- bool GetForkTests() const;
- ITestSuiteProcessor* Processor() const noexcept;
- private:
- TTestFactory* Parent_;
- size_t TestErrors_;
- const char* CurrentSubtest_;
- TAdaptiveLock AfterTestFunctionsLock_;
- TVector<std::function<void()>> AfterTestFunctions_;
- };
- #define UNIT_TEST_SUITE(N) \
- typedef N TThisUnitTestSuite; \
- \
- public: \
- static TString StaticName() noexcept { \
- return TString(#N); \
- } \
- \
- private: \
- virtual TString Name() const noexcept override { \
- return this->StaticName(); \
- } \
- \
- virtual void Execute() override { \
- this->AtStart();
- #define UNIT_TEST_SUITE_DEMANGLE(N) \
- typedef N TThisUnitTestSuite; \
- \
- public: \
- static TString StaticName() noexcept { \
- return TypeName<N>(); \
- } \
- \
- private: \
- virtual TString Name() const noexcept override { \
- return this->StaticName(); \
- } \
- \
- virtual void Execute() override { \
- this->AtStart();
- #ifndef UT_SKIP_EXCEPTIONS
- #define CATCH_REACTION(FN, e, context) this->AddError(("(" + TypeName(e) + ") " + e.what()).data(), context)
- #define CATCH_REACTION_BT(FN, e, context) this->AddError(("(" + TypeName(e) + ") " + e.what()).data(), (e.BackTrace() ? e.BackTrace()->PrintToString() : TString()), context)
- #else
- #define CATCH_REACTION(FN, e, context) throw
- #define CATCH_REACTION_BT(FN, e, context) throw
- #endif
- #define UNIT_TEST_CHECK_TEST_IS_DECLARED_ONLY_ONCE(F) \
- /* If you see this message - delete multiple UNIT_TEST(TestName) with same TestName. */ \
- /* It's forbidden to declare same test twice because it breaks --fork-tests logic. */ \
- int You_have_declared_test_##F##_multiple_times_This_is_forbidden; \
- Y_UNUSED(You_have_declared_test_##F##_multiple_times_This_is_forbidden);
- #define UNIT_TEST_RUN(F, FF, context) \
- this->BeforeTest((#F)); \
- { \
- struct T##F##Caller { \
- static void X(TThisUnitTestSuite* thiz, NUnitTest::TTestContext&) { \
- TCleanUp cleaner(thiz); \
- thiz->F(); \
- } \
- }; \
- this->TTestBase::Run(std::bind(&T##F##Caller::X, this, context), StaticName(), (#F), FF); \
- }
- #define UNIT_TEST_IMPL(F, FF) \
- UNIT_TEST_CHECK_TEST_IS_DECLARED_ONLY_ONCE(F) { \
- NUnitTest::TTestContext context(this->TTestBase::Processor()); \
- if (this->CheckAccessTest((#F))) { \
- try { \
- UNIT_TEST_RUN(F, FF, context) \
- } catch (const ::NUnitTest::TAssertException&) { \
- } catch (const yexception& e) { \
- CATCH_REACTION_BT((#F), e, &context); \
- } catch (const std::exception& e) { \
- CATCH_REACTION((#F), e, &context); \
- } catch (...) { \
- this->AddError("non-std exception!", &context); \
- } \
- this->Finish((#F), &context); \
- } \
- }
- #define UNIT_TEST(F) UNIT_TEST_IMPL(F, false)
- #define UNIT_FORKED_TEST(F) UNIT_TEST_IMPL(F, true)
- #define UNIT_TEST_EXCEPTION(F, E) \
- /* main process with "--fork-tests" flag treats exceptions as errors - it's result of forked test run */ \
- if (this->GetForkTests() && !this->GetIsForked()) { \
- UNIT_TEST_IMPL(F, false); \
- /* forked process (or main without "--fork-tests") treats some exceptions as success - it's exception test! */ \
- } else { \
- NUnitTest::TTestContext context(this->TTestBase::Processor()); \
- if (this->CheckAccessTest((#F))) { \
- try { \
- UNIT_TEST_RUN(F, false, context) \
- this->AddError("exception expected", &context); \
- } catch (const ::NUnitTest::TAssertException&) { \
- } catch (const E& e) { \
- TString err; \
- if (!CheckExceptionMessage(e.what(), err)) \
- this->AddError(err.c_str(), &context); \
- } catch (const std::exception& e) { \
- this->AddError(e.what(), &context); \
- } catch (...) { \
- this->AddError("non-std exception!", &context); \
- } \
- this->Finish((#F), &context); \
- } \
- }
- #define UNIT_TEST_SUITE_END() \
- this->AtEnd(); \
- } \
- \
- public: \
- /*for ; after macros*/ void sub##F()
- #define UNIT_FAIL_IMPL(R, M) \
- do { \
- ::NUnitTest::NPrivate::RaiseError(R, ::TStringBuilder() << R << " at " << __LOCATION__ << ", " << __PRETTY_FUNCTION__ << ": " << M, true); \
- } while (false)
- #define UNIT_FAIL_NONFATAL_IMPL(R, M) \
- do { \
- ::NUnitTest::NPrivate::RaiseError(R, ::TStringBuilder() << R << " at " << __LOCATION__ << ", " << __PRETTY_FUNCTION__ << ": " << M, false); \
- } while (false)
- #define UNIT_FAIL(M) UNIT_FAIL_IMPL("forced failure", M)
- #define UNIT_FAIL_NONFATAL(M) UNIT_FAIL_NONFATAL_IMPL("forced failure", M)
- //types
- #define UNIT_ASSERT_TYPES_EQUAL(A, B) \
- do { \
- if (!std::is_same<A, B>::value) { \
- UNIT_FAIL_IMPL("types equal assertion failed", (::TStringBuilder() << #A << " (" << TypeName<A>() << ") != " << #B << " (" << TypeName<B>() << ")").data()); \
- } \
- } while (false)
- //doubles
- // UNIT_ASSERT_DOUBLES_EQUAL_DEPRECATED* macros do not handle NaNs correctly (see IGNIETFERRO-1419) and are for backward compatibility
- // only. Consider switching to regular UNIT_ASSERT_DOUBLES_EQUAL* macros if you're still using the deprecated version.
- #define UNIT_ASSERT_DOUBLES_EQUAL_DEPRECATED_C(E, A, D, C) \
- do { \
- if (std::abs((E) - (A)) > (D)) { \
- const auto _es = ToString((long double)(E)); \
- const auto _as = ToString((long double)(A)); \
- const auto _ds = ToString((long double)(D)); \
- auto&& failMsg = Sprintf("std::abs(%s - %s) > %s %s", _es.data(), _as.data(), _ds.data(), (::TStringBuilder() << C).data()); \
- UNIT_FAIL_IMPL("assertion failure", failMsg); \
- } \
- } while (false)
- #define UNIT_ASSERT_DOUBLES_EQUAL_DEPRECATED(E, A, D) UNIT_ASSERT_DOUBLES_EQUAL_DEPRECATED_C(E, A, D, "")
- #define UNIT_ASSERT_DOUBLES_EQUAL_C(E, A, D, C) \
- do { \
- const auto _ed = (E); \
- const auto _ad = (A); \
- const auto _dd = (D); \
- if (std::isnan((long double)_ed) && !std::isnan((long double)_ad)) { \
- const auto _as = ToString((long double)_ad); \
- auto&& failMsg = Sprintf("expected NaN, got %s %s", _as.data(), (::TStringBuilder() << C).data()); \
- UNIT_FAIL_IMPL("assertion failure", failMsg); \
- } \
- if (!std::isnan((long double)_ed) && std::isnan((long double)_ad)) { \
- const auto _es = ToString((long double)_ed); \
- auto&& failMsg = Sprintf("expected %s, got NaN %s", _es.data(), (::TStringBuilder() << C).data()); \
- UNIT_FAIL_IMPL("assertion failure", failMsg); \
- } \
- if (std::abs((_ed) - (_ad)) > (_dd)) { \
- const auto _es = ToString((long double)_ed); \
- const auto _as = ToString((long double)_ad); \
- const auto _ds = ToString((long double)_dd); \
- auto&& failMsg = Sprintf("std::abs(%s - %s) > %s %s", _es.data(), _as.data(), _ds.data(), (::TStringBuilder() << C).data()); \
- UNIT_FAIL_IMPL("assertion failure", failMsg); \
- } \
- } while (false)
- #define UNIT_ASSERT_DOUBLES_EQUAL(E, A, D) UNIT_ASSERT_DOUBLES_EQUAL_C(E, A, D, "")
- //strings
- #define UNIT_ASSERT_STRINGS_EQUAL_C(A, B, C) \
- do { \
- const TString _a(A); /* NOLINT(performance-unnecessary-copy-initialization) */ \
- const TString _b(B); /* NOLINT(performance-unnecessary-copy-initialization) */ \
- if (_a != _b) { \
- auto&& failMsg = Sprintf("%s != %s %s", ToString(_a).data(), ToString(_b).data(), (::TStringBuilder() << C).data()); \
- UNIT_FAIL_IMPL("strings equal assertion failed", failMsg); \
- } \
- } while (false)
- #define UNIT_ASSERT_STRINGS_EQUAL(A, B) UNIT_ASSERT_STRINGS_EQUAL_C(A, B, "")
- #define UNIT_ASSERT_STRING_CONTAINS_C(A, B, C) \
- do { \
- const TString _a(A); /* NOLINT(performance-unnecessary-copy-initialization) */ \
- const TString _b(B); /* NOLINT(performance-unnecessary-copy-initialization) */ \
- if (!_a.Contains(_b)) { \
- auto&& msg = Sprintf("\"%s\" does not contain \"%s\", %s", ToString(_a).data(), ToString(_b).data(), (::TStringBuilder() << C).data()); \
- UNIT_FAIL_IMPL("strings contains assertion failed", msg); \
- } \
- } while (false)
- #define UNIT_ASSERT_STRING_CONTAINS(A, B) UNIT_ASSERT_STRING_CONTAINS_C(A, B, "")
- #define UNIT_ASSERT_NO_DIFF(A, B) \
- do { \
- const TString _a(A); /* NOLINT(performance-unnecessary-copy-initialization) */ \
- const TString _b(B); /* NOLINT(performance-unnecessary-copy-initialization) */ \
- if (_a != _b) { \
- UNIT_FAIL_IMPL("strings (" #A ") and (" #B ") are different", Sprintf("\n%s", ::NUnitTest::ColoredDiff(_a, _b, " \t\n.,:;'\"").data())); \
- } \
- } while (false)
- //strings
- #define UNIT_ASSERT_STRINGS_UNEQUAL_C(A, B, C) \
- do { \
- const TString _a(A); /* NOLINT(performance-unnecessary-copy-initialization) */ \
- const TString _b(B); /* NOLINT(performance-unnecessary-copy-initialization) */ \
- if (_a == _b) { \
- auto&& msg = Sprintf("%s == %s %s", ToString(_a).data(), ToString(_b).data(), (::TStringBuilder() << C).data()); \
- UNIT_FAIL_IMPL("strings unequal assertion failed", msg); \
- } \
- } while (false)
- #define UNIT_ASSERT_STRINGS_UNEQUAL(A, B) UNIT_ASSERT_STRINGS_UNEQUAL_C(A, B, "")
- //bool
- #define UNIT_ASSERT_C(A, C) \
- do { \
- if (!(A)) { \
- UNIT_FAIL_IMPL("assertion failed", Sprintf("(%s) %s", #A, (::TStringBuilder() << C).data())); \
- } \
- } while (false)
- #define UNIT_ASSERT(A) UNIT_ASSERT_C(A, "")
- //general
- #define UNIT_ASSERT_EQUAL_C(A, B, C) \
- do { \
- if (!((A) == (B))) { /* NOLINT(readability-container-size-empty) */ \
- UNIT_FAIL_IMPL("equal assertion failed", Sprintf("%s == %s %s", #A, #B, (::TStringBuilder() << C).data())); \
- } \
- } while (false)
- #define UNIT_ASSERT_EQUAL(A, B) UNIT_ASSERT_EQUAL_C(A, B, "")
- #define UNIT_ASSERT_UNEQUAL_C(A, B, C) \
- do { \
- if ((A) == (B)) { /* NOLINT(readability-container-size-empty) */ \
- UNIT_FAIL_IMPL("unequal assertion failed", Sprintf("%s != %s %s", #A, #B, (::TStringBuilder() << C).data()));\
- } \
- } while (false)
- #define UNIT_ASSERT_UNEQUAL(A, B) UNIT_ASSERT_UNEQUAL_C(A, B, "")
- #define UNIT_ASSERT_LT_C(A, B, C) \
- do { \
- if (!((A) < (B))) { \
- UNIT_FAIL_IMPL("less-than assertion failed", Sprintf("%s < %s %s", #A, #B, (::TStringBuilder() << C).data())); \
- } \
- } while (false)
- #define UNIT_ASSERT_LT(A, B) UNIT_ASSERT_LT_C(A, B, "")
- #define UNIT_ASSERT_LE_C(A, B, C) \
- do { \
- if (!((A) <= (B))) { \
- UNIT_FAIL_IMPL("less-or-equal assertion failed", Sprintf("%s <= %s %s", #A, #B, (::TStringBuilder() << C).data())); \
- } \
- } while (false)
- #define UNIT_ASSERT_LE(A, B) UNIT_ASSERT_LE_C(A, B, "")
- #define UNIT_ASSERT_GT_C(A, B, C) \
- do { \
- if (!((A) > (B))) { \
- UNIT_FAIL_IMPL("greater-than assertion failed", Sprintf("%s > %s %s", #A, #B, (::TStringBuilder() << C).data())); \
- } \
- } while (false)
- #define UNIT_ASSERT_GT(A, B) UNIT_ASSERT_GT_C(A, B, "")
- #define UNIT_ASSERT_GE_C(A, B, C) \
- do { \
- if (!((A) >= (B))) { \
- UNIT_FAIL_IMPL("greater-or-equal assertion failed", Sprintf("%s >= %s %s", #A, #B, (::TStringBuilder() << C).data())); \
- } \
- } while (false)
- #define UNIT_ASSERT_GE(A, B) UNIT_ASSERT_GE_C(A, B, "")
- #define UNIT_CHECK_GENERATED_EXCEPTION_C(A, E, C) \
- do { \
- try { \
- (void)(A); \
- } catch (const ::NUnitTest::TAssertException&) { \
- throw; \
- } catch (const E&) { \
- break; \
- } \
- UNIT_ASSERT_C(0, "Exception hasn't been thrown, but it should have happened " << C); \
- } while (false)
- #define UNIT_CHECK_GENERATED_EXCEPTION(A, E) UNIT_CHECK_GENERATED_EXCEPTION_C(A, E, "")
- #define UNIT_CHECK_GENERATED_NO_EXCEPTION_C(A, E, C) \
- do { \
- try { \
- (void)(A); \
- } catch (const ::NUnitTest::TAssertException&) { \
- throw; \
- } catch (const E&) { \
- UNIT_ASSERT_C(0, "Exception has been thrown, but it shouldn't have happened " << C); \
- } \
- } while (false)
- #define UNIT_CHECK_GENERATED_NO_EXCEPTION(A, E) UNIT_CHECK_GENERATED_NO_EXCEPTION_C(A, E, "and exception message is:\n" << CurrentExceptionMessage())
- // Same as UNIT_ASSERT_EXCEPTION_SATISFIES but prints additional string C when nothing was thrown
- #define UNIT_ASSERT_EXCEPTION_SATISFIES_C(A, E, pred, C) \
- do { \
- bool _thrown = false; \
- try { \
- (void)(A); \
- } catch (const ::NUnitTest::TAssertException&) { \
- throw; \
- } catch (const E& e) { \
- _thrown = true; \
- UNIT_ASSERT_C(pred(e), "Exception does not satisfy predicate '" \
- << #pred << "'"); \
- } catch (...) { \
- _thrown = true; \
- UNIT_FAIL_IMPL("exception assertion failed", \
- #A << " did not throw " << #E \
- << ", but threw other exception " \
- << "with message:\n" \
- << CurrentExceptionMessage()); \
- } \
- if (!_thrown) { \
- UNIT_FAIL_IMPL("exception assertion failed", \
- #A << " did not throw any exception" \
- << " (expected " << #E << ") " << C); \
- } \
- } while (false)
- // Assert that a specific exception is thrown and satisfies predicate pred(e), where e is the exception instance.
- // Example:
- // UNIT_ASSERT_EXCEPTION_SATISFIES(MakeRequest(invalidData), TError,
- // [](const TError& e){ return e.Status == HTTP_BAD_REQUEST; })
- // This code validates that MakeRequest with invalidData throws TError with code 400.
- #define UNIT_ASSERT_EXCEPTION_SATISFIES(A, E, pred) \
- UNIT_ASSERT_EXCEPTION_SATISFIES_C(A, E, pred, "")
- // Same as UNIT_ASSERT_EXCEPTION_CONTAINS but prints additional string C when nothing was thrown
- #define UNIT_ASSERT_EXCEPTION_CONTAINS_C(A, E, substr, C) \
- do { \
- const TString _substr{substr}; \
- UNIT_ASSERT_EXCEPTION_SATISFIES_C(A, E, \
- [&_substr](const E&){ \
- if (!_substr.empty()) { \
- auto cure = CurrentExceptionMessage() ; \
- UNIT_ASSERT_C(cure.Contains(_substr), \
- "Exception message does not contain \"" \
- << _substr << "\".\n" \
- << "Exception message: " \
- << cure); \
- } \
- return true; \
- }, \
- C); \
- } while (false)
- // Assert that a specific exception is thrown and CurrentExceptionMessage() contains substr
- #define UNIT_ASSERT_EXCEPTION_CONTAINS(A, E, substr) \
- UNIT_ASSERT_EXCEPTION_CONTAINS_C(A, E, substr, "")
- // Same as UNIT_ASSERT_EXCEPTION but prints additional string C when nothing was thrown
- #define UNIT_ASSERT_EXCEPTION_C(A, E, C) UNIT_ASSERT_EXCEPTION_SATISFIES_C(A, E, [](const E&){ return true; }, C)
- // Assert that a specific exception is thrown
- #define UNIT_ASSERT_EXCEPTION(A, E) UNIT_ASSERT_EXCEPTION_C(A, E, "")
- #define UNIT_ASSERT_NO_EXCEPTION_RESULT_C(A, C) \
- [&] () mutable -> decltype(A) { \
- static_assert(!std::is_void_v<decltype(A)>); \
- try { return (A); } \
- catch (const ::NUnitTest::TAssertException&) { throw; } \
- catch (...) { \
- UNIT_FAIL_IMPL( \
- "exception-free assertion failed", \
- Sprintf("%s throws %s\nException message: %s", \
- #A, (::TStringBuilder() << C).data(), \
- CurrentExceptionMessage().data())); \
- return decltype(A){}; \
- } \
- }()
- #define UNIT_ASSERT_NO_EXCEPTION_RESULT(A) UNIT_ASSERT_NO_EXCEPTION_RESULT_C(A, "")
- #define UNIT_ASSERT_NO_EXCEPTION_C(A, C) \
- do { \
- try { \
- (void)(A); \
- } catch (const ::NUnitTest::TAssertException&) { \
- throw; \
- } catch (...) { \
- UNIT_FAIL_IMPL("exception-free assertion failed", Sprintf("%s throws %s\nException message: %s", #A, (::TStringBuilder() << C).data(), CurrentExceptionMessage().data())); \
- } \
- } while (false)
- #define UNIT_ASSERT_NO_EXCEPTION(A) UNIT_ASSERT_NO_EXCEPTION_C(A, "")
- namespace NPrivate {
- template <class T, class U, bool Integers>
- struct TCompareValuesImpl {
- static inline bool Compare(const T& a, const U& b) {
- return a == b;
- }
- };
- template <class T, class U>
- struct TCompareValuesImpl<T, U, true> {
- static inline bool Compare(const T& a, const U& b) {
- return ::ToString(a) == ::ToString(b);
- }
- };
- template <class T, class U>
- using TCompareValues = TCompareValuesImpl<T, U, std::is_integral<T>::value && std::is_integral<U>::value>;
- template <typename T, typename U>
- static inline bool CompareEqual(const T& a, const U& b) {
- return TCompareValues<T, U>::Compare(a, b);
- }
- static inline bool CompareEqual(const char* a, const char* b) {
- return 0 == strcmp(a, b);
- }
- // helper method to avoid double evaluation of A and B expressions in UNIT_ASSERT_VALUES_EQUAL_C
- template <typename T, typename U>
- static inline bool CompareAndMakeStrings(const T& a, const U& b, TString& as, TString& asInd, TString& bs, TString& bsInd, bool& usePlainDiff, bool want) {
- const bool have = CompareEqual(a, b);
- usePlainDiff = std::is_integral<T>::value && std::is_integral<U>::value;
- if (want == have) {
- return true;
- }
- as = ::TStringBuilder() << ::DbgDump(a);
- bs = ::TStringBuilder() << ::DbgDump(b);
- asInd = ::TStringBuilder() << ::DbgDump(a).SetIndent(true);
- bsInd = ::TStringBuilder() << ::DbgDump(b).SetIndent(true);
- return false;
- }
- }
- //values
- #define UNIT_ASSERT_VALUES_EQUAL_IMPL(A, B, C, EQflag, EQstr, NEQstr) \
- do { \
- TString _as; \
- TString _bs; \
- TString _asInd; \
- TString _bsInd; \
- bool _usePlainDiff; \
- if (!::NUnitTest::NPrivate::CompareAndMakeStrings(A, B, _as, _asInd, _bs, _bsInd, _usePlainDiff, EQflag)) { \
- auto&& failMsg = Sprintf("(%s %s %s) failed: (%s %s %s) %s", #A, EQstr, #B, _as.data(), NEQstr, _bs.data(), (::TStringBuilder() << C).data()); \
- if (EQflag && !_usePlainDiff) { \
- failMsg += ", with diff:\n"; \
- failMsg += ::NUnitTest::ColoredDiff(_asInd, _bsInd); \
- } \
- UNIT_FAIL_IMPL("assertion failed", failMsg); \
- } \
- } while (false)
- #define UNIT_ASSERT_VALUES_EQUAL_C(A, B, C) \
- UNIT_ASSERT_VALUES_EQUAL_IMPL(A, B, C, true, "==", "!=")
- #define UNIT_ASSERT_VALUES_UNEQUAL_C(A, B, C) \
- UNIT_ASSERT_VALUES_EQUAL_IMPL(A, B, C, false, "!=", "==")
- #define UNIT_ASSERT_VALUES_EQUAL(A, B) UNIT_ASSERT_VALUES_EQUAL_C(A, B, "")
- #define UNIT_ASSERT_VALUES_UNEQUAL(A, B) UNIT_ASSERT_VALUES_UNEQUAL_C(A, B, "")
- // Checks that test will fail while executing given expression
- // Macro for using in unitests for ut helpers
- #define UNIT_ASSERT_TEST_FAILS_C(A, C) \
- do { \
- ::NUnitTest::TUnitTestFailChecker checker; \
- try { \
- auto guard = checker.InvokeGuard(); \
- (void)(A); \
- } catch (...) { \
- UNIT_FAIL_IMPL("fail test assertion failure", \
- "code is expected to generate test failure, " \
- "but it throws exception with message: " \
- << CurrentExceptionMessage()); \
- } \
- if (!checker.Failed()) { \
- UNIT_FAIL_IMPL("fail test assertion failure", \
- "code is expected to generate test failure"); \
- } \
- } while (false)
- #define UNIT_ASSERT_TEST_FAILS(A) UNIT_ASSERT_TEST_FAILS_C(A, "")
- #define UNIT_ADD_METRIC(name, value) ut_context.Metrics[name] = value
- class TTestFactory {
- friend class TTestBase;
- friend class ITestBaseFactory;
- public:
- static TTestFactory& Instance();
- unsigned Execute();
- void SetProcessor(ITestSuiteProcessor* processor);
- private:
- void Register(ITestBaseFactory* b) noexcept;
- ITestSuiteProcessor* Processor() const noexcept;
- private:
- explicit TTestFactory(ITestSuiteProcessor* processor);
- ~TTestFactory();
- private:
- TIntrusiveList<ITestBaseFactory> Items_;
- ITestSuiteProcessor* Processor_;
- };
- template <class T>
- class TTestBaseFactory: public ITestBaseFactory {
- public:
- ~TTestBaseFactory() override = default;
- inline TTestBase* ConstructTest() override {
- return new T;
- }
- inline TString Name() const noexcept override {
- return T::StaticName();
- }
- };
- struct TBaseTestCase {
- // NOTE: since EACH test case is instantiated for listing tests, its
- // ctor/dtor are not the best place to do heavy preparations in test fixtures.
- //
- // Consider using SetUp()/TearDown() methods instead
- inline TBaseTestCase()
- : TBaseTestCase(nullptr, nullptr, false)
- {
- }
- inline TBaseTestCase(const char* name, std::function<void(TTestContext&)> body, bool forceFork)
- : Name_(name)
- , Body_(std::move(body))
- , ForceFork_(forceFork)
- {
- }
- virtual ~TBaseTestCase() = default;
- // Each test case is executed in 3 steps:
- //
- // 1. SetUp() (from fixture)
- // 2. Execute_() (test body from Y_UNIT_TEST macro)
- // 3. TearDown() (from fixture)
- //
- // Both SetUp() and TearDown() may use UNIT_* check macros and are only
- // called when the test is executed.
- virtual void SetUp(TTestContext& /* context */) {
- }
- virtual void TearDown(TTestContext& /* context */) {
- }
- virtual void Execute_(TTestContext& context) {
- Body_(context);
- }
- const char* Name_;
- std::function<void(TTestContext&)> Body_;
- bool ForceFork_;
- };
- using TBaseFixture = TBaseTestCase;
- // Class for checking that code raises unittest failure
- class TUnitTestFailChecker {
- public:
- struct TInvokeGuard {
- explicit TInvokeGuard(TUnitTestFailChecker& parent)
- : Parent(&parent)
- {
- Parent->SetHandler();
- }
- TInvokeGuard(TInvokeGuard&& guard) noexcept
- : Parent(guard.Parent)
- {
- guard.Parent = nullptr;
- }
- ~TInvokeGuard() {
- if (Parent) {
- ClearRaiseErrorHandler();
- }
- }
- TUnitTestFailChecker* Parent;
- };
- TUnitTestFailChecker() = default;
- TUnitTestFailChecker(const TUnitTestFailChecker&) = delete;
- TUnitTestFailChecker(TUnitTestFailChecker&&) = delete;
- TInvokeGuard InvokeGuard() {
- return TInvokeGuard(*this);
- }
- const TString& What() const {
- return What_;
- }
- const TString& Msg() const {
- return Msg_;
- }
- bool FatalFailure() const {
- return FatalFailure_;
- }
- bool Failed() const {
- return Failed_;
- }
- private:
- void Handler(const char* what, const TString& msg, bool fatalFailure) {
- What_ = what;
- Msg_ = msg;
- FatalFailure_ = fatalFailure;
- Failed_ = true;
- }
- void SetHandler() {
- TRaiseErrorHandler handler = [this](const char* what, const TString& msg, bool fatalFailure) {
- Handler(what, msg, fatalFailure);
- };
- SetRaiseErrorHandler(std::move(handler));
- }
- private:
- TString What_;
- TString Msg_;
- bool FatalFailure_ = false;
- bool Failed_ = false;
- };
- #define UNIT_TEST_SUITE_REGISTRATION(T) \
- static const ::NUnitTest::TTestBaseFactory<T> Y_GENERATE_UNIQUE_ID(UTREG_);
- #define Y_UNIT_TEST_SUITE_IMPL_F(N, T, F) \
- namespace NTestSuite##N { \
- class TCurrentTestCase: public F { \
- }; \
- class TCurrentTest: public T { \
- private: \
- typedef std::function<THolder<NUnitTest::TBaseTestCase>()> TTestCaseFactory; \
- typedef TVector<TTestCaseFactory> TTests; \
- \
- static TTests& Tests() { \
- static TTests tests; \
- return tests; \
- } \
- \
- public: \
- static TString StaticName() { \
- return #N; \
- } \
- virtual TString Name() const noexcept { \
- return StaticName(); \
- } \
- \
- static void AddTest(const char* name, \
- const std::function<void(NUnitTest::TTestContext&)>& body, bool forceFork) \
- { \
- Tests().push_back([=]{ return MakeHolder<NUnitTest::TBaseTestCase>(name, body, forceFork); }); \
- } \
- \
- static void AddTest(TTestCaseFactory testCaseFactory) { \
- Tests().push_back(std::move(testCaseFactory)); \
- } \
- \
- virtual void Execute() { \
- this->AtStart(); \
- this->GlobalSuiteSetUp(); \
- for (TTests::iterator it = Tests().begin(), ie = Tests().end(); it != ie; ++it) { \
- const auto i = (*it)(); \
- if (!this->CheckAccessTest(i->Name_)) { \
- continue; \
- } \
- NUnitTest::TTestContext context(this->TTestBase::Processor()); \
- try { \
- this->BeforeTest(i->Name_); \
- { \
- TCleanUp cleaner(this); \
- auto testCase = [this, &i, &context] { \
- Y_DEFER { \
- try { \
- i->TearDown(context); \
- } catch (const ::NUnitTest::TAssertException&) { \
- } catch (const yexception& e) { \
- CATCH_REACTION_BT(i->Name_, e, &context); \
- } catch (const std::exception& e) { \
- CATCH_REACTION(i->Name_, e, &context); \
- } catch (...) { \
- this->AddError("non-std exception!", &context); \
- } \
- }; \
- i->SetUp(context); \
- i->Execute_(context); \
- }; \
- this->T::Run(testCase, StaticName(), i->Name_, i->ForceFork_); \
- } \
- } catch (const ::NUnitTest::TAssertException&) { \
- } catch (const yexception& e) { \
- CATCH_REACTION_BT(i->Name_, e, &context); \
- } catch (const std::exception& e) { \
- CATCH_REACTION(i->Name_, e, &context); \
- } catch (...) { \
- this->AddError("non-std exception!", &context); \
- } \
- this->Finish(i->Name_, &context); \
- } \
- this->GlobalSuiteTearDown(); \
- this->AtEnd(); \
- } \
- }; \
- UNIT_TEST_SUITE_REGISTRATION(TCurrentTest) \
- } \
- namespace NTestSuite##N
- #define Y_UNIT_TEST_SUITE_IMPL(N, T) Y_UNIT_TEST_SUITE_IMPL_F(N, T, ::NUnitTest::TBaseTestCase)
- #define Y_UNIT_TEST_SUITE(N) Y_UNIT_TEST_SUITE_IMPL(N, TTestBase)
- #define Y_UNIT_TEST_SUITE_F(N, F) Y_UNIT_TEST_SUITE_IMPL_F(N, TTestBase, F)
- #define RUSAGE_UNIT_TEST_SUITE(N) Y_UNIT_TEST_SUITE_IMPL(N, NUnitTest::TRusageTest, ::NUnitTest::TBaseTestCase)
- #define Y_UNIT_TEST_IMPL_REGISTER(N, FF, F) \
- struct TTestCase##N : public F { \
- TTestCase##N() \
- { \
- Name_ = #N; \
- ForceFork_ = FF; \
- } \
- static THolder<NUnitTest::TBaseTestCase> Create() { \
- return ::MakeHolder<TTestCase##N>(); \
- } \
- void Execute_(NUnitTest::TTestContext&) override; \
- }; \
- struct TTestRegistration##N { \
- TTestRegistration##N() { \
- TCurrentTest::AddTest(TTestCase##N::Create); \
- } \
- }; \
- static const TTestRegistration##N testRegistration##N;
- #define Y_UNIT_TEST_IMPL(N, FF, F) \
- Y_UNIT_TEST_IMPL_REGISTER(N, FF, F) \
- void TTestCase##N::Execute_(NUnitTest::TTestContext& ut_context Y_DECLARE_UNUSED)
- #define Y_UNIT_TEST(N) Y_UNIT_TEST_IMPL(N, false, TCurrentTestCase)
- #define Y_UNIT_TEST_F(N, F) Y_UNIT_TEST_IMPL(N, false, F)
- #define SIMPLE_UNIT_FORKED_TEST(N) Y_UNIT_TEST_IMPL(N, true, TCurrentTestCase)
- #define Y_UNIT_TEST_SUITE_IMPLEMENTATION(N) \
- namespace NTestSuite##N
- #define Y_UNIT_TEST_DECLARE(N) \
- struct TTestCase##N
- #define Y_UNIT_TEST_FRIEND(N, T) \
- friend NTestSuite##N::TTestCase##T \
- TString RandomString(size_t len, ui32 seed = 0);
- }
- using ::NUnitTest::TTestBase;
|