catch2/examples/210-Evt-EventListeners.cpp
Martin Hořeňovský 45577a1f4c
Refactor implementation of case-insensitivity in tags
By not materializing the lower cased tags ahead of time, we
save allocations at the cost of worsened performance when comparing
two tags.

Since there are rarely many tags, and commonly they are not
compared even if present, this is almost always a win. The new
implementation also improves the robustness of the code
responsible for handling tags in a case-insensitive manner.
2021-12-28 00:20:55 +01:00

430 lines
14 KiB
C++

// 210-Evt-EventListeners.cpp
// Contents:
// 1. Printing of listener data
// 2. My listener and registration
// 3. Test cases
#include <catch2/catch_test_macros.hpp>
#include <catch2/reporters/catch_reporter_event_listener.hpp>
#include <catch2/reporters/catch_reporter_registrars.hpp>
#include <catch2/catch_test_case_info.hpp>
#include <iostream>
// -----------------------------------------------------------------------
// 1. Printing of listener data:
//
namespace {
std::string ws(int const level) {
return std::string( 2 * level, ' ' );
}
std::ostream& operator<<(std::ostream& out, Catch::Tag t) {
return out << "original: " << t.original;
}
template< typename T >
std::ostream& operator<<( std::ostream& os, std::vector<T> const& v ) {
os << "{ ";
for ( const auto& x : v )
os << x << ", ";
return os << "}";
}
// struct SourceLineInfo {
// char const* file;
// std::size_t line;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::SourceLineInfo const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- file: " << info.file << "\n"
<< ws(level+1) << "- line: " << info.line << "\n";
}
//struct MessageInfo {
// std::string macroName;
// std::string message;
// SourceLineInfo lineInfo;
// ResultWas::OfType type;
// unsigned int sequence;
//};
void print( std::ostream& os, int const level, Catch::MessageInfo const& info ) {
os << ws(level+1) << "- macroName: '" << info.macroName << "'\n"
<< ws(level+1) << "- message '" << info.message << "'\n";
print( os,level+1 , "- lineInfo", info.lineInfo );
os << ws(level+1) << "- sequence " << info.sequence << "\n";
}
void print( std::ostream& os, int const level, std::string const& title, std::vector<Catch::MessageInfo> const& v ) {
os << ws(level ) << title << ":\n";
for ( const auto& x : v )
{
os << ws(level+1) << "{\n";
print( os, level+2, x );
os << ws(level+1) << "}\n";
}
// os << ws(level+1) << "\n";
}
// struct TestRunInfo {
// std::string name;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::TestRunInfo const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- name: " << info.name << "\n";
}
// struct Counts {
// std::size_t total() const;
// bool allPassed() const;
// bool allOk() const;
//
// std::size_t passed = 0;
// std::size_t failed = 0;
// std::size_t failedButOk = 0;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::Counts const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- total(): " << info.total() << "\n"
<< ws(level+1) << "- allPassed(): " << info.allPassed() << "\n"
<< ws(level+1) << "- allOk(): " << info.allOk() << "\n"
<< ws(level+1) << "- passed: " << info.passed << "\n"
<< ws(level+1) << "- failed: " << info.failed << "\n"
<< ws(level+1) << "- failedButOk: " << info.failedButOk << "\n";
}
// struct Totals {
// Counts assertions;
// Counts testCases;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::Totals const& info ) {
os << ws(level) << title << ":\n";
print( os, level+1, "- assertions", info.assertions );
print( os, level+1, "- testCases" , info.testCases );
}
// struct TestRunStats {
// TestRunInfo runInfo;
// Totals totals;
// bool aborting;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::TestRunStats const& info ) {
os << ws(level) << title << ":\n";
print( os, level+1 , "- runInfo", info.runInfo );
print( os, level+1 , "- totals" , info.totals );
os << ws(level+1) << "- aborting: " << info.aborting << "\n";
}
// struct Tag {
// StringRef original, lowerCased;
// };
//
//
// enum class TestCaseProperties : uint8_t {
// None = 0,
// IsHidden = 1 << 1,
// ShouldFail = 1 << 2,
// MayFail = 1 << 3,
// Throws = 1 << 4,
// NonPortable = 1 << 5,
// Benchmark = 1 << 6
// };
//
//
// struct TestCaseInfo : NonCopyable {
//
// bool isHidden() const;
// bool throws() const;
// bool okToFail() const;
// bool expectedToFail() const;
//
//
// std::string name;
// std::string className;
// std::vector<Tag> tags;
// SourceLineInfo lineInfo;
// TestCaseProperties properties = TestCaseProperties::None;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::TestCaseInfo const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- isHidden(): " << info.isHidden() << "\n"
<< ws(level+1) << "- throws(): " << info.throws() << "\n"
<< ws(level+1) << "- okToFail(): " << info.okToFail() << "\n"
<< ws(level+1) << "- expectedToFail(): " << info.expectedToFail() << "\n"
<< ws(level+1) << "- tagsAsString(): '" << info.tagsAsString() << "'\n"
<< ws(level+1) << "- name: '" << info.name << "'\n"
<< ws(level+1) << "- className: '" << info.className << "'\n"
<< ws(level+1) << "- tags: " << info.tags << "\n";
print( os, level+1 , "- lineInfo", info.lineInfo );
os << ws(level+1) << "- properties (flags): 0x" << std::hex << static_cast<uint32_t>(info.properties) << std::dec << "\n";
}
// struct TestCaseStats {
// TestCaseInfo testInfo;
// Totals totals;
// std::string stdOut;
// std::string stdErr;
// bool aborting;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::TestCaseStats const& info ) {
os << ws(level ) << title << ":\n";
print( os, level+1 , "- testInfo", *info.testInfo );
print( os, level+1 , "- totals" , info.totals );
os << ws(level+1) << "- stdOut: " << info.stdOut << "\n"
<< ws(level+1) << "- stdErr: " << info.stdErr << "\n"
<< ws(level+1) << "- aborting: " << info.aborting << "\n";
}
// struct SectionInfo {
// std::string name;
// std::string description;
// SourceLineInfo lineInfo;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::SectionInfo const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- name: " << info.name << "\n";
print( os, level+1 , "- lineInfo", info.lineInfo );
}
// struct SectionStats {
// SectionInfo sectionInfo;
// Counts assertions;
// double durationInSeconds;
// bool missingAssertions;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::SectionStats const& info ) {
os << ws(level ) << title << ":\n";
print( os, level+1 , "- sectionInfo", info.sectionInfo );
print( os, level+1 , "- assertions" , info.assertions );
os << ws(level+1) << "- durationInSeconds: " << info.durationInSeconds << "\n"
<< ws(level+1) << "- missingAssertions: " << info.missingAssertions << "\n";
}
// struct AssertionInfo
// {
// StringRef macroName;
// SourceLineInfo lineInfo;
// StringRef capturedExpression;
// ResultDisposition::Flags resultDisposition;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionInfo const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- macroName: '" << info.macroName << "'\n";
print( os, level+1 , "- lineInfo" , info.lineInfo );
os << ws(level+1) << "- capturedExpression: '" << info.capturedExpression << "'\n"
<< ws(level+1) << "- resultDisposition (flags): 0x" << std::hex << info.resultDisposition << std::dec << "\n";
}
//struct AssertionResultData
//{
// std::string reconstructExpression() const;
//
// std::string message;
// mutable std::string reconstructedExpression;
// LazyExpression lazyExpression;
// ResultWas::OfType resultType;
//};
void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionResultData const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- reconstructExpression(): '" << info.reconstructExpression() << "'\n"
<< ws(level+1) << "- message: '" << info.message << "'\n"
<< ws(level+1) << "- lazyExpression: '" << "(info.lazyExpression)" << "'\n"
<< ws(level+1) << "- resultType: '" << info.resultType << "'\n";
}
//class AssertionResult {
// bool isOk() const;
// bool succeeded() const;
// ResultWas::OfType getResultType() const;
// bool hasExpression() const;
// bool hasMessage() const;
// std::string getExpression() const;
// std::string getExpressionInMacro() const;
// bool hasExpandedExpression() const;
// std::string getExpandedExpression() const;
// std::string getMessage() const;
// SourceLineInfo getSourceInfo() const;
// std::string getTestMacroName() const;
//
// AssertionInfo m_info;
// AssertionResultData m_resultData;
//};
void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionResult const& info ) {
os << ws(level ) << title << ":\n"
<< ws(level+1) << "- isOk(): " << info.isOk() << "\n"
<< ws(level+1) << "- succeeded(): " << info.succeeded() << "\n"
<< ws(level+1) << "- getResultType(): " << info.getResultType() << "\n"
<< ws(level+1) << "- hasExpression(): " << info.hasExpression() << "\n"
<< ws(level+1) << "- hasMessage(): " << info.hasMessage() << "\n"
<< ws(level+1) << "- getExpression(): '" << info.getExpression() << "'\n"
<< ws(level+1) << "- getExpressionInMacro(): '" << info.getExpressionInMacro() << "'\n"
<< ws(level+1) << "- hasExpandedExpression(): " << info.hasExpandedExpression() << "\n"
<< ws(level+1) << "- getExpandedExpression(): " << info.getExpandedExpression() << "'\n"
<< ws(level+1) << "- getMessage(): '" << info.getMessage() << "'\n";
print( os, level+1 , "- getSourceInfo(): ", info.getSourceInfo() );
os << ws(level+1) << "- getTestMacroName(): '" << info.getTestMacroName() << "'\n";
print( os, level+1 , "- *** m_info (AssertionInfo)", info.m_info );
print( os, level+1 , "- *** m_resultData (AssertionResultData)", info.m_resultData );
}
// struct AssertionStats {
// AssertionResult assertionResult;
// std::vector<MessageInfo> infoMessages;
// Totals totals;
// };
void print( std::ostream& os, int const level, std::string const& title, Catch::AssertionStats const& info ) {
os << ws(level ) << title << ":\n";
print( os, level+1 , "- assertionResult", info.assertionResult );
print( os, level+1 , "- infoMessages", info.infoMessages );
print( os, level+1 , "- totals", info.totals );
}
// -----------------------------------------------------------------------
// 2. My listener and registration:
//
char const * dashed_line =
"--------------------------------------------------------------------------";
struct MyListener : Catch::EventListenerBase {
using EventListenerBase::EventListenerBase; // inherit constructor
// Get rid of Wweak-tables
~MyListener();
// The whole test run starting
void testRunStarting( Catch::TestRunInfo const& testRunInfo ) override {
std::cout
<< std::boolalpha
<< "\nEvent: testRunStarting:\n";
print( std::cout, 1, "- testRunInfo", testRunInfo );
}
// The whole test run ending
void testRunEnded( Catch::TestRunStats const& testRunStats ) override {
std::cout
<< dashed_line
<< "\nEvent: testRunEnded:\n";
print( std::cout, 1, "- testRunStats", testRunStats );
}
// A test is being skipped (because it is "hidden")
void skipTest( Catch::TestCaseInfo const& testInfo ) override {
std::cout
<< dashed_line
<< "\nEvent: skipTest:\n";
print( std::cout, 1, "- testInfo", testInfo );
}
// Test cases starting
void testCaseStarting( Catch::TestCaseInfo const& testInfo ) override {
std::cout
<< dashed_line
<< "\nEvent: testCaseStarting:\n";
print( std::cout, 1, "- testInfo", testInfo );
}
// Test cases ending
void testCaseEnded( Catch::TestCaseStats const& testCaseStats ) override {
std::cout << "\nEvent: testCaseEnded:\n";
print( std::cout, 1, "testCaseStats", testCaseStats );
}
// Sections starting
void sectionStarting( Catch::SectionInfo const& sectionInfo ) override {
std::cout << "\nEvent: sectionStarting:\n";
print( std::cout, 1, "- sectionInfo", sectionInfo );
}
// Sections ending
void sectionEnded( Catch::SectionStats const& sectionStats ) override {
std::cout << "\nEvent: sectionEnded:\n";
print( std::cout, 1, "- sectionStats", sectionStats );
}
// Assertions before/ after
void assertionStarting( Catch::AssertionInfo const& assertionInfo ) override {
std::cout << "\nEvent: assertionStarting:\n";
print( std::cout, 1, "- assertionInfo", assertionInfo );
}
void assertionEnded( Catch::AssertionStats const& assertionStats ) override {
std::cout << "\nEvent: assertionEnded:\n";
print( std::cout, 1, "- assertionStats", assertionStats );
}
};
} // end anonymous namespace
CATCH_REGISTER_LISTENER( MyListener )
// Get rid of Wweak-tables
MyListener::~MyListener() {}
// -----------------------------------------------------------------------
// 3. Test cases:
//
TEST_CASE( "1: Hidden testcase", "[.hidden]" ) {
}
TEST_CASE( "2: Testcase with sections", "[tag-A][tag-B]" ) {
int i = 42;
REQUIRE( i == 42 );
SECTION("Section 1") {
INFO("Section 1");
i = 7;
SECTION("Section 1.1") {
INFO("Section 1.1");
REQUIRE( i == 42 );
}
}
SECTION("Section 2") {
INFO("Section 2");
REQUIRE( i == 42 );
}
WARN("At end of test case");
}
struct Fixture {
int fortytwo() const {
return 42;
}
};
TEST_CASE_METHOD( Fixture, "3: Testcase with class-based fixture", "[tag-C][tag-D]" ) {
REQUIRE( fortytwo() == 42 );
}
// Compile & run:
// - g++ -std=c++11 -Wall -I$(CATCH_SINGLE_INCLUDE) -o 210-Evt-EventListeners 210-Evt-EventListeners.cpp 000-CatchMain.o && 210-Evt-EventListeners --success
// - cl -EHsc -I%CATCH_SINGLE_INCLUDE% 210-Evt-EventListeners.cpp 000-CatchMain.obj && 210-Evt-EventListeners --success
// Expected compact output (all assertions):
//
// prompt> 210-Evt-EventListeners --reporter compact --success
// result omitted for brevity.