From 00ca84a2aa6417d0e8037878458ca8d6a2ae3d32 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 4 May 2023 00:48:29 +0200 Subject: [PATCH] test-helper for comparison with expected (string) result ...this is something I should have done since YEARS, really... Whenever working with symbolically represented data, tests typically involve checking *hundreds* of expected results, and thus it can be really hard to find out where the failure actually happens; it is better for readability to have the expected result string immediately in the test code; now this expected result can be marked with a user-defined literal, and then on mismatch the expected and the real value will be printed. --- src/lib/test/test-helper.cpp | 29 ++++++-- src/lib/test/test-helper.hpp | 50 +++++++++++++- tests/15library.tests | 5 ++ tests/library/split-splice-test.cpp | 102 ++++++++++++++++++++-------- wiki/thinkPad.ichthyo.mm | 22 ++++++ 5 files changed, 175 insertions(+), 33 deletions(-) diff --git a/src/lib/test/test-helper.cpp b/src/lib/test/test-helper.cpp index c8599ba0e..4681e4c53 100644 --- a/src/lib/test/test-helper.cpp +++ b/src/lib/test/test-helper.cpp @@ -33,6 +33,7 @@ #include "lib/test/test-helper.hpp" #include "lib/test/testdummy.hpp" #include "lib/format-string.hpp" +#include "lib/format-cout.hpp" #include "lib/unique-malloc-owner.hpp" #include @@ -42,6 +43,12 @@ using std::string; namespace lib { namespace test{ + + + /** storage for test-dummy flags */ + long Dummy::_local_checksum = 0; + bool Dummy::_throw_in_ctor = false; + string @@ -54,7 +61,6 @@ namespace test{ - /** @todo probably this can be done in a more clever way. Anyone...? */ string @@ -72,9 +78,24 @@ namespace test{ - /** storage for test-dummy flags */ - long Dummy::_local_checksum = 0; - bool Dummy::_throw_in_ctor = false; + /** + * @internal check equality and print difference + * @remark defined here to avoid inclusion of `` in header + */ + bool + ExpectString::verify (std::string const& actual) const + { + std::string const& expected{*this}; // to avoid endless recursion + bool expectationMatch {actual == expected}; + if (not expectationMatch) + { + cerr << "FAIL___expectation___________" + << "\nexpect:"< + friend bool + operator== (X const& x, ExpectString const& expected) + { + std::string actual{util::StringConv::invoke (x)}; + return expected.verify (actual); + } + + template + friend bool + operator== (ExpectString const& expected, X const& x) + { + std::string actual{util::StringConv::invoke (x)}; + return expected.verify (actual); + } + + bool verify (std::string const& actual) const; + }; + }} // namespace lib::test +/** + * user defined literal for expected result strings. + * On equality comparison to any other string convertible object, + * the difference to this expected string is printed to STDERR + * + * @example + * \code + * CHECK (result23 == "[-100..100]"_expect); + * \endcode + */ +inline lib::test::ExpectString +operator""_expect (const char* lit, size_t siz) +{ + return lib::test::ExpectString{lit, siz}; +} + + /* === test helper macros === */ diff --git a/tests/15library.tests b/tests/15library.tests index 317745078..32285b56a 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -585,6 +585,11 @@ return: 0 END +PLANNED "Split/Splice segmentation" SplitSplice_test < @@ -130,6 +145,18 @@ namespace test { // using standard copy operator string() const + { + return renderContent() + assess(); + } + + bool + isValid() const + { + return isnil (this->assess()); + } + + string + renderContent() const { return "├"+util::join(*this,"")+"┤"; } @@ -160,19 +187,13 @@ namespace test { } return diagnosis; } - - bool - isValid() const - { - return isnil (this->assess()); - } }; - }//(End)Test Fixture + /****************************************************************************//** * @test verify proper working of a generic procedure to splice an interval * into a complete segmentation of an ordered axis into seamless intervals. @@ -210,29 +231,54 @@ namespace test { verify_testFixture() { { - Seg x{1,3}, u{2,4,true}; - cout << x << u << Seg::check<-5_!"_expect); - cout <<"l1:"<-5_!"_expect); + + CHECK ( 9 == Seg::cnt ); // 9 objects are alive + CHECK ( 9 == Seg::idGen); // ID generator sticks at 9 + CHECK (45 == Seg::check); // checksum 1+..+9 l3.erase(l3.begin()); - cout <<"l3x:"<after = 5; - cout <<"l3x:"<5_!!order_5>-5_!"_expect); + CHECK ( 8 == Seg::cnt ); // also one object less alive - cout << Seg::check<after = 5; // manipulate first segment to make it degenerate (empty + CHECK (l3.renderContent() == "├[5_5[[-5_10[[10~100[┤"_expect); + CHECK (l3.assess() == "missing-lower-bound!!gap_-100<>5_!!degen_5_!!gap_5<>-5_!"_expect); + l3.clear(); + CHECK (l3.assess() == "!empty!"_expect); + + CHECK ( 5 == Seg::cnt ); + CHECK ( 9 == Seg::idGen); + CHECK (15 == Seg::check); } - cout << Seg::check< + + + + + + +

+ tja... +

+

+ das hätte ich schon seit JAHREN machen können.... +

+

+ +

+

+ (aber auf den Kniff mit dem Marker-Typ bin ich erst gekommen, seitdem ich mich neulich nochmal mit user-defined-Literals beschäftigt habe) +

+ +
+ +