View Usage Example | License
#ifndef TESTER_H
#define TESTER_H
#include <iostream>
#include <vector>
/*! Tester: A Micro Unit Testing Library for C++
See http://nathan.ca/code/tester for the original and more information.
*/
//! Tester encapsulate all this into a class for scoping convenience.
class Tester
{
public:
//! Base class for all test cases.
class ICase
{
protected:
bool verbose;
int passes, attempts;
virtual void test() = 0; // defined by the USER
public:
ICase() : verbose(false), passes(0), attempts(0) {}
virtual ~ICase() {}
// mandatory overrides to return the name and to run the test case
virtual const char *getName() const = 0;
virtual bool run(bool setverbose) = 0;
// getters
int getAttempts() const { return attempts; }
int getPassedAttempts() const { return passes; }
};
private:
// test cases to run (we are responsible for these)
std::vector<ICase*> tests;
public:
// add an ICase-derived test case (which we take responsibility for) and then run them all
void addCase(ICase *testcase) { tests.push_back(testcase); }
bool run(bool verbose)
{
int attempts = 0, passes = 0,
total = 0, successes = 0;
for(auto it = tests.begin(); it != tests.end(); it++)
{
ICase *testcase = *it;
std::cout << "Running " << testcase->getName() << "\n\n";
if (testcase->run(verbose))
successes++;
total++;
std::cout << "Finished running " << testcase->getName() << " "
<< testcase->getPassedAttempts() << " / " << testcase->getAttempts() << "\n";
attempts += testcase->getAttempts();
passes += testcase->getPassedAttempts();
}
//
std::cout << "\nTOTAL: " << successes << " / " << total << "\n";
// delete 'em all
while(tests.size() > 0)
{
ICase *t = tests.back();
tests.pop_back();
delete t;
}
return successes == total;
}
};
// defines the given class as an ICase derivitive and runs it
#define tester_addsuite(cl, name) \
namespace tests { class cl : public Tester::ICase { public: \
cl() : ICase() {} virtual ~cl() {} \
const char *getName() const { return name; } \
bool run(bool setverbose) { verbose = setverbose; test(); return passes == attempts; } \
void test(); \
}; }
// test whether the boolean `cond` succeeded on the pretense of `msg`.
#define tester_bool(cond, msg) do { \
bool res = cond; /* keep this as a variable so that it only runs ONCE */ \
std::cout << "\t[" << (res ? "PASS" : "FAIL") << "] " << msg; \
if (verbose) \
std::cout << ":\n\t\t" #cond << "\n"; \
std::cout << "\n"; \
attempts++; if (res) passes++; \
} while(0)
// test whether the `left` and `right` fulfill the `operand` given on the pretense of `msg`.
#define tester_cmp(left, operand, right, msg) do { \
auto lres = left; \
auto rres = right; \
bool res = lres operand rres; /* keep these as variables so that it only runs ONCE */ \
std::cout << "\t[" << (res ? "PASS" : "FAIL") << "] " << msg; \
if (verbose) \
std::cout << "\n\t\t" #left " " #operand " " #right "\n\t\t" \
<< lres << " " #operand " " << rres << "\n"; \
std::cout << "\n"; \
attempts++; if (res) passes++; \
} while(0)
// output the given variable for debugging purposes
#define tester_with(var) do { \
if (verbose) \
std::cout << "\tWith " << #var << " = " << var << "\n"; \
} while(0)
#endif