#ifndef WIBBLE_TESTS_H #define WIBBLE_TESTS_H /** * @file test-utils.h * @author Enrico Zini , Peter Rockai (mornfall) * @brief Utility functions for the unit tests * * Copyright (C) 2006--2007 Peter Rockai (mornfall) * Copyright (C) 2003--2013 Enrico Zini */ #include #include #include #include namespace wibble { namespace tests { struct Location; struct LocationInfo; } } /* * These global arguments will be shadowed by local variables in functions that * implement tests. * * They are here to act as default root nodes to fulfill method signatures when * tests are called from outside other tests. */ extern const wibble::tests::Location wibble_test_location; extern const wibble::tests::LocationInfo wibble_test_location_info; #define TESTGRP(name) \ typedef test_group tg; \ typedef tg::object to; \ tg name ## _tg (#name); namespace wibble { namespace tests { #define WIBBLE_TESTS_ALWAYS_THROWS __attribute__ ((noreturn)) struct Location { const Location* parent; const wibble::tests::LocationInfo* info; const char* file; int line; const char* args; Location(const Location* parent, const wibble::tests::LocationInfo& info, const char* file, int line, const char* args); public: Location(); // legacy Location(const char* file, int line, const char* args); // legacy Location(const Location& parent, const char* file, int line, const char* args); Location nest(const wibble::tests::LocationInfo& info, const char* file, int line, const char* args=0) const; std::string locstr() const; std::string msg(const std::string m) const; void fail_test(const std::string& msg) const WIBBLE_TESTS_ALWAYS_THROWS; void fail_test(const wibble::tests::LocationInfo& info, const char* file, int line, const char* args, const std::string& msg) const WIBBLE_TESTS_ALWAYS_THROWS; void backtrace(std::ostream& out) const; }; struct LocationInfo : public std::stringstream { /** * Clear the stringstream and return self. * * Example usage: * * test_function(...) * { * WIBBLE_TEST_INFO(info); * for (unsigned i = 0; i < 10; ++i) * { * info() << "Iteration #" << i; * ... * } * } */ std::ostream& operator()(); LocationInfo() {} }; #define WIBBLE_TEST_LOCPRM wibble::tests::Location wibble_test_location /// Use this to declare a local variable with the given name that will be /// picked up by tests as extra local info #define WIBBLE_TEST_INFO(name) \ wibble::tests::LocationInfo wibble_test_location_info; \ wibble::tests::LocationInfo& name = wibble_test_location_info #define ensure(x) wibble::tests::impl_ensure(wibble::tests::Location(__FILE__, __LINE__, #x), (x)) #define inner_ensure(x) wibble::tests::impl_ensure(wibble::tests::Location(loc, __FILE__, __LINE__, #x), (x)) void impl_ensure(const Location& loc, bool res); #define ensure_equals(x, y) wibble::tests::impl_ensure_equals(wibble::tests::Location(__FILE__, __LINE__, #x " == " #y), (x), (y)) #define inner_ensure_equals(x, y) wibble::tests::impl_ensure_equals(wibble::tests::Location(loc, __FILE__, __LINE__, #x " == " #y), (x), (y)) template void impl_ensure_equals(const Location& loc, const Actual& actual, const Expected& expected) { if( expected != actual ) { std::stringstream ss; ss << "expected '" << expected << "' actual '" << actual << "'"; loc.fail_test(ss.str()); } } #define ensure_similar(x, y, prec) wibble::tests::impl_ensure_similar(wibble::tests::Location(__FILE__, __LINE__, #x " == " #y), (x), (y), (prec)) #define inner_ensure_similar(x, y, prec) wibble::tests::impl_ensure_similar(wibble::tests::Location(loc, __FILE__, __LINE__, #x " == " #y), (x), (y), (prec)) template void impl_ensure_similar(const Location& loc, const Actual& actual, const Expected& expected, const Precision& precision) { if( actual < expected - precision || expected + precision < actual ) { std::stringstream ss; ss << "expected '" << expected << "' actual '" << actual << "'"; loc.fail_test(ss.str()); } } #define ensure_contains(x, y) wibble::tests::impl_ensure_contains(wibble::tests::Location(__FILE__, __LINE__, #x " == " #y), (x), (y)) #define inner_ensure_contains(x, y) wibblwibblempl_ensure_contains(wibble::tests::Location(loc, __FILE__, __LINE__, #x " == " #y), (x), (y)) void impl_ensure_contains(const wibble::tests::Location& loc, const std::string& haystack, const std::string& needle); #define ensure_not_contains(x, y) wibble::tests::impl_ensure_not_contains(wibble::tests::Location(__FILE__, __LINE__, #x " == " #y), (x), (y)) #define inner_ensure_not_contains(x, y) wibble::tests::impl_ensure_not_contains(wibble::tests::Location(loc, __FILE__, __LINE__, #x " == " #y), (x), (y)) void impl_ensure_not_contains(const wibble::tests::Location& loc, const std::string& haystack, const std::string& needle); template struct TestBool { const A& actual; bool inverted; TestBool(const A& actual, bool inverted=false) : actual(actual), inverted(inverted) {} TestBool operator!() { return TestBool(actual, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual) return; wibble_test_location.fail_test("actual value is false"); } else { if (!actual) return; wibble_test_location.fail_test("actual value is true"); } } }; template struct TestEquals { A actual; E expected; bool inverted; TestEquals(const A& actual, const E& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestEquals operator!() { return TestEquals(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual == expected) return; std::stringstream ss; ss << "value '" << actual << "' is different than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (actual != expected) return; std::stringstream ss; ss << "value '" << actual << "' is not different than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } }; template struct TestIsLt { A actual; E expected; bool inverted; TestIsLt(const A& actual, const E& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestIsLt operator!() { return TestIsLt(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual < expected) return; std::stringstream ss; ss << "value '" << actual << "' is not less than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!(actual < expected)) return; std::stringstream ss; ss << "value '" << actual << "' is less than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } }; template struct TestIsLte { A actual; E expected; bool inverted; TestIsLte(const A& actual, const E& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestIsLte operator!() { return TestIsLte(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual <= expected) return; std::stringstream ss; ss << "value '" << actual << "' is not less than or equals to the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!(actual <= expected)) return; std::stringstream ss; ss << "value '" << actual << "' is less than or equals to the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } }; template struct TestIsGt { A actual; E expected; bool inverted; TestIsGt(const A& actual, const E& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestIsGt operator!() { return TestIsGt(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual > expected) return; std::stringstream ss; ss << "value '" << actual << "' is not greater than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!(actual > expected)) return; std::stringstream ss; ss << "value '" << actual << "' is greater than the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } }; template struct TestIsGte { A actual; E expected; bool inverted; TestIsGte(const A& actual, const E& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestIsGte operator!() { return TestIsGte(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const { if (!inverted) { if (actual >= expected) return; std::stringstream ss; ss << "value '" << actual << "' is not greater than or equals to the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } else { if (!(actual >= expected)) return; std::stringstream ss; ss << "value '" << actual << "' is greater than or equals to the expected '" << expected << "'"; wibble_test_location.fail_test(ss.str()); } } }; struct TestStartsWith { std::string actual; std::string expected; bool inverted; TestStartsWith(const std::string& actual, const std::string& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestStartsWith operator!() { return TestStartsWith(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const; }; struct TestEndsWith { std::string actual; std::string expected; bool inverted; TestEndsWith(const std::string& actual, const std::string& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestEndsWith operator!() { return TestEndsWith(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const; }; struct TestContains { std::string actual; std::string expected; bool inverted; TestContains(const std::string& actual, const std::string& expected, bool inverted=false) : actual(actual), expected(expected), inverted(inverted) {} TestContains operator!() { return TestContains(actual, expected, !inverted); } void check(WIBBLE_TEST_LOCPRM) const; }; struct TestRegexp { std::string actual; std::string regexp; bool inverted; TestRegexp(const std::string& actual, const std::string& regexp, bool inverted=false) : actual(actual), regexp(regexp), inverted(inverted) {} TestRegexp operator!() { return TestRegexp(actual, regexp, !inverted); } void check(WIBBLE_TEST_LOCPRM) const; }; struct TestFileExists { std::string pathname; bool inverted; TestFileExists(const std::string& pathname, bool inverted=false) : pathname(pathname), inverted(inverted) {} TestFileExists operator!() { return TestFileExists(pathname, !inverted); } void check(WIBBLE_TEST_LOCPRM) const; }; template struct Actual { A actual; Actual(const A& actual) : actual(actual) {} ~Actual() {} template TestEquals operator==(const E& expected) const { return TestEquals(actual, expected); } template TestEquals operator!=(const E& expected) const { return !TestEquals(actual, expected); } template TestIsLt operator<(const E& expected) const { return TestIsLt(actual, expected); } template TestIsLte operator<=(const E& expected) const { return TestIsLte(actual, expected); } template TestIsGt operator>(const E& expected) const { return TestIsGt(actual, expected); } template TestIsGte operator>=(const E& expected) const { return TestIsGte(actual, expected); } TestBool istrue() const { return TestBool(actual); } TestBool isfalse() const { return TestBool(actual, true); } }; struct ActualString : public Actual { ActualString(const std::string& s) : Actual(s) {} TestEquals operator==(const std::string& expected) const { return TestEquals(actual, expected); } TestEquals operator!=(const std::string& expected) const { return !TestEquals(actual, expected); } TestIsLt operator<(const std::string& expected) const { return TestIsLt(actual, expected); } TestIsLte operator<=(const std::string& expected) const { return TestIsLte(actual, expected); } TestIsGt operator>(const std::string& expected) const { return TestIsGt(actual, expected); } TestIsGte operator>=(const std::string& expected) const { return TestIsGte(actual, expected); } TestStartsWith startswith(const std::string& expected) const { return TestStartsWith(actual, expected); } TestEndsWith endswith(const std::string& expected) const { return TestEndsWith(actual, expected); } TestContains contains(const std::string& expected) const { return TestContains(actual, expected); } TestRegexp matches(const std::string& regexp) const { return TestRegexp(actual, regexp); } TestFileExists fileexists() const { return TestFileExists(actual); } }; template inline Actual actual(const A& actual) { return Actual(actual); } inline ActualString actual(const std::string& actual) { return ActualString(actual); } inline ActualString actual(const char* actual) { return ActualString(actual ? actual : ""); } inline ActualString actual(char* actual) { return ActualString(actual ? actual : ""); } /* template void _wassert(WIBBLE_TEST_LOCPRM, T& a, P& op) { op.invoke(wibble_test_location, a); } */ template static inline void _wassert(WIBBLE_TEST_LOCPRM, const T& expr) { expr.check(wibble_test_location); } #define wibble_test_runner(loc, func, ...) \ do { try { \ func(loc, ##__VA_ARGS__); \ } catch (tut::failure) { \ throw; \ } catch (std::exception& e) { \ loc.fail_test(e.what()); \ } } while(0) #define wrunchecked(func) \ do { try { \ func; \ } catch (tut::failure) { \ throw; \ } catch (std::exception& e) { \ wibble_test_location.fail_test(wibble_test_location_info, __FILE__, __LINE__, #func, e.what()); \ } } while(0) // function test, just runs the function without mangling its name #define wruntest(test, ...) wibble_test_runner(wibble_test_location.nest(wibble_test_location_info, __FILE__, __LINE__, "function: " #test "(" #__VA_ARGS__ ")"), test, ##__VA_ARGS__) #define wassert(...) wibble_test_runner(wibble_test_location.nest(wibble_test_location_info, __FILE__, __LINE__, #__VA_ARGS__), _wassert, ##__VA_ARGS__) } } #endif