ifndef unittest__hpp_ define unittest__hpp_
! \file \brief A (very) simple unit test framework.
if defined(_MSC_VER) define NOMINMAX endif
include <vector> include <string> include <stdexcept> include <sstream> include <iostream>
if !defined(_MSC_VER) using namespace std; endif
class Failure { public:
Failure(
std::string const & test_suite_name,
std::string const & test_case_name,
std::string what)
: test_suite_name_(test_suite_name)
, test_case_name_(test_case_name)
, what_(what)
{
}
friend std::ostream & operator<<(std::ostream & out, Failure const & failure);
private:
std::string test_suite_name_; std::string test_case_name_; std::string what_;
};
class Test_Result { public:
void add_failure(Failure const & failure)
{
failures_.push_back(failure);
}
void add_error(Failure const & failure)
{
errors_.push_back(failure);
}
std::vector<Failure> const & failures() const
{
return failures_;
}
std::vector<Failure> const & errors() const
{
return errors_;
}
private:
std::vector<Failure> failures_; std::vector<Failure> errors_;
};
class Test_Case { public:
typedef void (*Func)();
Test_Case(
std::string const & name,
Func f)
: name_(name)
, f_(f)
{
}
void run()
{
f_();
}
std::string const & name() const { return name_; }
size_t size() const { return 1; }
private:
std::string name_; Func f_;
};
class Test_Suite { public:
Test_Suite(std::string const & name = "");
void add_test_case(Test_Case const & test_case)
{
test_cases_.push_back(test_case);
}
void setup(void (*f)()) { setup_ = f; }
void teardown(void (*f)()) { teardown_ = f; }
void run(Test_Result & result);
std::string const & name() const { return name_; }
size_t size() const { return test_cases_.size(); }
private:
std::string name_; typedef std::vector<Test_Case> Test_Cases; Test_Cases test_cases_; void (*setup_)(); void (*teardown_)();
};
Test_Suite & test_suite(); void new_test_suite(std::string const & name);
class Assertion_Failed
: public std::runtime_error
{ public:
Assertion_Failed(std::string const & what)
: std::runtime_error(what)
{
}
};
// TODO: not sure how to append __LINE__ correctly here define UNIQUE_SUITE_NAME(prefix, name) \
prefix ## __ ## name \
define TESTSUITE(name) \
struct UNIQUE_SUITE_NAME(testsuite_append, name) \
{ \
UNIQUE_SUITE_NAME(testsuite_append, name)() \
{ \
new_test_suite(#name); \
} \
} UNIQUE_SUITE_NAME(testsuite_append__initializer, name)
define TESTCASE(name) \
static void UNIQUE_SUITE_NAME(test, name)(); \
\
namespace \
{ \
struct UNIQUE_SUITE_NAME(test_append, name) \
{ \
UNIQUE_SUITE_NAME(test_append, name)() \
{ \
test_suite().add_test_case( \
Test_Case(#name, & UNIQUE_SUITE_NAME(test, name))); \
} \
} UNIQUE_SUITE_NAME(test_append__initializer, name); \
} \
\
static void UNIQUE_SUITE_NAME(test, name)()
define TESTFIXTURE(name, type) \
static void UNIQUE_SUITE_NAME(fixture ## __ ## name, type)(); \
\
namespace \
{ \
struct UNIQUE_SUITE_NAME(fixture_append ## __ ## name, type) \
{ \
UNIQUE_SUITE_NAME(fixture_append ## __ ## name, type)() \
{ \
test_suite().type(UNIQUE_SUITE_NAME(fixture ## __ ## name, type)); \
} \
} UNIQUE_SUITE_NAME(fixture_append__initializer ## __ ## name, type); \
} \
\
static void UNIQUE_SUITE_NAME(fixture ## __ ## name, type)()
define SETUP(name) TESTFIXTURE(name, setup)
define TEARDOWN(name) TESTFIXTURE(name, teardown)
template<typename RHS_T, typename LHS_T> inline bool is_equal(RHS_T const & rhs, LHS_T const & lhs) {
return rhs == lhs;
}
inline bool is_equal(char const * lhs, char const * rhs) {
return std::string(lhs) == std::string(rhs);
}
inline bool is_equal(char * lhs, char const * rhs) {
return std::string(lhs) == std::string(rhs);
}
inline bool is_equal(char const * lhs, char * rhs) {
return std::string(lhs) == std::string(rhs);
}
template<typename T, typename U> inline bool is_not_equal(T const & t, U const & u) {
return !is_equal(t, u);
}
extern size_t assertions;
template<typename T, typename U> void assert_equal(
T const & t, U const & u, std::string const & s_t, std::string const & s_u, std::string const & file, size_t line)
{
if(!is_equal(t, u))
{
std::stringstream strm;
strm << "Assertion failed: "
<< s_t << " != " << s_u
<< " (" << t << " != " << u << ")"
<< " at " << file << ":" << line;
throw Assertion_Failed(strm.str());
}
}
template<typename T, typename U> void assert_not_equal(
T const & t, U const & u, std::string const & s_t, std::string const & s_u, std::string const & file, size_t line)
{
if(!is_not_equal(t, u))
{
std::stringstream strm;
strm << "Assertion failed: "
<< s_t << " should != " << s_u
<< " (" << t << " should != " << u << ")"
<< " at " << file << ":" << line;
throw Assertion_Failed(strm.str());
}
}
define ASSERT_EQUAL(x, y) \
do \
{ \
++assertions; \
assert_equal((x), (y), #x, #y, __FILE__, __LINE__); \
} while(0)
define ASSERT_NOT_EQUAL(x, y) \
do \
{ \
++assertions; \
assert_not_equal((x), (y), #x, #y, __FILE__, __LINE__); \
} while(0)
define ASSERT(x) \
ASSERT_EQUAL(true, !!x);
define ASSERT_EXCEPTION_CHECK(type, code, check_exception) \
try \
{ \
++assertions; \
code; \
ASSERT(!"Expected exception"); \
} \
catch(type const & ex) \
{ \
check_exception; \
}
define ASSERT_EXCEPTION(type, code) \
ASSERT_EXCEPTION_CHECK(type, code, )
endif