diff --git a/bitshift/validate/ansi.cxx b/bitshift/validate/ansi.cxx index f09851a..3a6378f 100644 --- a/bitshift/validate/ansi.cxx +++ b/bitshift/validate/ansi.cxx @@ -40,6 +40,10 @@ namespace bitshift::validate o << "\033[0m"; switch (ansi.get_color()) { + case ansi::kNone: + o << "\033[0"; + break; + case ansi::kBlack: o << "\033[30"; break; @@ -109,7 +113,7 @@ namespace bitshift::validate ostream& operator<<(ostream& o, ansi::style const& style) { - return o << ansi{ansi::color::kBlack, style}; + return o << ansi{ansi::color::kNone, style}; } } // namespace bitshift::validate diff --git a/bitshift/validate/ansi.hxx b/bitshift/validate/ansi.hxx index 021a5ed..35fdf65 100644 --- a/bitshift/validate/ansi.hxx +++ b/bitshift/validate/ansi.hxx @@ -16,6 +16,7 @@ namespace bitshift::validate enum color : uint8_t { + kNone, kBlack, kRed, kGreen, diff --git a/bitshift/validate/assert.hxx b/bitshift/validate/assert.hxx index 0d354f9..15b7ab7 100644 --- a/bitshift/validate/assert.hxx +++ b/bitshift/validate/assert.hxx @@ -1,9 +1,10 @@ #ifndef bitshift__validate__assert_hxx_ #define bitshift__validate__assert_hxx_ -#include #include +#include #include +#include namespace bitshift::validate::assertion { @@ -42,6 +43,21 @@ namespace bitshift::validate::assertion string const& rhs_str, source_location const& origin = source_location::current()); + template + void + assert_nothrow(F const& f, + source_location const& origin = source_location::current()); + + template + void + assert_throws(F const& f, + source_location const& origin = source_location::current()); + + template + void + assert_throws_any(F const& f, + source_location const& origin = source_location::current()); + } // namespace bitshift::validate::assertion #include diff --git a/bitshift/validate/assert.txx b/bitshift/validate/assert.txx index 8d5ae1e..390a434 100644 --- a/bitshift/validate/assert.txx +++ b/bitshift/validate/assert.txx @@ -11,20 +11,18 @@ namespace bitshift::validate::assertion return; } - string value_str; - - if constexpr (traits::has_output_operator) { - stringstream str; - str << expr; - value_str = str.str(); - } - - throw unary_assertion_failure{ - "expected expression to be true; was false", - origin.file_name(), - origin.line(), - std::move(expr_str), - std::move(value_str) + throw failure{ + [=](reporter& r) + { + auto p = r.log_failure( + "expected expression to be true; was false", + origin.file_name(), + origin.line() + ); + + p << "expression: " << expr_str << '\n'; + p << "value : " << expr << '\n'; + } }; } @@ -38,20 +36,18 @@ namespace bitshift::validate::assertion return; } - string value_str; - - if constexpr (traits::has_output_operator) { - stringstream str; - str << expr; - value_str = str.str(); - } - - throw unary_assertion_failure{ - "expected expression to be false; was true", - origin.file_name(), - origin.line(), - std::move(expr_str), - std::move(value_str) + throw failure{ + [=](reporter& r) + { + auto p = r.log_failure( + "expected expression to be false; was true", + origin.file_name(), + origin.line() + ); + + p << "expression: " << expr_str << '\n'; + p << "value : " << expr << '\n'; + } }; } @@ -65,20 +61,18 @@ namespace bitshift::validate::assertion return; } - string value_str; - - if constexpr (traits::has_output_operator) { - stringstream str; - str << expr; - value_str = str.str(); - } - - throw unary_assertion_failure{ - "expected expression to be null; was not null", - origin.file_name(), - origin.line(), - std::move(expr_str), - std::move(value_str) + throw failure{ + [=](reporter& r) + { + auto p = r.log_failure( + "expected expression to be null; was not", + origin.file_name(), + origin.line() + ); + + p << "expression: " << expr_str << '\n'; + p << "value : " << expr << '\n'; + } }; } @@ -94,29 +88,20 @@ namespace bitshift::validate::assertion return; } - std::string lhs_val; - std::string rhs_val; - - if constexpr (traits::has_output_operator) { - std::stringstream str; - str << lhs; - lhs_val = str.str(); - } - - if constexpr (traits::has_output_operator) { - std::stringstream str; - str << rhs; - rhs_val = str.str(); - } - - throw binary_assertion_failure{ - "expected lhs to equal rhs", - origin.file_name(), - origin.line(), - std::move(lhs_str), - std::move(lhs_val), - std::move(rhs_str), - std::move(rhs_val) + throw failure{ + [=](reporter& r) + { + auto p = r.log_failure( + "expected lhs to equal rhs", + origin.file_name(), + origin.line() + ); + + p << "lhs : " << lhs_str << '\n'; + p << "lhs value: " << lhs << '\n'; + p << "rhs : " << rhs_str << '\n'; + p << "rhs value: " << rhs << '\n'; + } }; } @@ -132,29 +117,87 @@ namespace bitshift::validate::assertion return; } - std::string lhs_val; - std::string rhs_val; + throw failure{ + [=](reporter& r) + { + auto p = r.log_failure( + "expected lhs to not equal rhs", + origin.file_name(), + origin.line() + ); + + p << "lhs : " << lhs_str << '\n'; + p << "lhs value: " << lhs << '\n'; + p << "rhs : " << rhs_str << '\n'; + p << "rhs value: " << rhs << '\n'; + } + }; + } - if constexpr (traits::has_output_operator) { - std::stringstream str; - str << lhs; - lhs_val = str.str(); + template + void + assert_nothrow(F const& f, source_location const& origin) + { + try { + f(); + } + catch (...) { + throw failure{ + [=](reporter& r) + { + auto p = r.log_failure( + "expression unexpectedly threw an exception", + origin.file_name(), + origin.line() + ); + } + }; + } + } + + template + void + assert_throws(F const& f, source_location const& origin) + { + try { + f(); + } + catch (T const&) { + return; } - if constexpr (traits::has_output_operator) { - std::stringstream str; - str << rhs; - rhs_val = str.str(); + throw failure{ + [=](reporter& r) + { + auto p = r.log_failure( + "expected expression was not thrown", + origin.file_name(), + origin.line() + ); + } + }; + } + + template + void + assert_throws_any(F const& f, source_location const& origin) + { + try { + f(); + } + catch (...) { + return; } - throw binary_assertion_failure{ - "expected lhs to not equal rhs", - origin.file_name(), - origin.line(), - std::move(lhs_str), - std::move(lhs_val), - std::move(rhs_str), - std::move(rhs_val) + throw failure{ + [=](reporter& r) + { + auto p = r.log_failure( + "expected expression was not thrown", + origin.file_name(), + origin.line() + ); + } }; } diff --git a/bitshift/validate/execute.cxx b/bitshift/validate/execute.cxx index 85cab38..38444a6 100644 --- a/bitshift/validate/execute.cxx +++ b/bitshift/validate/execute.cxx @@ -73,11 +73,15 @@ namespace bitshift::validate try { tc.run(); } - catch (ignore const&) { + catch (ignored const&) { execute_teardown(r); return kExecuteIgnored; } - catch (assertion_failure const& failure) { + catch (passed const&) { + // No-op. + // + } + catch (failure const& failure) { failure.report(r); execute_teardown(r); return kExecuteFailed; diff --git a/bitshift/validate/failure.cxx b/bitshift/validate/failure.cxx deleted file mode 100644 index 13f33d7..0000000 --- a/bitshift/validate/failure.cxx +++ /dev/null @@ -1,130 +0,0 @@ -#include - -#include - -namespace bitshift::validate -{ - - unary_assertion_failure:: - unary_assertion_failure(string message, - string file, - uint32_t line, - string expr, - string val) - : _message{std::move(message)} - , _file {std::move(file)} - , _line {line} - , _expr {std::move(expr)} - , _val {std::move(val)} - {} - - string const& - unary_assertion_failure:: - get_file() const - { - return _file; - } - - uint32_t - unary_assertion_failure:: - get_line() const - { - return _line; - } - - string const& - unary_assertion_failure:: - get_expr() const - { - return _expr; - } - - string const& - unary_assertion_failure:: - get_val() const - { - return _val; - } - - void - unary_assertion_failure:: - report(reporter& r) const - { - auto p = r.log_failure(_message, _file, _line); - - p << "expression: " << _expr << '\n'; - p << "value : " << _val << '\n'; - } - - binary_assertion_failure:: - binary_assertion_failure(string message, - string file, - uint32_t line, - string lhs_expr, - string lhs_val, - string rhs_expr, - string rhs_val) - : _message {std::move(message)} - , _file {std::move(file)} - , _line {line} - , _lhs_expr{std::move(lhs_expr)} - , _lhs_val {std::move(lhs_val)} - , _rhs_expr{std::move(rhs_expr)} - , _rhs_val {std::move(rhs_val)} - {} - - string const& - binary_assertion_failure:: - get_file() const - { - return _file; - } - - uint32_t - binary_assertion_failure:: - get_line() const - { - return _line; - } - - string const& - binary_assertion_failure:: - get_lhs_expr() const - { - return _lhs_expr; - } - - string const& - binary_assertion_failure:: - get_lhs_val() const - { - return _lhs_val; - } - - string const& - binary_assertion_failure:: - get_rhs_expr() const - { - return _rhs_expr; - } - - string const& - binary_assertion_failure:: - get_rhs_val() const - { - return _rhs_val; - } - - void - binary_assertion_failure:: - report(reporter& r) const - { - auto p = r.log_failure(_message, _file, _line); - - p << "lhs expression: " << _lhs_expr << '\n'; - p << "lhs value : " << _lhs_val << '\n'; - p << "rhs expression: " << _rhs_expr << '\n'; - p << "rhs value : " << _rhs_val << '\n'; - } - -} // namespace bitshift::validate diff --git a/bitshift/validate/failure.hxx b/bitshift/validate/failure.hxx index c790ac7..5f74101 100644 --- a/bitshift/validate/failure.hxx +++ b/bitshift/validate/failure.hxx @@ -2,204 +2,79 @@ #define bitshift__validate__failure_hxx_ #include +#include namespace bitshift::validate { - struct ignore { }; - - class reporter; - - /// @brief Class used to indicate an assertion failure. + /// @brief Class used to explicitly ignore a test. /// - class assertion_failure + struct ignored {}; + + /// @brief Class used to explicitly pass a test. + /// + struct passed {}; + + /// @brief Class used to explicitly fail a test. + /// + class failure { public: - /// @brief Destructor. + /// @brief Constructor. + /// @param rf The reporter function. /// - virtual - ~assertion_failure() noexcept = default; + explicit + failure(function rf) + : _rf{rf} + {} + + /// @brief Copy-construction is disabled. + /// + failure(failure const&) = delete; + + /// @brief Move-construction is disabled. + /// + failure(failure&&) = delete; /// @brief Report assertion failure. /// - /// @param r The reporter. - /// - virtual void - report(reporter& r) const = 0; - - protected: - /// @brief Constructor. - /// - assertion_failure() = default; - - /// @brief Copy-construction is disabled. - /// - assertion_failure(assertion_failure const&) = delete; - - /// @brief Move-construction is disabled. - /// - assertion_failure(assertion_failure&&) = delete; + report(reporter& r) const + { + _rf(r); + } /// @brief Copy-assignment is disabled. /// - assertion_failure& operator=(assertion_failure const&) = delete; + failure& operator=(failure const&) = delete; /// @brief Move-assignment is disabled. /// - assertion_failure& operator=(assertion_failure&&) = delete; + failure& operator=(failure&&) = delete; - }; - - /// @brief Indicates a unary assertion failure. - /// - class unary_assertion_failure - : public assertion_failure - { - public: - /// @brief Constructor. + /// @brief Helper function to throw a failure exception. /// - /// @param message The assertion failure message. - /// @param file The file in which the assertion failure occurred. - /// @param line The line on which the assertion failure occurred. - /// @param expr The expression that caused the assertion failure. - /// @param val The expression value that caused the assertion failure. + /// @param message The message describing the failure. /// - unary_assertion_failure(string message, - string file, - uint32_t line, - string expr, - string val); - - /// @brief Copy-construction is disabled. - /// - unary_assertion_failure(unary_assertion_failure const&) = delete; - - /// @brief Move-construction is disabled. - /// - unary_assertion_failure(unary_assertion_failure&&) = delete; - - /// @brief Get file. - /// - string const& - get_file() const; - - /// @brief Get line. - /// - uint32_t - get_line() const; - - /// @brief Get expression. - /// - string const& - get_expr() const; - - /// @brief Get expression value. - /// - string const& - get_val() const; - + static void - report(reporter&) const override; - - /// @brief Copy-assignment is disabled. - /// - unary_assertion_failure& operator=(unary_assertion_failure const&) = delete; - - /// @brief Move-assignment is disabled. - /// - unary_assertion_failure& operator=(unary_assertion_failure&&) = delete; + fail(string const& message, + source_location const& origin = source_location::current()) + { + throw failure{ + [=](reporter& r) + { + r.log_failure( + message, + origin.file_name(), + origin.line() + ); + } + }; + } private: - string _message; - string _file; - uint32_t _line{}; - string _expr; - string _val; - - }; - - /// @brief Indicates a binary assertion failure. - /// - class binary_assertion_failure - : public assertion_failure - { - public: - /// @brief Constructor. - /// - /// @param message The assertion failure message. - /// @param file The file in which the assertion failure occurred. - /// @param line The line on which the assertion failure occurred. - /// @param lhs_expr The left-hand side expression that caused the assertion failure. - /// @param lhs_val The left-hand side expression value that caused the assertion failure. - /// @param rhs_expr The right-hand side expression that caused the assertion failure. - /// @param rhs_val The right-hand side expression value that caused the assertion failure. - /// - binary_assertion_failure(string message, - string file, - uint32_t line, - string lhs_expr, - string lhs_val, - string rhs_expr, - string rhs_val); - - /// @brief Copy-construction is disabled. - /// - binary_assertion_failure(binary_assertion_failure const&) = delete; - - /// @brief Move-construction is disabled. - /// - binary_assertion_failure(binary_assertion_failure&&) = delete; - - /// @brief Get file. - /// - string const& - get_file() const; - - /// @brief Get line. - /// - uint32_t - get_line() const; - - /// @brief Get left-hand side expression. - /// - string const& - get_lhs_expr() const; - - /// @brief Get left-hand side expression value. - /// - string const& - get_lhs_val() const; - - /// @brief Get right-hand side expression. - /// - string const& - get_rhs_expr() const; - - /// @brief Get right-hand side expression value. - /// - string const& - get_rhs_val() const; - - void - report(reporter&) const override; - - /// @brief Copy-assignment is disabled. - /// - binary_assertion_failure& operator=(binary_assertion_failure const&) = delete; - - /// @brief Move-assignment is disabled. - /// - binary_assertion_failure& operator=(binary_assertion_failure&&) = delete; - - private: - string _message; - string _file; - uint32_t _line{}; - string _lhs_expr; - string _lhs_val; - string _rhs_expr; - string _rhs_val; + function _rf; }; diff --git a/bitshift/validate/macro.hxx b/bitshift/validate/macro.hxx index a110b54..b074c0e 100644 --- a/bitshift/validate/macro.hxx +++ b/bitshift/validate/macro.hxx @@ -103,7 +103,20 @@ /// @brief Ignore a test. /// @hideinitializer /// -#define BV_IGNORE throw bitshift::validate::ignore {}; +#define BV_IGNORE throw bitshift::validate::ignored{}; + +/// @ingroup assertions +/// @brief Explicitly pass a test. +/// @hideinitializer +/// +#define BV_PASS throw bitshift::validate::passed{}; + +/// @ingroup assertions +/// @brief Explicitly fail a test. +/// @hideinitializer +/// +#define BV_FAIL(msg) \ + bitshift::validate::failure::fail(msg); /// @ingroup assertions /// @brief Assert a @c true value. @@ -199,4 +212,18 @@ #define BV_ASSERT_NOT_EQUAL(lhs, rhs) \ bitshift::validate::assertion::assert_not_equal((lhs), (rhs), #lhs, #rhs) +/// @ingroup assertions +/// @brief Assert @a expr does not throw. +/// @param expr The expression to test. +/// @hideinitializer +/// +#define BV_ASSERT_NOTHROW(expr) \ + bitshift::validate::assertion::assert_nothrow([] { (expr); }) + +#define BV_ASSERT_THROWS(except, expr) \ + bitshift::validate::assertion::assert_throws([] { (expr); }) + +#define BV_ASSERT_THROWS_ANY(expr) \ + bitshift::validate::assertion::assert_throws_any([] { (expr); }) + #endif diff --git a/bitshift/validate/reporting.hxx b/bitshift/validate/reporting.hxx index 22c5c28..591dcd0 100644 --- a/bitshift/validate/reporting.hxx +++ b/bitshift/validate/reporting.hxx @@ -6,6 +6,7 @@ #include #include #include +#include namespace bitshift::validate { @@ -174,7 +175,13 @@ namespace bitshift::validate proxy& operator<<(U const& other) { - _detail << std::boolalpha << other; + if constexpr (traits::has_output_operator) { + _detail << std::boolalpha << other; + } + else { + _detail << ""; + } + return *this; } diff --git a/tests/sanity-check/driver.cxx b/tests/sanity-check/driver.cxx index 6c2615c..872477e 100644 --- a/tests/sanity-check/driver.cxx +++ b/tests/sanity-check/driver.cxx @@ -9,9 +9,110 @@ BV_TEST(sanity, "basic sanity check") BV_ASSERT_NOT_EQUAL(0, 1); } -BV_TEST(ignored, "ignored test") +BV_TEST(ignore, "test BV_IGNORE") { - BV_IGNORE; + try { + BV_IGNORE; + } + catch (bitshift::validate::ignored const&) { + BV_PASS; + } + catch (...) { + // Fall-through. + // + } + + BV_FAIL("expected exception not thrown"); +} + +BV_TEST(pass, "test BV_PASS") +{ + try { + BV_PASS; + } + catch (bitshift::validate::passed const&) { + BV_PASS; + } + catch (...) { + // Fall-through. + // + } + + BV_FAIL("expected exception not thrown"); +} + +BV_TEST(fail, "test BV_FAIL") +{ + try { + BV_FAIL("message"); + } + catch (bitshift::validate::failure const&) { + BV_PASS; + } + catch (...) { + // Fall-through. + // + } + + BV_FAIL("expected exception not thrown"); +} + +BV_TEST(nothrow, "test BV_ASSERT_NOTHROW") +{ + struct except {}; + + try { + // This assertion should fail. + // + BV_ASSERT_NOTHROW(throw except{}); + } + catch (bitshift::validate::failure const&) { + BV_PASS; + } + catch (...) { + // Fall-through. + // + } + + BV_FAIL("expected exception not thrown"); +} + +BV_TEST(throws, "test BV_ASSERT_THROWS") +{ + struct except {}; + + try { + // This assertion should fail. + // + BV_ASSERT_THROWS(except, (void)0); + } + catch (bitshift::validate::failure const&) { + BV_PASS; + } + catch (...) { + // Fall-through. + // + } + + BV_FAIL("expected exception not thrown"); +} + +BV_TEST(throws_any, "test BV_ASSERT_THROWS_ANY") +{ + try { + // This assertion should fail. + // + BV_ASSERT_THROWS_ANY((void)0); + } + catch (bitshift::validate::failure const&) { + BV_PASS; + } + catch (...) { + // Fall-through. + // + } + + BV_FAIL("expected exception not thrown"); } int