File: minitest.h

package info (click to toggle)
concurrentqueue 1.0.3%2Bds-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 2,648 kB
  • sloc: cpp: 37,303; makefile: 88; ansic: 67; python: 46; sh: 18
file content (130 lines) | stat: -rw-r--r-- 3,109 bytes parent folder | download | duplicates (3)
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;
};