aboutsummaryrefslogtreecommitdiffstats
path: root/library/cpp/testing/unittest/registar.cpp
diff options
context:
space:
mode:
authorDevtools Arcadia <arcadia-devtools@yandex-team.ru>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/testing/unittest/registar.cpp
downloadydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/testing/unittest/registar.cpp')
-rw-r--r--library/cpp/testing/unittest/registar.cpp513
1 files changed, 513 insertions, 0 deletions
diff --git a/library/cpp/testing/unittest/registar.cpp b/library/cpp/testing/unittest/registar.cpp
new file mode 100644
index 0000000000..3679b768ed
--- /dev/null
+++ b/library/cpp/testing/unittest/registar.cpp
@@ -0,0 +1,513 @@
+#include "registar.h"
+
+#include <library/cpp/diff/diff.h>
+#include <library/cpp/colorizer/colors.h>
+
+#include <util/generic/bt_exception.h>
+#include <util/random/fast.h>
+#include <util/string/printf.h>
+#include <util/system/backtrace.h>
+#include <util/system/guard.h>
+#include <util/system/tls.h>
+#include <util/system/error.h>
+#include <util/string/cast.h>
+
+bool NUnitTest::ShouldColorizeDiff = true;
+bool NUnitTest::ContinueOnFail = false;
+
+TString NUnitTest::RandomString(size_t len, ui32 seed) {
+ TReallyFastRng32 rand(seed);
+ TString ret;
+
+ ret.reserve(len);
+
+ for (size_t i = 0; i < len; ++i) {
+ ret.push_back(char(rand.Uniform(1, 128)));
+ }
+
+ return ret;
+}
+
+Y_POD_STATIC_THREAD(bool)
+UnittestThread;
+Y_POD_STATIC_THREAD(NUnitTest::TTestBase*)
+currentTest;
+::NUnitTest::TRaiseErrorHandler RaiseErrorHandler;
+
+void ::NUnitTest::NPrivate::RaiseError(const char* what, const TString& msg, bool fatalFailure) {
+ Y_VERIFY(UnittestThread, "%s in non-unittest thread with message:\n%s", what, msg.data());
+ Y_VERIFY(GetCurrentTest());
+
+ if (RaiseErrorHandler) {
+ RaiseErrorHandler(what, msg, fatalFailure);
+ return;
+ }
+
+ // Default handler
+ TBackTrace bt;
+ bt.Capture();
+ GetCurrentTest()->AddError(msg.data(), bt.PrintToString());
+ if (::NUnitTest::ContinueOnFail || !fatalFailure) {
+ return;
+ }
+ throw TAssertException();
+}
+
+void ::NUnitTest::SetRaiseErrorHandler(::NUnitTest::TRaiseErrorHandler handler) {
+ Y_VERIFY(UnittestThread);
+ RaiseErrorHandler = std::move(handler);
+}
+
+void ::NUnitTest::NPrivate::SetUnittestThread(bool unittestThread) {
+ Y_VERIFY(UnittestThread != unittestThread, "state check");
+ UnittestThread = unittestThread;
+}
+
+void ::NUnitTest::NPrivate::SetCurrentTest(TTestBase* test) {
+ Y_VERIFY(!test || !currentTest, "state check");
+ currentTest = test;
+}
+
+NUnitTest::TTestBase* ::NUnitTest::NPrivate::GetCurrentTest() {
+ return currentTest;
+}
+
+struct TDiffColorizer {
+ NColorizer::TColors Colors;
+ bool Reverse = false;
+
+ explicit TDiffColorizer(bool reverse = false)
+ : Reverse(reverse)
+ {
+ }
+
+ TString Special(TStringBuf str) const {
+ return ToString(Colors.YellowColor()) + str;
+ }
+
+ TString Common(TArrayRef<const char> str) const {
+ return ToString(Colors.OldColor()) + TString(str.begin(), str.end());
+ }
+
+ TString Left(TArrayRef<const char> str) const {
+ return ToString(GetLeftColor()) + TString(str.begin(), str.end());
+ }
+
+ TString Right(TArrayRef<const char> str) const {
+ return ToString(GetRightColor()) + TString(str.begin(), str.end());
+ }
+
+ TStringBuf GetLeftColor() const {
+ return Reverse ? Colors.RedColor() : Colors.GreenColor();
+ }
+
+ TStringBuf GetRightColor() const {
+ return Reverse ? Colors.GreenColor() : Colors.RedColor();
+ }
+};
+
+struct TTraceDiffFormatter {
+ bool Reverse = false;
+
+ explicit TTraceDiffFormatter(bool reverse = false)
+ : Reverse(reverse)
+ {
+ }
+
+ TString Special(TStringBuf str) const {
+ return ToString(str);
+ }
+
+ TString Common(TArrayRef<const char> str) const {
+ return TString(str.begin(), str.end());
+ }
+
+ TString Left(TArrayRef<const char> str) const {
+ return NUnitTest::GetFormatTag("good") +
+ TString(str.begin(), str.end()) +
+ NUnitTest::GetResetTag();
+ }
+
+ TString Right(TArrayRef<const char> str) const {
+ return NUnitTest::GetFormatTag("bad") +
+ TString(str.begin(), str.end()) +
+ NUnitTest::GetResetTag();
+ }
+};
+
+TString NUnitTest::GetFormatTag(const char* name) {
+ return Sprintf("[[%s]]", name);
+}
+
+TString NUnitTest::GetResetTag() {
+ return TString("[[rst]]");
+}
+
+TString NUnitTest::ColoredDiff(TStringBuf s1, TStringBuf s2, const TString& delims, bool reverse) {
+ TStringStream res;
+ TVector<NDiff::TChunk<char>> chunks;
+ NDiff::InlineDiff(chunks, s1, s2, delims);
+ if (NUnitTest::ShouldColorizeDiff) {
+ NDiff::PrintChunks(res, TDiffColorizer(reverse), chunks);
+ } else {
+ res << NUnitTest::GetResetTag();
+ NDiff::PrintChunks(res, TTraceDiffFormatter(reverse), chunks);
+ }
+ return res.Str();
+}
+
+static TString MakeTestName(const NUnitTest::ITestSuiteProcessor::TTest& test) {
+ return TStringBuilder() << test.unit->name << "::" << test.name;
+}
+
+static size_t CountTests(const TMap<TString, size_t>& testErrors, bool succeeded) {
+ size_t cnt = 0;
+ for (const auto& t : testErrors) {
+ if (succeeded && t.second == 0) {
+ ++cnt;
+ } else if (!succeeded && t.second > 0) {
+ ++cnt;
+ }
+ }
+ return cnt;
+}
+
+NUnitTest::ITestSuiteProcessor::ITestSuiteProcessor() = default;
+
+NUnitTest::ITestSuiteProcessor::~ITestSuiteProcessor() = default;
+
+void NUnitTest::ITestSuiteProcessor::Start() {
+ OnStart();
+}
+
+void NUnitTest::ITestSuiteProcessor::End() {
+ OnEnd();
+}
+
+void NUnitTest::ITestSuiteProcessor::UnitStart(const TUnit& unit) {
+ CurTestErrors_.clear();
+
+ OnUnitStart(&unit);
+}
+
+void NUnitTest::ITestSuiteProcessor::UnitStop(const TUnit& unit) {
+ OnUnitStop(&unit);
+}
+
+void NUnitTest::ITestSuiteProcessor::Error(const TError& descr) {
+ AddTestError(*descr.test);
+
+ OnError(&descr);
+}
+
+void NUnitTest::ITestSuiteProcessor::BeforeTest(const TTest& test) {
+ OnBeforeTest(&test);
+}
+
+void NUnitTest::ITestSuiteProcessor::Finish(const TFinish& descr) {
+ AddTestFinish(*descr.test);
+
+ OnFinish(&descr);
+}
+
+unsigned NUnitTest::ITestSuiteProcessor::GoodTests() const noexcept {
+ return CountTests(TestErrors_, true);
+}
+
+unsigned NUnitTest::ITestSuiteProcessor::FailTests() const noexcept {
+ return CountTests(TestErrors_, false);
+}
+
+unsigned NUnitTest::ITestSuiteProcessor::GoodTestsInCurrentUnit() const noexcept {
+ return CountTests(CurTestErrors_, true);
+}
+
+unsigned NUnitTest::ITestSuiteProcessor::FailTestsInCurrentUnit() const noexcept {
+ return CountTests(CurTestErrors_, false);
+}
+
+bool NUnitTest::ITestSuiteProcessor::CheckAccess(TString /*name*/, size_t /*num*/) {
+ return true;
+}
+
+bool NUnitTest::ITestSuiteProcessor::CheckAccessTest(TString /*suite*/, const char* /*name*/) {
+ return true;
+}
+
+void NUnitTest::ITestSuiteProcessor::Run(std::function<void()> f, const TString& /*suite*/, const char* /*name*/, const bool /*forceFork*/) {
+ f();
+}
+
+bool NUnitTest::ITestSuiteProcessor::GetIsForked() const {
+ return false;
+}
+
+bool NUnitTest::ITestSuiteProcessor::GetForkTests() const {
+ return false;
+}
+
+void NUnitTest::ITestSuiteProcessor::OnStart() {
+}
+
+void NUnitTest::ITestSuiteProcessor::OnEnd() {
+}
+
+void NUnitTest::ITestSuiteProcessor::OnUnitStart(const TUnit* /*unit*/) {
+}
+
+void NUnitTest::ITestSuiteProcessor::OnUnitStop(const TUnit* /*unit*/) {
+}
+
+void NUnitTest::ITestSuiteProcessor::OnError(const TError* /*error*/) {
+}
+
+void NUnitTest::ITestSuiteProcessor::OnFinish(const TFinish* /*finish*/) {
+}
+
+void NUnitTest::ITestSuiteProcessor::OnBeforeTest(const TTest* /*test*/) {
+}
+
+void NUnitTest::ITestSuiteProcessor::AddTestError(const TTest& test) {
+ const TString name = MakeTestName(test);
+ ++TestErrors_[name];
+ ++CurTestErrors_[name];
+}
+
+void NUnitTest::ITestSuiteProcessor::AddTestFinish(const TTest& test) {
+ const TString name = MakeTestName(test);
+ TestErrors_[name]; // zero errors if not touched
+ CurTestErrors_[name]; // zero errors if not touched
+}
+
+NUnitTest::ITestBaseFactory::ITestBaseFactory() {
+ Register();
+}
+
+NUnitTest::ITestBaseFactory::~ITestBaseFactory() = default;
+
+void NUnitTest::ITestBaseFactory::Register() noexcept {
+ TTestFactory::Instance().Register(this);
+}
+
+NUnitTest::TTestBase::TTestBase() noexcept
+ : Parent_(nullptr)
+ , TestErrors_()
+ , CurrentSubtest_()
+{
+}
+
+NUnitTest::TTestBase::~TTestBase() = default;
+
+TString NUnitTest::TTestBase::TypeId() const {
+ return TypeName(*this);
+}
+
+void NUnitTest::TTestBase::SetUp() {
+}
+
+void NUnitTest::TTestBase::TearDown() {
+}
+
+void NUnitTest::TTestBase::AddError(const char* msg, const TString& backtrace, TTestContext* context) {
+ ++TestErrors_;
+ const NUnitTest::ITestSuiteProcessor::TUnit unit = {Name()};
+ const NUnitTest::ITestSuiteProcessor::TTest test = {&unit, CurrentSubtest_};
+ const NUnitTest::ITestSuiteProcessor::TError err = {&test, msg, backtrace, context};
+
+ Processor()->Error(err);
+}
+
+void NUnitTest::TTestBase::AddError(const char* msg, TTestContext* context) {
+ AddError(msg, TString(), context);
+}
+
+void NUnitTest::TTestBase::RunAfterTest(std::function<void()> f) {
+ with_lock (AfterTestFunctionsLock_) {
+ AfterTestFunctions_.emplace_back(std::move(f));
+ }
+}
+
+bool NUnitTest::TTestBase::CheckAccessTest(const char* test) {
+ return Processor()->CheckAccessTest(Name(), test);
+}
+
+void NUnitTest::TTestBase::BeforeTest(const char* func) {
+ const NUnitTest::ITestSuiteProcessor::TUnit unit = {Name()};
+ const NUnitTest::ITestSuiteProcessor::TTest test = {&unit, func};
+ rusage.Fill();
+ Processor()->BeforeTest(test);
+}
+
+void NUnitTest::TTestBase::Finish(const char* func, TTestContext* context) {
+ TRusage finishRusage = TRusage::Get();
+ context->Metrics["ru_rss"] = finishRusage.MaxRss - rusage.MaxRss;
+ context->Metrics["ru_major_pagefaults"] = finishRusage.MajorPageFaults - rusage.MajorPageFaults;
+ context->Metrics["ru_utime"] = (finishRusage.Utime - rusage.Utime).MicroSeconds();
+ context->Metrics["ru_stime"] = (finishRusage.Stime - rusage.Stime).MicroSeconds();
+
+ const NUnitTest::ITestSuiteProcessor::TUnit unit = {Name()};
+ const NUnitTest::ITestSuiteProcessor::TTest test = {&unit, func};
+ const NUnitTest::ITestSuiteProcessor::TFinish finish = {&test, context, TestErrors_ == 0};
+
+ Processor()->Finish(finish);
+}
+
+void NUnitTest::TTestBase::AtStart() {
+ const NUnitTest::ITestSuiteProcessor::TUnit unit = {Name()};
+
+ Processor()->UnitStart(unit);
+}
+
+void NUnitTest::TTestBase::AtEnd() {
+ const NUnitTest::ITestSuiteProcessor::TUnit unit = {Name()};
+
+ Processor()->UnitStop(unit);
+}
+
+void NUnitTest::TTestBase::Run(std::function<void()> f, const TString& suite, const char* name, const bool forceFork) {
+ TestErrors_ = 0;
+ CurrentSubtest_ = name;
+ Processor()->Run(f, suite, name, forceFork);
+}
+
+void NUnitTest::TTestBase::BeforeTest() {
+ SetUp();
+}
+
+void NUnitTest::TTestBase::AfterTest() {
+ TearDown();
+
+ TVector<std::function<void()>> afterTestFunctions;
+ with_lock (AfterTestFunctionsLock_) {
+ afterTestFunctions.swap(AfterTestFunctions_);
+ }
+
+ for (auto i = afterTestFunctions.rbegin(); i != afterTestFunctions.rend(); ++i) {
+ std::function<void()>& f = *i;
+ if (f) {
+ f();
+ }
+ }
+}
+
+bool NUnitTest::TTestBase::GetIsForked() const {
+ return Processor()->GetIsForked();
+}
+
+bool NUnitTest::TTestBase::GetForkTests() const {
+ return Processor()->GetForkTests();
+}
+
+NUnitTest::ITestSuiteProcessor* NUnitTest::TTestBase::Processor() const noexcept {
+ return Parent_->Processor();
+}
+
+NUnitTest::TTestBase::TCleanUp::TCleanUp(TTestBase* base)
+ : Base_(base)
+{
+ ::NUnitTest::NPrivate::SetCurrentTest(base);
+ ::NUnitTest::NPrivate::SetUnittestThread(true);
+ Base_->BeforeTest();
+}
+
+NUnitTest::TTestBase::TCleanUp::~TCleanUp() {
+ try {
+ Base_->AfterTest();
+ } catch (...) {
+ Base_->AddError(CurrentExceptionMessage().data());
+ }
+ ::NUnitTest::NPrivate::SetUnittestThread(false);
+ ::NUnitTest::NPrivate::SetCurrentTest(nullptr);
+}
+
+namespace {
+ /*
+ * by default do nothing
+ */
+ class TCommonProcessor: public NUnitTest::ITestSuiteProcessor {
+ public:
+ TCommonProcessor() = default;
+
+ ~TCommonProcessor() override = default;
+ };
+
+ struct TCmp {
+ template <class T>
+ inline bool operator()(const T& l, const T& r) const noexcept {
+ return stricmp(Fix(l.Name().data()), Fix(r.Name().data())) < 0;
+ }
+
+ static inline const char* Fix(const char* n) noexcept {
+ if (*n == 'T') {
+ return n + 1;
+ }
+
+ return n;
+ }
+ };
+}
+
+NUnitTest::TTestFactory::TTestFactory(ITestSuiteProcessor* processor)
+ : Processor_(processor)
+{
+}
+
+NUnitTest::TTestFactory::~TTestFactory() = default;
+
+NUnitTest::TTestFactory& NUnitTest::TTestFactory::Instance() {
+ static TCommonProcessor p;
+ static TTestFactory f(&p);
+
+ return f;
+}
+
+unsigned NUnitTest::TTestFactory::Execute() {
+ Items_.QuickSort(TCmp());
+ Processor_->Start();
+
+ TSet<TString> types;
+ size_t cnt = 0;
+
+ for (TIntrusiveList<ITestBaseFactory>::TIterator factory = Items_.Begin(); factory != Items_.End(); ++factory) {
+ if (!Processor_->CheckAccess(factory->Name(), cnt++)) {
+ continue;
+ }
+
+ THolder<TTestBase> test(factory->ConstructTest());
+
+#ifdef _unix_ // on Windows RTTI causes memory leaks
+ TString type = test->TypeId();
+ if (types.insert(type).second == false) {
+ warnx("Duplicate suite found: %s (%s). Probably you have copy-pasted suite without changing it name", factory->Name().c_str(), type.c_str());
+ return 1;
+ }
+#endif // _unix_
+
+ test->Parent_ = this;
+
+#ifdef UT_SKIP_EXCEPTIONS
+ try {
+#endif
+ test->Execute();
+#ifdef UT_SKIP_EXCEPTIONS
+ } catch (...) {
+ }
+#endif
+ }
+
+ Processor_->End();
+
+ return bool(Processor_->FailTests());
+}
+
+void NUnitTest::TTestFactory::SetProcessor(ITestSuiteProcessor* processor) {
+ Processor_ = processor;
+}
+
+void NUnitTest::TTestFactory::Register(ITestBaseFactory* b) noexcept {
+ Items_.PushBack(b);
+}
+
+NUnitTest::ITestSuiteProcessor* NUnitTest::TTestFactory::Processor() const noexcept {
+ return Processor_;
+}