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
|
// ©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;
}
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; i != iterations; ++i) {
if (!(static_cast<subclass_t*>(this)->*testRef.second)()) {
result = false;
break;
}
}
if (result) {
std::printf(" passed\n\n");
}
else {
std::printf(" FAILED!\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;
};
|