mirror of
https://github.com/libSDL2pp/libSDL2pp.git
synced 2025-08-03 10:55:57 -04:00
Update testing.h
This commit is contained in:
parent
b92804536c
commit
cced165fbd
372
tests/testing.h
372
tests/testing.h
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2011-2013 Dmitry Marakasov <amdmi3@amdmi3.ru>
|
||||
* Copyright (c) 2011-2014 Dmitry Marakasov <amdmi3@amdmi3.ru>
|
||||
* All rights reserved.
|
||||
*
|
||||
* See https://github.com/AMDmi3/testing.h for updates, bug reports,
|
||||
@ -32,109 +32,301 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
|
||||
// Define NO_TEST_COLOR before including testing.h to disable colors
|
||||
#ifndef NO_TEST_COLOR
|
||||
# define TEST_COLOR
|
||||
//
|
||||
// Helper class for literal quoting / extra processing
|
||||
//
|
||||
// You may instance this template for printing your own classes
|
||||
//
|
||||
template<class T>
|
||||
std::string QuoteLiteral(T value) {
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string QuoteLiteral<std::string>(std::string value) {
|
||||
return std::string("\"") + value + std::string("\"");
|
||||
}
|
||||
|
||||
template<>
|
||||
std::string QuoteLiteral<const char*>(const char* value) {
|
||||
return std::string("\"") + value + std::string("\"");
|
||||
}
|
||||
|
||||
//
|
||||
// Test registry
|
||||
//
|
||||
class Tester {
|
||||
private:
|
||||
int num_tests_; // Total numer of tests registered
|
||||
int num_failures_; // Number of tests failed
|
||||
bool use_color_; // Whether to use color
|
||||
|
||||
bool unexpected_exception_; // Whether unexpected exception was encountered
|
||||
|
||||
int permanent_flags_; // Flags which affect each test invocation
|
||||
|
||||
public:
|
||||
// Test results
|
||||
enum Result {
|
||||
PASS,
|
||||
FAIL,
|
||||
WARN,
|
||||
};
|
||||
|
||||
// Flags to control test bahavior
|
||||
enum Flags {
|
||||
NON_FATAL = 0x01,
|
||||
HIDE_MESSAGE = 0x02,
|
||||
};
|
||||
|
||||
// Styles for coloring
|
||||
enum Styles {
|
||||
PASS_BANNER,
|
||||
FAIL_BANNER,
|
||||
WARN_BANNER,
|
||||
LITERAL,
|
||||
EXPRESSION,
|
||||
DESCRIPTION,
|
||||
};
|
||||
|
||||
// Dummy type for preprocessor magic to happen
|
||||
struct DummyArgument {
|
||||
};
|
||||
|
||||
private:
|
||||
//
|
||||
// Apply named style to a string
|
||||
//
|
||||
std::string Stylify(Styles style, const std::string& str) const {
|
||||
if (!use_color_)
|
||||
return str;
|
||||
|
||||
bool bright = false;
|
||||
int color = 0;
|
||||
|
||||
switch (style) {
|
||||
case PASS_BANNER: bright = false; color = 2; break;
|
||||
case WARN_BANNER: bright = true; color = 3; break;
|
||||
case FAIL_BANNER: bright = true; color = 1; break;
|
||||
case LITERAL: bright = true; color = 5; break;
|
||||
case EXPRESSION: bright = true; color = 7; break;
|
||||
case DESCRIPTION: bright = true; color = 4; break;
|
||||
default:
|
||||
return str;
|
||||
}
|
||||
return std::string("\033[") + (bright ? "1" : "0") + ";" + std::to_string(30 + color) + "m" + str + "\033[0m";
|
||||
}
|
||||
|
||||
//
|
||||
// Main test handling routine
|
||||
//
|
||||
// This method handles test counters and formats messages
|
||||
//
|
||||
void RegisterTest(bool passed, int flags, const std::string& message, const std::string& description) {
|
||||
num_tests_++;
|
||||
|
||||
if (passed) {
|
||||
std::cerr << Stylify(PASS_BANNER, "PASS: ");
|
||||
} else if ((flags | permanent_flags_) & NON_FATAL) {
|
||||
std::cerr << Stylify(WARN_BANNER, "WARN: ");
|
||||
} else {
|
||||
std::cerr << Stylify(FAIL_BANNER, "FAIL: ");
|
||||
num_failures_++;
|
||||
}
|
||||
|
||||
if ((flags | permanent_flags_) & HIDE_MESSAGE && !description.empty()) {
|
||||
std::cerr << description;
|
||||
} else {
|
||||
std::cerr << message;
|
||||
if (!description.empty())
|
||||
std::cerr << " " << Stylify(DESCRIPTION, std::string("// ") + description);
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
public:
|
||||
Tester() : num_tests_(0), num_failures_(0), use_color_(true), unexpected_exception_(false), permanent_flags_(0) {
|
||||
#ifdef TESTING_NO_COLOR
|
||||
use_color_ = false;
|
||||
#endif
|
||||
|
||||
#define TEST_ESCAPE "\033"
|
||||
|
||||
#ifdef TEST_COLOR
|
||||
# define PASSED TEST_ESCAPE "[0;32mPASSED:" TEST_ESCAPE "[0m "
|
||||
# define FAILED TEST_ESCAPE "[1;31mFAILED:" TEST_ESCAPE "[0m "
|
||||
#else
|
||||
# define PASSED "PASSED: "
|
||||
# define FAILED "FAILED: "
|
||||
#endif
|
||||
|
||||
// Test begin/end
|
||||
#define BEGIN_TEST(...) int main(__VA_ARGS__) { int num_failing_tests_ = 0;
|
||||
|
||||
#define END_TEST() if (num_failing_tests_ > 0) std::cerr << num_failing_tests_ << " failures" << std::endl; return num_failing_tests_; }
|
||||
|
||||
// Equality checks
|
||||
#define EXPECT_TRUE(expr) { \
|
||||
if (!(expr)) { \
|
||||
std::cerr << FAILED #expr << std::endl; \
|
||||
++num_failing_tests_; \
|
||||
} else { \
|
||||
std::cerr << PASSED #expr << std::endl; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define EXPECT_STRING(expr, expected) { \
|
||||
std::string result = (expr); \
|
||||
if (result != expected) { \
|
||||
std::cerr << FAILED #expr " returned \"" << result << "\", while expected \"" << expected << "\"" << std::endl; \
|
||||
++num_failing_tests_; \
|
||||
} else { \
|
||||
std::cerr << PASSED #expr " == \"" << expected << "\"" << std::endl; \
|
||||
} \
|
||||
void EnableColor(bool enable) {
|
||||
use_color_ = enable;
|
||||
}
|
||||
|
||||
#define EXPECT_EQUAL(type, expr, expected) { \
|
||||
type result = (expr); \
|
||||
if (result != expected) { \
|
||||
std::cerr << FAILED #expr " returned " << result << ", while expected " << expected << std::endl; \
|
||||
++num_failing_tests_; \
|
||||
} else { \
|
||||
std::cerr << PASSED #expr " == " << expected << std::endl; \
|
||||
} \
|
||||
void EnableFlags(int flags) {
|
||||
permanent_flags_ |= flags;
|
||||
}
|
||||
|
||||
// Range checks
|
||||
#define EXPECT_VALUE_IN_RANGE(type, expr, from, to) { \
|
||||
type result = (expr); \
|
||||
if (from <= result && result <= to) { \
|
||||
std::cerr << PASSED #expr " returned " << result << ", which is in range [" << from << ", " << to << "] as expected" << std::endl; \
|
||||
} else { \
|
||||
std::cerr << FAILED #expr " returned " << result << ", which is out of expected range [" << from << ", " << to << "]" << std::endl; \
|
||||
++num_failing_tests_; \
|
||||
} \
|
||||
void DisableFlags(int flags) {
|
||||
permanent_flags_ &= ~flags;
|
||||
}
|
||||
|
||||
// Shortcuts for above; feel free to ask to add more
|
||||
#define EXPECT_INT(expr, expected) EXPECT_EQUAL(int, expr, expected)
|
||||
#define EXPECT_FLOAT_IN_RANGE(expr, from, to) EXPECT_VALUE_IN_RANGE(float, expr, from, to)
|
||||
void ResetFlags() {
|
||||
permanent_flags_ = 0;
|
||||
}
|
||||
|
||||
// Exception checks
|
||||
#define EXPECT_EXCEPTION(expr, exception) { \
|
||||
bool correct_catch = false; \
|
||||
try { \
|
||||
expr; \
|
||||
} catch (exception &e) { \
|
||||
correct_catch = true; \
|
||||
// ExpectTrue
|
||||
void ExpectTrue(const std::string& expression_str, bool sample, const std::string& description, int flags, DummyArgument) {
|
||||
RegisterTest(sample, flags, Stylify(EXPRESSION, expression_str), description);
|
||||
}
|
||||
|
||||
// ExpectEqual
|
||||
template<class T1, class T2>
|
||||
void ExpectEqual(const std::string& expression_str, const T1& sample, const T2& expected, const std::string& description, int flags, DummyArgument) {
|
||||
std::string message;
|
||||
|
||||
if (sample == expected)
|
||||
message = Stylify(EXPRESSION, expression_str) + " returned " + Stylify(LITERAL, QuoteLiteral(sample)) + " as expected";
|
||||
else
|
||||
message = Stylify(EXPRESSION, expression_str) + " returned " + Stylify(LITERAL, QuoteLiteral(sample)) + " while expected " + Stylify(LITERAL, QuoteLiteral(expected));
|
||||
|
||||
RegisterTest(sample == expected, flags, message, description);
|
||||
}
|
||||
|
||||
// ExpectException
|
||||
template<class E>
|
||||
void ExpectException(const std::string& expression_str, std::function<void()>&& func, const std::string& exception_str, const std::string& description, int flags, DummyArgument) {
|
||||
bool as_expected = false;
|
||||
bool thrown = false;
|
||||
bool std_exception = false;
|
||||
|
||||
std::string what;
|
||||
|
||||
try {
|
||||
func();
|
||||
} catch (E& e) {
|
||||
as_expected = true;
|
||||
thrown = true;
|
||||
} catch (std::exception& e) {
|
||||
thrown = true;
|
||||
std_exception = true;
|
||||
what = e.what();
|
||||
} catch (...) {
|
||||
thrown = true;
|
||||
}
|
||||
|
||||
std::string message;
|
||||
|
||||
if (thrown && as_expected)
|
||||
message = Stylify(EXPRESSION, expression_str) + " has thrown " + Stylify(EXPRESSION, exception_str) + " as expected";
|
||||
else if (thrown && std_exception)
|
||||
message = Stylify(EXPRESSION, expression_str) + " has thrown unexpected exception derived from std::exception (what(): " + Stylify(LITERAL, what) + "), while expected " + Stylify(EXPRESSION, exception_str);
|
||||
else if (thrown)
|
||||
message = Stylify(EXPRESSION, expression_str) + " has thrown unexpected exception not derived from std::exception, while expected " + Stylify(EXPRESSION, exception_str);
|
||||
else
|
||||
message = Stylify(EXPRESSION, expression_str) + " hasn't thrown expected exception " + Stylify(EXPRESSION, exception_str);
|
||||
|
||||
RegisterTest(thrown && as_expected, flags, message, description);
|
||||
}
|
||||
|
||||
// ExpectTrue overloads
|
||||
void ExpectTrue(const std::string& expression_str, bool sample, DummyArgument dummy) {
|
||||
ExpectTrue(expression_str, sample, "", 0, dummy);
|
||||
}
|
||||
|
||||
void ExpectTrue(const std::string& expression_str, bool sample, const std::string& description, DummyArgument dummy) {
|
||||
ExpectTrue(expression_str, sample, description, 0, dummy);
|
||||
}
|
||||
|
||||
void ExpectTrue(const std::string& expression_str, bool sample, int flags, DummyArgument dummy) {
|
||||
ExpectTrue(expression_str, sample, "", flags, dummy);
|
||||
}
|
||||
|
||||
// ExpectEqual overloads
|
||||
template<class T1, class T2>
|
||||
void ExpectEqual(const std::string& expression_str, const T1& sample, const T2& expected, DummyArgument dummy) {
|
||||
return ExpectEqual<T1, T2>(expression_str, sample, expected, "", 0, dummy);
|
||||
}
|
||||
|
||||
template<class T1, class T2>
|
||||
void ExpectEqual(const std::string& expression_str, const T1& sample, const T2& expected, const std::string& description, DummyArgument dummy) {
|
||||
return ExpectEqual<T1, T2>(expression_str, sample, expected, description, 0, dummy);
|
||||
}
|
||||
|
||||
template<class T1, class T2>
|
||||
void ExpectEqual(const std::string& expression_str, const T1& sample, const T2& expected, int flags, DummyArgument dummy) {
|
||||
return ExpectEqual<T1, T2>(expression_str, sample, expected, "", flags, dummy);
|
||||
}
|
||||
|
||||
// ExpectException overloads
|
||||
template<class E>
|
||||
void ExpectException(const std::string& expression_str, std::function<void()>&& func, const std::string& exception_str, DummyArgument dummy) {
|
||||
return ExpectException<E>(expression_str, std::move(func), exception_str, "", 0, dummy);
|
||||
}
|
||||
|
||||
template<class E>
|
||||
void ExpectException(const std::string& expression_str, std::function<void()>&& func, const std::string& exception_str, const std::string& description, DummyArgument dummy) {
|
||||
return ExpectException<E>(expression_str, std::move(func), exception_str, description, 0, dummy);
|
||||
}
|
||||
|
||||
template<class E>
|
||||
void ExpectException(const std::string& expression_str, std::function<void()>&& func, const std::string& exception_str, int flags, DummyArgument dummy) {
|
||||
return ExpectException<E>(expression_str, std::move(func), exception_str, "", flags, dummy);
|
||||
}
|
||||
|
||||
void RegisterUnexpectedException() {
|
||||
unexpected_exception_ = true;
|
||||
}
|
||||
|
||||
void PrintSummary() {
|
||||
std::cerr << num_failures_ << " failures out of " << num_tests_ << " tests";
|
||||
if (unexpected_exception_)
|
||||
std::cerr << ", and was terminated prematurely due to unexpected exception";
|
||||
std::cerr << "\n";
|
||||
}
|
||||
|
||||
int GetExitCode() {
|
||||
return num_failures_ || unexpected_exception_;
|
||||
}
|
||||
};
|
||||
|
||||
// core macros
|
||||
#define BEGIN_TEST(...) \
|
||||
int main(__VA_ARGS__) { \
|
||||
Tester tester_; \
|
||||
try {
|
||||
|
||||
#define HANDLE_EXCEPTION(exception) \
|
||||
} catch(exception) { \
|
||||
tester_.RegisterUnexpectedException();
|
||||
|
||||
#define END_TEST() \
|
||||
} catch(std::exception& e) { \
|
||||
tester_.RegisterUnexpectedException(); \
|
||||
std::cerr << "unexpected exception was thrown during the test: " << e.what() << std::endl; \
|
||||
} catch (...) { \
|
||||
tester_.RegisterUnexpectedException(); \
|
||||
std::cerr << "unexpected exception was thrown during the test" << std::endl; \
|
||||
} \
|
||||
if (correct_catch) { \
|
||||
std::cerr << PASSED #expr " has thrown " #exception << " as expected " << std::endl; \
|
||||
} else { \
|
||||
std::cerr << FAILED #expr " hasn't thrown expected " #exception << std::endl; \
|
||||
++num_failing_tests_; \
|
||||
} \
|
||||
tester_.PrintSummary(); \
|
||||
return tester_.GetExitCode(); \
|
||||
}
|
||||
|
||||
#define EXPECT_NO_EXCEPTION(expr) { \
|
||||
bool had_exception = false; \
|
||||
const char* what = NULL; \
|
||||
try { \
|
||||
expr; \
|
||||
} catch (std::exception& e) { \
|
||||
had_exception = true; \
|
||||
what = e.what(); \
|
||||
} catch (...) { \
|
||||
had_exception = true; \
|
||||
} \
|
||||
if (had_exception && what) { \
|
||||
std::cerr << FAILED #expr << " has thrown unexpected exception derived from std::exception, what() returned \"" << what << "\"" << std::endl; \
|
||||
++num_failing_tests_; \
|
||||
} else if (had_exception) { \
|
||||
std::cerr << FAILED #expr << " has thrown unexpected exception not derived from std::exception" << std::endl; \
|
||||
++num_failing_tests_; \
|
||||
} else { \
|
||||
std::cerr << PASSED #expr << " hasn't thrown any exceptions as expected" << std::endl; \
|
||||
} \
|
||||
}
|
||||
// wrappers to allow true variable number of arguments
|
||||
#define METHOD_WRAPPER(method, expr, ...) tester_.method(#expr, expr, __VA_ARGS__)
|
||||
#define METHOD_WRAPPER_LAMBDA(method, expr, ...) tester_.method(#expr, [&](){return expr;}, __VA_ARGS__)
|
||||
#define METHOD_WRAPPER_EXCEPTION(expr, exception, ...) tester_.ExpectException<exception>(#expr, [&](){expr;}, #exception, __VA_ARGS__)
|
||||
|
||||
// checks
|
||||
#define EXPECT_TRUE(...) do { METHOD_WRAPPER(ExpectTrue, __VA_ARGS__, Tester::DummyArgument()); } while(0)
|
||||
#define EXPECT_EQUAL(...) do { METHOD_WRAPPER(ExpectEqual, __VA_ARGS__, Tester::DummyArgument()); } while(0)
|
||||
#define EXPECT_EXCEPTION(...) do { METHOD_WRAPPER_EXCEPTION(__VA_ARGS__, Tester::DummyArgument()); } while(0)
|
||||
|
||||
// functions
|
||||
#define ENABLE_FLAGS(flags) do { tester_.EnableFlags(flags); } while(0)
|
||||
#define DISABLE_FLAGS(flags) do { tester_.DisableFlags(flags); } while(0)
|
||||
#define RESET_FLAGS() do { tester_.ResetFlags(); } while(0)
|
||||
|
||||
// flags
|
||||
#define NON_FATAL Tester::NON_FATAL
|
||||
#define HIDE_MESSAGE Tester::HIDE_MESSAGE
|
||||
|
||||
#endif // TESTING_H_INCLUDED
|
||||
|
Loading…
x
Reference in New Issue
Block a user