diff options
author | Alex Zolotarev <deathbaba@gmail.com> | 2010-12-05 19:24:16 +0300 |
---|---|---|
committer | Alex Zolotarev <alex@maps.me> | 2015-09-22 22:33:57 +0300 |
commit | d6e12b7ce4bcbf0ccd1c07eb25de143422913c34 (patch) | |
tree | a7e910c330ce4da9b4f2d8be76067adece2561c4 /testing |
One Month In Minsk. Made in Belarus.
Diffstat (limited to 'testing')
-rw-r--r-- | testing/benchmark.hpp | 56 | ||||
-rw-r--r-- | testing/on_test_fail.hpp | 13 | ||||
-rw-r--r-- | testing/testing.config | 1 | ||||
-rw-r--r-- | testing/testing.creator | 1 | ||||
-rw-r--r-- | testing/testing.files | 4 | ||||
-rw-r--r-- | testing/testing.hpp | 60 | ||||
-rw-r--r-- | testing/testing.includes | 0 | ||||
-rw-r--r-- | testing/testingmain.cpp | 98 | ||||
-rw-r--r-- | testing/testregister.hpp | 36 |
9 files changed, 269 insertions, 0 deletions
diff --git a/testing/benchmark.hpp b/testing/benchmark.hpp new file mode 100644 index 0000000000..b7c8972967 --- /dev/null +++ b/testing/benchmark.hpp @@ -0,0 +1,56 @@ +#pragma once +#include "testing.hpp" +#include "testregister.hpp" +#include "../base/timer.hpp" +#include "../std/iostream.hpp" + +namespace my +{ + 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)); + cout << secondsElapsed << "s total"; + if (secondsElapsed > 0) + { + cout << ", " << static_cast<int>(m_RepeatCount / secondsElapsed) << "/s, "; + /* + if (secondsElapsed / m_RepeatCount * 1000 >= 10) + cout << static_cast<int>(secondsElapsed / m_RepeatCount * 1000) << "ms each"; + else */ if (secondsElapsed / m_RepeatCount * 1000000 >= 10) + cout << static_cast<int>(secondsElapsed / m_RepeatCount * 1000000) << "us each"; + else + cout << static_cast<int>(secondsElapsed / m_RepeatCount * 1000000000) << "ns each"; + } + cout << " ..."; + } + + inline int Iteration() const { return m_Iteration; } + inline bool ContinueIterating() const { return m_Iteration < m_RepeatCount; } + inline void NextIteration() { ++m_Iteration; } + + private: + int const m_RepeatCount; + double const m_MaxSecondsToSucceed; + int m_Iteration; + Timer m_Timer; + }; +} + +#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 (::my::BenchmarkNTimes benchmark(times, maxTimeToSucceed); \ + benchmark.ContinueIterating(); benchmark.NextIteration()) diff --git a/testing/on_test_fail.hpp b/testing/on_test_fail.hpp new file mode 100644 index 0000000000..1854e2c95f --- /dev/null +++ b/testing/on_test_fail.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include <string> + +class OnTestFailImpl; + +class OnTestFail +{ + OnTestFailImpl * m_pImpl; +public: + OnTestFail(bool ok, char const * check, char const * file, int line); + void operator () (std::wstring const & s) const; +}; diff --git a/testing/testing.config b/testing/testing.config new file mode 100644 index 0000000000..8cec188b38 --- /dev/null +++ b/testing/testing.config @@ -0,0 +1 @@ +// ADD PREDEFINED MACROS HERE! diff --git a/testing/testing.creator b/testing/testing.creator new file mode 100644 index 0000000000..e94cbbd302 --- /dev/null +++ b/testing/testing.creator @@ -0,0 +1 @@ +[General] diff --git a/testing/testing.files b/testing/testing.files new file mode 100644 index 0000000000..283ba17402 --- /dev/null +++ b/testing/testing.files @@ -0,0 +1,4 @@ +on_test_fail.hpp +testing.hpp +testingmain.cpp +testregister.hpp
\ No newline at end of file diff --git a/testing/testing.hpp b/testing/testing.hpp new file mode 100644 index 0000000000..9289840beb --- /dev/null +++ b/testing/testing.hpp @@ -0,0 +1,60 @@ +#pragma once +#include "testregister.hpp" +#include "../base/assert.hpp" +#include "../base/base.hpp" +#include "../base/exception.hpp" +#include "../base/math.hpp" +#include "../base/src_point.hpp" +#include "../std/iostream.hpp" +#include "../std/string.hpp" + +#define UNIT_TEST(name) \ + void UnitTest_##name(); \ + TestRegister g_TestRegister_##name(#name, __FILE__, &UnitTest_##name); \ + void UnitTest_##name() + +DECLARE_EXCEPTION(TestFailureException, RootException); + +namespace my +{ + inline void OnTestFailed(SrcPoint const & srcPoint, string const & msg) + { + cerr << "FAILED" << endl << srcPoint.FileName() << ":" << srcPoint.Line() << " " << msg; + MYTHROW(TestFailureException, (srcPoint.FileName(), srcPoint.Line(), msg)); + } +} + +#define TEST(X, msg) if (X) {} else { \ + ::my::OnTestFailed(SRC(), ::my::impl::MergeMsg("TEST("#X")", ::my::impl::Message msg));} +#define TEST_EQUAL(X, Y, msg) if ((X) == (Y)) {} else { \ + ::my::OnTestFailed(SRC(), ::my::impl::MergeMsg("TEST("#X" == "#Y")", \ + ::my::impl::Message(X, Y), \ + ::my::impl::Message msg));} +#define TEST_NOT_EQUAL(X, Y, msg) if ((X) != (Y)) {} else { \ + ::my::OnTestFailed(SRC(), ::my::impl::MergeMsg("TEST("#X" != "#Y")", \ + ::my::impl::Message(X, Y), \ + ::my::impl::Message msg));} +#define TEST_LESS(X, Y, msg) if ((X) < (Y)) {} else { \ + ::my::OnTestFailed(SRC(), ::my::impl::MergeMsg("TEST("#X" < "#Y")", \ + ::my::impl::Message(X, Y), \ + ::my::impl::Message msg));} +#define TEST_LESS_OR_EQUAL(X, Y, msg) if ((X) <= (Y)) {} else { \ + ::my::OnTestFailed(SRC(), ::my::impl::MergeMsg("TEST("#X" <= "#Y")", \ + ::my::impl::Message(X, Y), \ + ::my::impl::Message msg));} +#define TEST_GREATER(X, Y, msg) if ((X) > (Y)) {} else { \ + ::my::OnTestFailed(SRC(), ::my::impl::MergeMsg("TEST("#X" > "#Y")", \ + ::my::impl::Message(X, Y), \ + ::my::impl::Message msg));} +#define TEST_GREATER_OR_EQUAL(X, Y, msg) if ((X) >= (Y)) {} else { \ + ::my::OnTestFailed(SRC(), ::my::impl::MergeMsg("TEST("#X" >= "#Y")", \ + ::my::impl::Message(X, Y), \ + ::my::impl::Message msg));} +#define TEST_ALMOST_EQUAL(X, Y, msg) if (::my::AlmostEqual(X, Y)) {} else { \ + ::my::OnTestFailed(SRC(), ::my::impl::MergeMsg("TEST(my::AlmostEqual("#X", "#Y")", \ + ::my::impl::Message(X, Y), \ + ::my::impl::Message msg));} +#define TEST_NOT_ALMOST_EQUAL(X, Y, msg) if (!::my::AlmostEqual(X, Y)) {} else { \ + ::my::OnTestFailed(SRC(), ::my::impl::MergeMsg("TEST(!my::AlmostEqual("#X", "#Y")", \ + ::my::impl::Message(X, Y), \ + ::my::impl::Message msg));} diff --git a/testing/testing.includes b/testing/testing.includes new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/testing/testing.includes diff --git a/testing/testingmain.cpp b/testing/testingmain.cpp new file mode 100644 index 0000000000..934da91019 --- /dev/null +++ b/testing/testingmain.cpp @@ -0,0 +1,98 @@ +#include "../base/SRC_FIRST.hpp" +#include "testregister.hpp" +#include "testing.hpp" +#include "../base/logging.hpp" +#include "../std/algorithm.hpp" +#include "../std/iostream.hpp" +#include "../std/string.hpp" +#include "../std/vector.hpp" + +static bool g_bLastTestOK = true; + +int main() +{ + my::g_LogLevel = LINFO; + + vector<string> testNames; + vector<bool> testResults; + int numFailedTests = 0; + + for (TestRegister * pTest = TestRegister::FirstRegister(); pTest; pTest = pTest->m_pNext) + { + string fileName(pTest->m_FileName); + string testName(pTest->m_TestName); + // Retrieve fine file name + { + int iFirstSlash = static_cast<int>(fileName.size()) - 1; + while (iFirstSlash >= 0 && fileName[iFirstSlash] != '\\' && fileName[iFirstSlash] != '/') + --iFirstSlash; + if (iFirstSlash >= 0) + fileName.erase(0, iFirstSlash + 1); + } + + testNames.push_back(fileName + "::" + testName); + testResults.push_back(true); + } + + int iTest = 0; + for (TestRegister * pTest = TestRegister::FirstRegister(); pTest; ++iTest, pTest = pTest->m_pNext) + { + cerr << "Running " << testNames[iTest] << endl << flush; + if (!g_bLastTestOK) + { + // Somewhere else global variables have been reset. + cerr << "\n\nSOMETHING IS REALLY WRONG IN THE UNIT TEST FRAMEWORK!!!" << endl; + return 5; + } + try + { + // Run the test. + pTest->m_Fn(); + + if (g_bLastTestOK) + { + cerr << "OK" << endl; + } + else + { + // You can set Break here if test failed, + // but it is already set in OnTestFail - to fail immediately. + testResults[iTest] = false; + ++numFailedTests; + } + + } catch (TestFailureException const & ex) + { + testResults[iTest] = false; + ++numFailedTests; + } catch (std::exception const & ex) + { + cerr << "FAILED" << endl << "<<<Exception thrown [" << ex.what() << "].>>>" << endl; + testResults[iTest] = false; + ++numFailedTests; + } catch (...) + { + cerr << "FAILED" << endl << "<<<Unknown exception thrown.>>>" << endl; + testResults[iTest] = false; + ++numFailedTests; + } + g_bLastTestOK = true; + } + + if (numFailedTests == 0) + { + cerr << endl << "All tests passed." << endl << flush; + return 0; + } + else + { + cerr << endl << numFailedTests << " tests failed:" << endl; + for (size_t i = 0; i < testNames.size(); ++i) + { + if (!testResults[i]) + cerr << testNames[i] << endl; + } + cerr << "Some tests FAILED." << endl << flush; + return 1; + } +} diff --git a/testing/testregister.hpp b/testing/testregister.hpp new file mode 100644 index 0000000000..803413473a --- /dev/null +++ b/testing/testregister.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "../base/base.hpp" + +class TestRegister +{ +public: + TestRegister(char const * testName, char const * fileName, void (* fnTest)()) + : m_TestName(testName), m_FileName(fileName), m_Fn(fnTest), m_pNext(NULL) + { + if (FirstRegister() == NULL) + FirstRegister() = this; + else + { + TestRegister * pLast = FirstRegister(); + while (pLast->m_pNext) + pLast = pLast->m_pNext; + pLast->m_pNext = this; + } + } + + // Test name. + char const * m_TestName; + // File name. + char const * m_FileName; + // Function to run test. + void (* m_Fn)(); + // Next test in chain. + TestRegister * m_pNext; + + static TestRegister * & FirstRegister() + { + static TestRegister * s_pRegister = 0; + return s_pRegister; + } +}; |