srctree

Robin Linden parent c548b368 b5a23d66
etest: Add support for passing custom assertion failure messages

inlinesplit
css/parser_test.cpp added: 79, removed: 44, total 35
@@ -73,7 +73,7 @@ template<class KeyT, class ValueT>
ValueT get_and_erase(std::map<KeyT, ValueT, std::less<>> &map,
KeyT key,
etest::source_location const &loc = etest::source_location::current()) {
require(map.contains(key), loc);
require(map.contains(key), {}, loc);
ValueT value = map.at(key);
map.erase(key);
return value;
 
css2/tokenizer_test.cpp added: 79, removed: 44, total 35
@@ -20,8 +20,8 @@ namespace {
class TokenizerOutput {
public:
~TokenizerOutput() {
expect(tokens.empty(), loc);
expect(errors.empty(), loc);
expect(tokens.empty(), "Not all tokens were handled", loc);
expect(errors.empty(), "Not all errors were handled", loc);
}
 
std::vector<Token> tokens;
@@ -43,15 +43,15 @@ TokenizerOutput run_tokenizer(std::string_view input, etest::source_location loc
 
void expect_token(
TokenizerOutput &output, Token t, etest::source_location const &loc = etest::source_location::current()) {
require(!output.tokens.empty(), loc);
expect_eq(output.tokens.front(), t, loc);
require(!output.tokens.empty(), "Unexpected end of token list", loc);
expect_eq(output.tokens.front(), t, {}, loc);
output.tokens.erase(begin(output.tokens));
}
 
void expect_error(
TokenizerOutput &output, ParseError e, etest::source_location const &loc = etest::source_location::current()) {
require(!output.errors.empty(), loc);
expect_eq(output.errors.front(), e, loc);
require(!output.errors.empty(), "Unexpected end of error list", loc);
expect_eq(output.errors.front(), e, {}, loc);
output.errors.erase(begin(output.errors));
}
 
 
etest/etest.cpp added: 79, removed: 44, total 35
@@ -86,7 +86,7 @@ void disabled_test(std::string, std::function<void()>) noexcept {
++disabled_tests;
}
 
void expect(bool b, etest::source_location const &loc) noexcept {
void expect(bool b, std::optional<std::string_view> log_message, etest::source_location const &loc) noexcept {
if (!b) {
++assertion_failures;
// Check if we're using the real source_location by checking for line == 0.
@@ -94,15 +94,24 @@ void expect(bool b, etest::source_location const &loc) noexcept {
test_log << " expectation failure at " << loc.file_name() << "(" << loc.line() << ":" << loc.column()
<< ")\n";
}
 
if (log_message) {
test_log << *log_message << "\n\n";
}
}
}
 
void require(bool b, etest::source_location const &loc) {
void require(bool b, std::optional<std::string_view> log_message, etest::source_location const &loc) {
if (!b) {
if (loc.line() != 0) {
test_log << " requirement failure at " << loc.file_name() << "(" << loc.line() << ":" << loc.column()
<< ")\n";
}
 
if (log_message) {
test_log << *log_message << "\n\n";
}
 
throw test_failure{};
}
}
 
etest/etest.h added: 79, removed: 44, total 35
@@ -9,8 +9,12 @@
 
#include <concepts>
#include <functional>
#include <optional>
#include <ostream>
#include <sstream>
#include <string>
#include <string_view>
#include <utility>
 
namespace etest {
 
@@ -24,44 +28,66 @@ void test(std::string name, std::function<void()> body) noexcept;
void disabled_test(std::string name, std::function<void()> body) noexcept;
 
// Weak test requirement. Allows the test to continue even if the check fails.
void expect(bool, etest::source_location const &loc = etest::source_location::current()) noexcept;
void expect(bool,
std::optional<std::string_view> log_message = std::nullopt,
etest::source_location const &loc = etest::source_location::current()) noexcept;
 
// Hard test requirement. Stops the test (using an exception) if the check fails.
void require(bool, etest::source_location const &loc = etest::source_location::current());
void require(bool,
std::optional<std::string_view> log_message = std::nullopt,
etest::source_location const &loc = etest::source_location::current());
 
// Access the internal test log.
[[nodiscard]] std::ostream &log();
 
// Weak test requirement. Prints the types compared on failure (if printable).
template<Printable T, Printable U>
void expect_eq(T const &a, U const &b, etest::source_location const &loc = etest::source_location::current()) noexcept {
void expect_eq(T const &a,
U const &b,
std::optional<std::string_view> log_message = std::nullopt,
etest::source_location const &loc = etest::source_location::current()) noexcept {
if (a != b) {
expect(false, loc);
log() << a << " !=\n" << b << "\n\n";
}
}
 
template<typename T, typename U>
void expect_eq(T const &a, U const &b, etest::source_location const &loc = etest::source_location::current()) noexcept {
expect(a == b, loc);
}
 
// Hard test requirement. Prints the types compared on failure (if printable).
template<Printable T, Printable U>
void require_eq(T const &a, U const &b, etest::source_location const &loc = etest::source_location::current()) {
if (a != b) {
try {
require(false, loc);
} catch (...) {
log() << a << " !=\n" << b << "\n\n";
throw;
if (log_message) {
expect(false, std::move(log_message), loc);
} else {
std::stringstream ss;
ss << a << " !=\n" << b;
expect(false, std::move(ss).str(), loc);
}
}
}
 
template<typename T, typename U>
void require_eq(T const &a, U const &b, etest::source_location const &loc = etest::source_location::current()) {
require(a == b, loc);
void expect_eq(T const &a,
U const &b,
std::optional<std::string_view> log_message = std::nullopt,
etest::source_location const &loc = etest::source_location::current()) noexcept {
expect(a == b, std::move(log_message), loc);
}
 
// Hard test requirement. Prints the types compared on failure (if printable).
template<Printable T, Printable U>
void require_eq(T const &a,
U const &b,
std::optional<std::string_view> log_message = std::nullopt,
etest::source_location const &loc = etest::source_location::current()) {
if (a != b) {
if (log_message) {
require(false, std::move(log_message), loc);
} else {
std::stringstream ss;
ss << a << " !=\n" << b;
require(false, std::move(ss).str(), loc);
}
}
}
 
template<typename T, typename U>
void require_eq(T const &a,
U const &b,
std::optional<std::string_view> log_message = std::nullopt,
etest::source_location const &loc = etest::source_location::current()) {
require(a == b, std::move(log_message), loc);
}
 
} // namespace etest
 
html2/tokenizer_test.cpp added: 79, removed: 44, total 35
@@ -31,8 +31,8 @@ static constexpr char const *kReplacementCharacter = "\xef\xbf\xbd";
class TokenizerOutput {
public:
~TokenizerOutput() {
expect(tokens.empty(), loc);
expect(errors.empty(), loc);
expect(tokens.empty(), "Not all tokens were handled", loc);
expect(errors.empty(), "Not all errors were handled", loc);
}
 
std::vector<Token> tokens;
@@ -61,8 +61,8 @@ TokenizerOutput run_tokenizer(std::string_view input, etest::source_location loc
 
void expect_token(
TokenizerOutput &output, Token t, etest::source_location const &loc = etest::source_location::current()) {
require(!output.tokens.empty(), loc);
expect_eq(output.tokens.front(), t, loc);
require(!output.tokens.empty(), "Unexpected end of token list", loc);
expect_eq(output.tokens.front(), t, {}, loc);
output.tokens.erase(begin(output.tokens));
}
 
@@ -76,8 +76,8 @@ void expect_text(TokenizerOutput &output,
 
void expect_error(
TokenizerOutput &output, ParseError e, etest::source_location const &loc = etest::source_location::current()) {
require(!output.errors.empty(), loc);
expect_eq(output.errors.front(), e, loc);
require(!output.errors.empty(), "Unexpected end of error list", loc);
expect_eq(output.errors.front(), e, {}, loc);
output.errors.erase(begin(output.errors));
}