Repo created
This commit is contained in:
parent
4af19165ec
commit
68073add76
12458 changed files with 12350765 additions and 2 deletions
61
libs/testing/benchmark.hpp
Normal file
61
libs/testing/benchmark.hpp
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#include "testing/testing.hpp"
|
||||
#include "testing/testregister.hpp"
|
||||
|
||||
#include "base/timer.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace base
|
||||
{
|
||||
class BenchmarkNTimes
|
||||
{
|
||||
public:
|
||||
BenchmarkNTimes(int repeatCount, double maxSecondsToSucceed)
|
||||
: m_repeatCount(repeatCount)
|
||||
, m_maxSecondsToSucceed(maxSecondsToSucceed)
|
||||
, m_iteration(0)
|
||||
{}
|
||||
|
||||
~BenchmarkNTimes()
|
||||
{
|
||||
double const secondsElapsed = m_timer.ElapsedSeconds();
|
||||
TEST_GREATER(m_repeatCount, 0, ());
|
||||
TEST_LESS_OR_EQUAL(secondsElapsed, m_maxSecondsToSucceed, (m_repeatCount));
|
||||
std::cout << secondsElapsed << "s total";
|
||||
if (secondsElapsed > 0)
|
||||
{
|
||||
std::cout << ", " << static_cast<int>(m_repeatCount / secondsElapsed) << "/s, ";
|
||||
/*
|
||||
if (secondsElapsed / m_repeatCount * 1000 >= 10)
|
||||
std::cout << static_cast<int>(secondsElapsed / m_repeatCount * 1000) << "ms each";
|
||||
else */
|
||||
if (secondsElapsed / m_repeatCount * 1000000 >= 10)
|
||||
std::cout << static_cast<int>(secondsElapsed / m_repeatCount * 1000000) << "us each";
|
||||
else
|
||||
std::cout << static_cast<int>(secondsElapsed / m_repeatCount * 1000000000) << "ns each";
|
||||
}
|
||||
std::cout << " ...";
|
||||
}
|
||||
|
||||
int Iteration() const { return m_iteration; }
|
||||
bool ContinueIterating() const { return m_iteration < m_repeatCount; }
|
||||
void NextIteration() { ++m_iteration; }
|
||||
|
||||
private:
|
||||
int const m_repeatCount;
|
||||
double const m_maxSecondsToSucceed;
|
||||
int m_iteration;
|
||||
Timer m_timer;
|
||||
};
|
||||
} // namespace base
|
||||
|
||||
#define BENCHMARK_TEST(name) \
|
||||
void Benchmark_##name(); \
|
||||
TestRegister g_BenchmarkRegister_##name("Benchmark::" #name, __FILE__, &Benchmark_##name); \
|
||||
void Benchmark_##name()
|
||||
|
||||
#define BENCHMARK_N_TIMES(times, maxTimeToSucceed) \
|
||||
for (::base::BenchmarkNTimes benchmark(times, maxTimeToSucceed); benchmark.ContinueIterating(); \
|
||||
benchmark.NextIteration())
|
||||
255
libs/testing/testing.hpp
Normal file
255
libs/testing/testing.hpp
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
#pragma once
|
||||
|
||||
#include "testing/testregister.hpp"
|
||||
|
||||
#include "base/exception.hpp"
|
||||
#include "base/logging.hpp"
|
||||
#include "base/math.hpp"
|
||||
#include "base/src_point.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
// TestRegister is static to avoid this warning:
|
||||
// No previous extern declaration for non-static variable 'g_testRegister_TESTNAME'
|
||||
// note: declare 'static' if the variable is not intended to be used outside of this translation unit
|
||||
#define UNIT_TEST(name) \
|
||||
void UnitTest_##name(); \
|
||||
static TestRegister g_testRegister_##name(#name, __FILE__, &UnitTest_##name); \
|
||||
void UnitTest_##name()
|
||||
|
||||
#define UNIT_CLASS_TEST(CLASS, NAME) \
|
||||
struct UnitClass_##CLASS##_##NAME : public CLASS \
|
||||
{ \
|
||||
public: \
|
||||
void NAME(); \
|
||||
}; \
|
||||
UNIT_TEST(CLASS##_##NAME) \
|
||||
{ \
|
||||
UnitClass_##CLASS##_##NAME instance; \
|
||||
instance.NAME(); \
|
||||
} \
|
||||
void UnitClass_##CLASS##_##NAME::NAME()
|
||||
|
||||
DECLARE_EXCEPTION(TestFailureException, RootException);
|
||||
|
||||
namespace base
|
||||
{
|
||||
[[noreturn]] inline void OnTestFailed(SrcPoint const & srcPoint, std::string const & msg)
|
||||
{
|
||||
LOG(LINFO, ("FAILED"));
|
||||
LOG(LINFO, (::DebugPrint(srcPoint.FileName()) + ":" + ::DebugPrint(srcPoint.Line()), msg));
|
||||
MYTHROW(TestFailureException, (srcPoint.FileName(), srcPoint.Line(), msg));
|
||||
}
|
||||
} // namespace base
|
||||
|
||||
namespace testing
|
||||
{
|
||||
void RunEventLoop();
|
||||
void StopEventLoop();
|
||||
|
||||
void Wait();
|
||||
void Notify();
|
||||
} // namespace testing
|
||||
|
||||
// This struct contains parsed command line options. It may contain pointers to argc contents.
|
||||
struct CommandLineOptions
|
||||
{
|
||||
CommandLineOptions() = default;
|
||||
|
||||
char const * m_filterRegExp = nullptr;
|
||||
char const * m_suppressRegExp = nullptr;
|
||||
char const * m_dataPath = nullptr;
|
||||
char const * m_resourcePath = nullptr;
|
||||
|
||||
bool m_help = false;
|
||||
bool m_listTests = false;
|
||||
};
|
||||
CommandLineOptions const & GetTestingOptions();
|
||||
|
||||
#define TEST(X, msg) \
|
||||
do \
|
||||
{ \
|
||||
if (X) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), ::base::Message("TEST(" #X ")", ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_EQUAL(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) == (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), \
|
||||
::base::Message("TEST(" #X " == " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_NOT_EQUAL(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) != (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), \
|
||||
::base::Message("TEST(" #X " != " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_LESS(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) < (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), \
|
||||
::base::Message("TEST(" #X " < " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_LESS_OR_EQUAL(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) <= (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), \
|
||||
::base::Message("TEST(" #X " <= " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_GREATER(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) > (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), \
|
||||
::base::Message("TEST(" #X " > " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_GREATER_OR_EQUAL(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if ((X) >= (Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), \
|
||||
::base::Message("TEST(" #X " >= " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_ALMOST_EQUAL_ULPS(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if (AlmostEqualULPs(X, Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
::base::OnTestFailed( \
|
||||
SRC(), ::base::Message("TEST(AlmostEqualULPs(" #X ", " #Y ")", ::base::Message(X, Y), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_NOT_ALMOST_EQUAL_ULPS(X, Y, msg) \
|
||||
do \
|
||||
{ \
|
||||
if (!AlmostEqualULPs(X, Y)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), ::base::Message("TEST(!AlmostEqualULPs(" #X ", " #Y ")", ::base::Message(X, Y), \
|
||||
::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_ALMOST_EQUAL_ABS(X, Y, eps, msg) \
|
||||
do \
|
||||
{ \
|
||||
if (AlmostEqualAbs(X, Y, eps)) \
|
||||
{} \
|
||||
else \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), ::base::Message("TEST(!AlmostEqualAbs(" #X ", " #Y ", " #eps ")", \
|
||||
::base::Message(X, Y, eps), ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
// TODO(AlexZ): Add more cool macroses (or switch all unit tests to gtest).
|
||||
#define TEST_THROW(X, exception, msg) \
|
||||
do \
|
||||
{ \
|
||||
bool expected_exception = false; \
|
||||
try \
|
||||
{ \
|
||||
X; \
|
||||
} \
|
||||
catch (std::exception const &) \
|
||||
{ \
|
||||
expected_exception = true; \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), ::base::Message("Unexpected exception at TEST(" #X ")", ::base::Message msg)); \
|
||||
} \
|
||||
if (!expected_exception) \
|
||||
::base::OnTestFailed(SRC(), ::base::Message("Expected exception " #exception " was not thrown in TEST(" #X ")", \
|
||||
::base::Message msg)); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_NO_THROW(X, msg) \
|
||||
do \
|
||||
{ \
|
||||
try \
|
||||
{ \
|
||||
X; \
|
||||
} \
|
||||
catch (RootException const & ex) \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), ::base::Message("Unexpected exception at TEST(" #X ")", ::base::Message(ex.Msg()), \
|
||||
::base::Message msg)); \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
::base::OnTestFailed(SRC(), ::base::Message("Unexpected exception at TEST(" #X ")", ::base::Message msg)); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define TEST_ANY_THROW(X, msg) \
|
||||
do \
|
||||
{ \
|
||||
bool was_exception = false; \
|
||||
try \
|
||||
{ \
|
||||
X; \
|
||||
} \
|
||||
catch (...) \
|
||||
{ \
|
||||
was_exception = true; \
|
||||
} \
|
||||
if (!was_exception) \
|
||||
::base::OnTestFailed(SRC(), ::base::Message("No exceptions were thrown in TEST(" #X ")", ::base::Message msg)); \
|
||||
} \
|
||||
while (0)
|
||||
292
libs/testing/testingmain.cpp
Normal file
292
libs/testing/testingmain.cpp
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
#include "testing/testing.hpp"
|
||||
#include "testing/testregister.hpp"
|
||||
|
||||
#include "base/logging.hpp"
|
||||
#include "base/timer.hpp"
|
||||
#include "base/waiter.hpp"
|
||||
|
||||
#include "std/target_os.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef WITH_GL_MOCK
|
||||
#include "base/scope_guard.hpp"
|
||||
#include "drape/drape_tests/gl_mock_functions.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef OMIM_OS_IPHONE
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
|
||||
#ifndef OMIM_UNIT_TEST_DISABLE_PLATFORM_INIT
|
||||
#include "platform/platform.hpp"
|
||||
#endif
|
||||
|
||||
#if defined(OMIM_UNIT_TEST_WITH_QT_EVENT_LOOP) && !defined(OMIM_OS_IPHONE)
|
||||
#include <QtCore/Qt>
|
||||
#ifdef OMIM_OS_MAC // on Mac OS X native run loop works only for QApplication :(
|
||||
#include <QtWidgets/QApplication>
|
||||
#define QAPP QApplication
|
||||
#else
|
||||
#include <QtCore/QCoreApplication>
|
||||
#define QAPP QCoreApplication
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace testing
|
||||
{
|
||||
using namespace std;
|
||||
|
||||
base::Waiter g_waiter;
|
||||
|
||||
void RunEventLoop()
|
||||
{
|
||||
#if defined(OMIM_OS_IPHONE)
|
||||
CFRunLoopRun();
|
||||
#elif defined(QAPP)
|
||||
QAPP::exec();
|
||||
#endif
|
||||
}
|
||||
|
||||
void StopEventLoop()
|
||||
{
|
||||
#if defined(OMIM_OS_IPHONE)
|
||||
CFRunLoopStop(CFRunLoopGetMain());
|
||||
#elif defined(QAPP)
|
||||
QAPP::exit();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Wait()
|
||||
{
|
||||
g_waiter.Wait();
|
||||
g_waiter.Reset();
|
||||
}
|
||||
|
||||
void Notify()
|
||||
{
|
||||
g_waiter.Notify();
|
||||
}
|
||||
|
||||
bool g_lastTestOK = true;
|
||||
CommandLineOptions g_testingOptions;
|
||||
|
||||
int const kOptionFieldWidth = 32;
|
||||
char const kFilterOption[] = "--filter=";
|
||||
char const kSuppressOption[] = "--suppress=";
|
||||
char const kHelpOption[] = "--help";
|
||||
char const kDataPathOptions[] = "--data_path=";
|
||||
char const kResourcePathOptions[] = "--user_resource_path=";
|
||||
char const kListAllTestsOption[] = "--list_tests";
|
||||
|
||||
enum Status
|
||||
{
|
||||
STATUS_SUCCESS = 0,
|
||||
STATUS_FAILED = 1,
|
||||
STATUS_BROKEN_FRAMEWORK = 5,
|
||||
};
|
||||
|
||||
void DisplayOption(ostream & os, char const * option, char const * description)
|
||||
{
|
||||
os << " " << setw(kOptionFieldWidth) << left << option << " " << description << '\n';
|
||||
}
|
||||
|
||||
void DisplayOption(ostream & os, char const * option, char const * value, char const * description)
|
||||
{
|
||||
os << " " << setw(kOptionFieldWidth) << left << (string(option) + value) << " " << description << '\n';
|
||||
}
|
||||
|
||||
void Usage(char const * name)
|
||||
{
|
||||
cerr << "USAGE: " << name << " [options]\n\n";
|
||||
cerr << "OPTIONS:\n";
|
||||
DisplayOption(cerr, kFilterOption, "<ECMA Regexp>", "Run tests with names corresponding to regexp.");
|
||||
DisplayOption(cerr, kSuppressOption, "<ECMA Regexp>", "Do not run tests with names corresponding to regexp.");
|
||||
DisplayOption(cerr, kDataPathOptions, "<Path>", "Path to data files.");
|
||||
DisplayOption(cerr, kResourcePathOptions, "<Path>", "Path to resources, styles and classificators.");
|
||||
DisplayOption(cerr, kListAllTestsOption, "List all the tests in the test suite and exit.");
|
||||
DisplayOption(cerr, kHelpOption, "Print this help message and exit.");
|
||||
}
|
||||
|
||||
void ParseOptions(int argc, char * argv[], CommandLineOptions & options)
|
||||
{
|
||||
for (int i = 1; i < argc; ++i)
|
||||
{
|
||||
std::string_view const arg = argv[i];
|
||||
if (arg.starts_with(kFilterOption))
|
||||
options.m_filterRegExp = argv[i] + sizeof(kFilterOption) - 1;
|
||||
if (arg.starts_with(kSuppressOption))
|
||||
options.m_suppressRegExp = argv[i] + sizeof(kSuppressOption) - 1;
|
||||
if (arg.starts_with(kDataPathOptions))
|
||||
options.m_dataPath = argv[i] + sizeof(kDataPathOptions) - 1;
|
||||
if (arg.starts_with(kResourcePathOptions))
|
||||
options.m_resourcePath = argv[i] + sizeof(kResourcePathOptions) - 1;
|
||||
if (arg == kHelpOption)
|
||||
options.m_help = true;
|
||||
if (arg == kListAllTestsOption)
|
||||
options.m_listTests = true;
|
||||
}
|
||||
#ifndef OMIM_UNIT_TEST_DISABLE_PLATFORM_INIT
|
||||
// Setting stored paths from testingmain.cpp
|
||||
Platform & pl = GetPlatform();
|
||||
if (options.m_dataPath)
|
||||
pl.SetWritableDirForTests(options.m_dataPath);
|
||||
if (options.m_resourcePath)
|
||||
{
|
||||
pl.SetResourceDir(options.m_resourcePath);
|
||||
pl.SetSettingsDir(options.m_resourcePath);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
CommandLineOptions const & GetTestingOptions()
|
||||
{
|
||||
return g_testingOptions;
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
#if defined(OMIM_UNIT_TEST_WITH_QT_EVENT_LOOP) && !defined(OMIM_OS_IPHONE)
|
||||
QAPP theApp(argc, argv);
|
||||
UNUSED_VALUE(theApp);
|
||||
#else
|
||||
UNUSED_VALUE(argc);
|
||||
UNUSED_VALUE(argv);
|
||||
#endif
|
||||
|
||||
base::ScopedLogLevelChanger const infoLogLevel(LINFO);
|
||||
#if defined(OMIM_OS_DESKTOP) || defined(OMIM_OS_IPHONE)
|
||||
base::SetLogMessageFn(base::LogMessageTests);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_GL_MOCK
|
||||
emul::GLMockFunctions::Init(&argc, argv);
|
||||
SCOPE_GUARD(GLMockScope, bind(&emul::GLMockFunctions::Teardown));
|
||||
#endif
|
||||
|
||||
vector<string> testnames;
|
||||
vector<bool> testResults;
|
||||
int numFailedTests = 0;
|
||||
|
||||
ParseOptions(argc, argv, g_testingOptions);
|
||||
if (g_testingOptions.m_help)
|
||||
{
|
||||
Usage(argv[0]);
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
regex filterRegExp;
|
||||
if (g_testingOptions.m_filterRegExp)
|
||||
filterRegExp.assign(g_testingOptions.m_filterRegExp);
|
||||
|
||||
regex suppressRegExp;
|
||||
if (g_testingOptions.m_suppressRegExp)
|
||||
suppressRegExp.assign(g_testingOptions.m_suppressRegExp);
|
||||
|
||||
for (TestRegister * test = TestRegister::FirstRegister(); test; test = test->m_next)
|
||||
{
|
||||
string filename(test->m_filename);
|
||||
string testname(test->m_testname);
|
||||
|
||||
// Retrieve fine file name.
|
||||
auto const lastSlash = filename.find_last_of("\\/");
|
||||
if (lastSlash != string::npos)
|
||||
filename.erase(0, lastSlash + 1);
|
||||
|
||||
testnames.push_back(filename + "::" + testname);
|
||||
testResults.push_back(true);
|
||||
}
|
||||
|
||||
if (GetTestingOptions().m_listTests)
|
||||
{
|
||||
for (auto const & name : testnames)
|
||||
cout << name << '\n';
|
||||
return 0;
|
||||
}
|
||||
|
||||
int testIndex = 0;
|
||||
for (TestRegister * test = TestRegister::FirstRegister(); test; ++testIndex, test = test->m_next)
|
||||
{
|
||||
auto const & testname = testnames[testIndex];
|
||||
if (g_testingOptions.m_filterRegExp && !regex_search(testname.begin(), testname.end(), filterRegExp))
|
||||
continue;
|
||||
if (g_testingOptions.m_suppressRegExp && regex_search(testname.begin(), testname.end(), suppressRegExp))
|
||||
continue;
|
||||
|
||||
LOG(LINFO, ("Running", testname));
|
||||
if (!g_lastTestOK)
|
||||
{
|
||||
// Somewhere else global variables have been reset.
|
||||
LOG(LERROR, ("\n\nSOMETHING IS REALLY WRONG IN THE UNIT TEST FRAMEWORK!!!"));
|
||||
return STATUS_BROKEN_FRAMEWORK;
|
||||
}
|
||||
|
||||
base::HighResTimer timer(true);
|
||||
|
||||
try
|
||||
{
|
||||
// Run the test.
|
||||
test->m_fn();
|
||||
#ifdef WITH_GL_MOCK
|
||||
emul::GLMockFunctions::ValidateAndClear();
|
||||
#endif
|
||||
|
||||
if (g_lastTestOK)
|
||||
{
|
||||
LOG(LINFO, ("OK"));
|
||||
}
|
||||
else
|
||||
{
|
||||
testResults[testIndex] = false;
|
||||
++numFailedTests;
|
||||
}
|
||||
}
|
||||
catch (TestFailureException const &)
|
||||
{
|
||||
testResults[testIndex] = false;
|
||||
++numFailedTests;
|
||||
}
|
||||
catch (exception const & ex)
|
||||
{
|
||||
LOG(LERROR, ("FAILED", "<<<Exception thrown [", ex.what(), "].>>>"));
|
||||
testResults[testIndex] = false;
|
||||
++numFailedTests;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
LOG(LERROR, ("FAILED<<<Unknown exception thrown.>>>"));
|
||||
testResults[testIndex] = false;
|
||||
++numFailedTests;
|
||||
}
|
||||
g_lastTestOK = true;
|
||||
|
||||
uint64_t const elapsed = timer.ElapsedNanoseconds();
|
||||
LOG(LINFO, ("Test took", elapsed / 1000000, "ms\n"));
|
||||
}
|
||||
|
||||
if (numFailedTests != 0)
|
||||
{
|
||||
LOG(LINFO, (numFailedTests, " tests failed:"));
|
||||
for (size_t i = 0; i < testnames.size(); ++i)
|
||||
if (!testResults[i])
|
||||
LOG(LINFO, (testnames[i]));
|
||||
LOG(LINFO, ("Some tests FAILED."));
|
||||
return STATUS_FAILED;
|
||||
}
|
||||
|
||||
LOG(LINFO, ("All tests passed."));
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
} // namespace testing
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
return ::testing::main(argc, argv);
|
||||
}
|
||||
45
libs/testing/testregister.hpp
Normal file
45
libs/testing/testregister.hpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
class TestRegister
|
||||
{
|
||||
public:
|
||||
TestRegister(char const * testname, char const * filename, std::function<void()> && fnTest)
|
||||
: m_testname(testname)
|
||||
, m_filename(filename)
|
||||
, m_fn(std::move(fnTest))
|
||||
, m_next(nullptr)
|
||||
{
|
||||
if (FirstRegister() == nullptr)
|
||||
{
|
||||
FirstRegister() = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
TestRegister * last = FirstRegister();
|
||||
while (last->m_next != nullptr)
|
||||
last = last->m_next;
|
||||
last->m_next = this;
|
||||
}
|
||||
}
|
||||
|
||||
static TestRegister *& FirstRegister()
|
||||
{
|
||||
static TestRegister * test = nullptr;
|
||||
return test;
|
||||
}
|
||||
|
||||
// Test name.
|
||||
char const * m_testname;
|
||||
|
||||
// File name.
|
||||
char const * m_filename;
|
||||
|
||||
// Function to run test.
|
||||
std::function<void()> m_fn;
|
||||
|
||||
// Next test in chain.
|
||||
TestRegister * m_next;
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue