Improve unit tests so they test each part of the Exception class.
All checks were successful
buildbot/Ascendent (Alpine) Build done.
buildbot/Ascendent (FreeBSD) Build done.

Close #21
This commit is contained in:
S David 2022-09-03 23:29:13 -04:00
parent 400be25557
commit 183eab166d
2 changed files with 82 additions and 40 deletions

View File

@ -20,8 +20,9 @@ namespace engine {
class Exception : public std::runtime_error
{
public:
explicit Exception(const std::string& message, const_c_string file,
unsigned line)
explicit Exception(
const std::string& message, const_c_string file, unsigned line
)
: std::runtime_error(message), error_message(file)
{
error_message += ":" + std::to_string(line) + " : " + message;
@ -30,13 +31,15 @@ class Exception : public std::runtime_error
static void set_ostream(std::ostream& newstream) { output = newstream; }
static void rethrow(const std::string& message, const_c_string file,
unsigned line);
static void rethrow(
const std::string& message, const_c_string file, unsigned line
);
static void backtrace(const std::exception& ex);
static void handler(const std::exception& ex,
const std::string& caller_name = "");
static void handler(
const std::exception& ex, const std::string& caller_name = ""
);
const_c_string what() const noexcept { return error_message.c_str(); }
protected:
@ -45,8 +48,9 @@ class Exception : public std::runtime_error
};
inline void
Exception::rethrow(const std::string& message, const_c_string file,
unsigned line)
Exception::rethrow(
const std::string& message, const_c_string file, unsigned line
)
try {
std::rethrow_exception(std::current_exception());
}
@ -73,6 +77,7 @@ try {
if (caller_name != "")
out << std::endl
<< "Exception caught in function \'" << caller_name << "\'"
<< std::endl
<< std::endl;
out << "Backtrace:" << std::endl;
backtrace(ex);
@ -86,15 +91,14 @@ catch (...) {
}
// Shorthand for throwing an Exception with file and line info using macros
#define throw_exception(message) \
#define THROW_EXCEPTION(message) \
throw engine::Exception(message, __FILE__, __LINE__)
// Shorthand for rethrowing and Exception with file and line info using macros
#define rethrow_exception(message) \
engine::Exception::rethrow(message, __FILE__, __LINE__)
#define RETHROW(message) engine::Exception::rethrow(message, __FILE__, __LINE__)
// Shorthand for handling an exception, including a backtrace
#define handle_exception(ex) engine::Exception::handler(ex, __func__)
#define HANDLE_EXCEPTION(ex) engine::Exception::handler(ex, __func__)
// clang-format off
// vim: set foldmethod=marker foldmarker=#region,#endregion textwidth=80 ts=8 sts=0 sw=8 noexpandtab ft=cpp.doxygen :

View File

@ -20,29 +20,27 @@
#include "src/engine/Exception.hpp"
#include "src/engine/types.hpp"
using engine::Exception;
BEGIN_TEST_SUITE("Exception.tests")
void
some_random_function()
try {
throw_exception("Inner Exception");
THROW_EXCEPTION("Inner Exception");
}
catch (std::exception& ex) {
rethrow_exception("Rethrowing Inner");
RETHROW("Rethrowing Inner");
}
class ExceptionInspector : public engine::Exception
class ExceptionInspector : public Exception
{
private:
explicit ExceptionInspector(engine::Exception& other)
: engine::Exception(other)
{
}
explicit ExceptionInspector(Exception& other) : Exception(other) {}
virtual ~ExceptionInspector() {}
public:
static std::ostream& get_ostream(engine::Exception& instance)
static std::ostream& get_ostream(Exception& instance)
{
return instance.output;
}
@ -53,24 +51,24 @@ struct TestFixture
TestFixture() : test_subject("Test", __FILE__, __LINE__) {}
~TestFixture() = default;
engine::Exception test_subject;
Exception test_subject;
};
CATCH_CASE("Construction")
try {
// Test ptr construciton as well as compiler macros
auto* ptr = new engine::Exception("Hello", __FILE__, __LINE__);
auto* ptr = new Exception("Hello", __FILE__, __LINE__);
REQUIRE(nullptr != ptr);
// Test macros from header file that construct Exceptions
REQUIRE_THROWS_AS(throw_exception("Message"), engine::Exception);
REQUIRE_THROWS_AS(THROW_EXCEPTION("Message"), Exception);
}
catch (...) {
throw std::runtime_error("Error occurred during construction");
}
FIXTURE_CASE("Output stream is interchangeable")
FIXTURE_CASE("set_osstream: Output stream is interchangeable")
try {
std::stringstream buffer;
@ -85,25 +83,63 @@ catch (...) {
FIXTURE_CASE("rethrow() behaves as expected and nests the exceptions")
{
REQUIRE(true == false);
auto ex = std::runtime_error("Testing!");
try {
try {
throw ex;
}
catch (...) {
REQUIRE_THROWS(Exception::rethrow(
"Caught an exception", __FILE__, __LINE__
));
RETHROW("Rethrown from macro");
}
}
catch (std::exception& nested) {
std::string message = nested.what();
REQUIRE(
message.find("Rethrown from macro") != std::string::npos
);
}
}
FIXTURE_CASE("backtrace() behaves as expected and outputs a backtrace")
{
REQUIRE(true == false);
std::stringstream buffer;
Exception::set_ostream(buffer);
Exception::backtrace(Exception("Test", __FILE__, __LINE__));
std::cout << buffer.str() << std::endl;
REQUIRE(true == true);
}
FIXTURE_CASE(
"handler() behaves as expected and outputs exception text to ostream")
"handler() behaves as expected and outputs exception text to ostream"
)
{
REQUIRE(true == false);
std::stringstream buffer;
Exception::set_ostream(buffer);
try {
THROW_EXCEPTION("Test #1");
}
catch (Exception& ex) {
Exception::handler(ex, "catch2_handler_test");
}
REQUIRE(
buffer.str().find("Exception caught in function") !=
std::string::npos
);
REQUIRE(buffer.str().find("catch2_handler_test") != std::string::npos);
}
FIXTURE_CASE("what() returns the error message contents with file and lineinfo")
{
const auto result = test_subject.what();
auto result = std::string(test_subject.what());
REQUIRE(0 == std::strcmp("../src/tests/Exception.tests.cpp:54 : Test",
result));
REQUIRE(result.find("Exception.tests.cpp") != std::string::npos);
REQUIRE(result.find("Test") != std::string::npos);
}
CATCH_CASE("Integration Tests: handler method can generate stacktrace.")
@ -111,25 +147,27 @@ CATCH_CASE("Integration Tests: handler method can generate stacktrace.")
std::stringstream buffer;
std::vector<std::string> output_lines;
engine::Exception::set_ostream(buffer);
Exception::set_ostream(buffer);
try {
some_random_function();
}
catch (engine::Exception& ex) {
handle_exception(ex);
engine::Exception::set_ostream(std::cerr);
catch (Exception& ex) {
HANDLE_EXCEPTION(ex);
Exception::set_ostream(std::cerr);
}
buffer.flush();
for (std::string line; std::getline(buffer, line, '\n');) {
output_lines.push_back(line);
}
REQUIRE(output_lines[2].find("Backtrace:") != std::string::npos);
REQUIRE(output_lines[3].find("Exception.tests.cpp") !=
std::string::npos);
REQUIRE(output_lines[3].find("Rethrowing Inner") != std::string::npos);
REQUIRE(output_lines[4].find("Inner Exception") != std::string::npos);
// Make sure the stack trace outputs lines in order
REQUIRE(output_lines[3].find("Backtrace:") != std::string::npos);
REQUIRE(
output_lines[4].find("Exception.tests.cpp") != std::string::npos
);
REQUIRE(output_lines[4].find("Rethrowing Inner") != std::string::npos);
REQUIRE(output_lines[5].find("Inner Exception") != std::string::npos);
}
END_TEST_SUITE