1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
// ©2013-2014 Cameron Desrochers.
// Distributed under the simplified BSD license (see the LICENSE file that
// should have come with this header).
// Provides an extremely basic unit testing framework.
#pragma once
#include <cstdio>
#include <string>
#include <map>
#include <vector>
#include <type_traits>
#include <typeinfo>
#ifdef __GNUG__
#include <cxxabi.h>
#include <cstdlib>
#endif
#define REGISTER_TEST(testName) registerTest(#testName, &subclass_t::testName)
#define ASSERT_OR_FAIL(expr) { if (!(expr)) { notifyTestFailed(__LINE__, #expr); return false; } }
#define SUCCEED() { return true; }
// Uses CRTP
template<typename TSubclass>
class TestClass
{
public:
static void notifyTestFailed(int line, const char* expr)
{
std::printf(" FAILED!\n ******* Assertion failed (line %d): %s\n\n", line, expr);
}
bool validateTestName(std::string const& which) const
{
return testMap.find(which) != testMap.end();
}
void getAllTestNames(std::vector<std::string>& names) const
{
for (auto it = testMap.cbegin(); it != testMap.cend(); ++it) {
names.push_back(it->first);
}
}
bool run(unsigned int iterations = 1)
{
bool success = true;
for (auto it = testVec.cbegin(); it != testVec.cend(); ++it) {
if (!execTest(*it, iterations)) {
success = false;
}
}
return success;
}
bool run(std::vector<std::string> const& which, unsigned int iterations = 1)
{
bool success = true;
for (auto it = which.begin(); it != which.end(); ++it) {
if (!execTest(*testMap.find(*it), iterations)) {
success = false;
}
}
return success;
}
protected:
typedef TSubclass subclass_t;
void registerTest(const char* name, bool (subclass_t::* method)())
{
testVec.push_back(std::make_pair(std::string(name), method));
testMap[std::string(name)] = method;
}
virtual bool preTest() { return true; }
virtual bool postTest(bool) { return true; }
bool execTest(std::pair<std::string, bool (subclass_t::*)()> const& testRef, unsigned int iterations)
{
std::printf("%s::%s... \n", demangle_type_name(typeid(subclass_t).name()).c_str(), testRef.first.c_str());
bool result = true;
for (unsigned int i = 0; result && i != iterations; ++i) {
result = preTest();
try {
result = result && (static_cast<subclass_t*>(this)->*testRef.second)();
}
catch (...) {
std::printf(" FAILED!\n ******* Unhandled exception thrown\n\n");
result = false;
}
result = postTest(result) && result;
}
if (result) {
std::printf(" passed\n\n");
}
return result;
}
private:
static std::string demangle_type_name(const char* name)
{
#ifdef __GNUG__
// Adapted from http://stackoverflow.com/a/4541470/21475
int status = -4;
char* res = abi::__cxa_demangle(name, nullptr, nullptr, &status);
const char* const demangled_name = (status == 0) ? res : name;
std::string ret(demangled_name);
std::free(res);
return ret;
#else
return name;
#endif
}
protected:
std::vector<std::pair<std::string, bool (TSubclass::*)()> > testVec;
std::map<std::string, bool (TSubclass::*)()> testMap;
};
|