///////////////////////////////////////////////////////////////////////////// // Name: wx/catch_cppunit.h // Purpose: Reimplementation of CppUnit macros in terms of CATCH // Author: Vadim Zeitlin // Created: 2017-10-30 // Copyright: (c) 2017 Vadim Zeitlin ///////////////////////////////////////////////////////////////////////////// #ifndef _WX_CATCH_CPPUNIT_H_ #define _WX_CATCH_CPPUNIT_H_ #include "catch.hpp" // CppUnit-compatible macros. // CPPUNIT_ASSERTs are mapped to REQUIRE(), not CHECK(), as this is how CppUnit // works but in many cases they really should be CHECK()s instead, i.e. the // test should continue to run even if one assert failed. Unfortunately there // is no automatic way to know it, so the existing code will need to be // reviewed and CHECK() used explicitly where appropriate. // // Also notice that we don't use parentheses around x and y macro arguments in // the macro expansion, as usual. This is because these parentheses would then // appear in CATCH error messages if the assertion fails, making them much less // readable and omitting should be fine here, exceptionally, as the arguments // of these macros are usually just simple expressions. #define CPPUNIT_ASSERT(cond) REQUIRE(cond) #define CPPUNIT_ASSERT_EQUAL(x, y) REQUIRE(x == y) // Using INFO() disallows putting more than one of these macros on the same // line but this can happen if they're used inside another macro, so wrap it // inside a scope. #define CPPUNIT_ASSERT_MESSAGE(msg, cond) \ do { INFO(msg); REQUIRE(cond); } while (Catch::alwaysFalse()) #define CPPUNIT_ASSERT_EQUAL_MESSAGE(msg, x, y) \ do { INFO(msg); REQUIRE(x == y); } while (Catch::alwaysFalse()) // CATCH Approx class uses the upper bound of "epsilon*(scale + max(|x|, |y|))" // for |x - y| which is not really compatible with our fixed delta, so we can't // use it here. #define CPPUNIT_ASSERT_DOUBLES_EQUAL(x, y, delta) \ REQUIRE(std::abs(x - y) < delta) #define CPPUNIT_FAIL(msg) FAIL(msg) #define CPPUNIT_ASSERT_THROW(expr, exception) \ try \ { \ expr; \ FAIL("Expected exception of type " #exception \ " not thrown by " #expr); \ } \ catch ( exception& ) {} // Define conversions to strings for some common wxWidgets types. namespace Catch { template <> struct StringMaker { static std::string convert(wxUniChar uc) { return wxString(uc).ToStdString(wxConvUTF8); } }; template <> struct StringMaker { static std::string convert(wxUniCharRef ucr) { return wxString(ucr).ToStdString(wxConvUTF8); } }; // While this conversion already works due to the existence of the stream // insertion operator for wxString, define a custom one making it more // obvious when strings containing non-printable characters differ. template <> struct StringMaker { static std::string convert(const wxString& wxs) { std::string s; s.reserve(wxs.length()); for ( wxString::const_iterator i = wxs.begin(); i != wxs.end(); ++i ) { #if wxUSE_UNICODE if ( !iswprint(*i) ) s += wxString::Format(wxASCII_STR("\\u%04X"), *i).ToAscii(); else #endif // wxUSE_UNICODE s += *i; } return s; } }; } // Use a different namespace for our mock ups of the real declarations in // CppUnit namespace to avoid clashes if we end up being linked with the real // CppUnit library, but bring it into scope with a using directive below to // make it possible to compile the original code using CppUnit unmodified. namespace CatchCppUnit { namespace CppUnit { // These classes don't really correspond to the real CppUnit ones, but contain // just the minimum we need to make CPPUNIT_TEST() macro and our mock up of // TestSuite class work. class Test { public: // Name argument exists only for compatibility with the real CppUnit but is // not used here. explicit Test(const std::string& name = std::string()) : m_name(name) { } virtual ~Test() { } virtual void runTest() = 0; const std::string& getName() const { return m_name; } private: std::string m_name; }; class TestCase : public Test { public: explicit TestCase(const std::string& name = std::string()) : Test(name) { } virtual void setUp() {} virtual void tearDown() {} }; class TestSuite : public Test { public: explicit TestSuite(const std::string& name = std::string()) : Test(name) { } ~TestSuite() { for ( size_t n = 0; n < m_tests.size(); ++n ) { delete m_tests[n]; } } void addTest(Test* test) { m_tests.push_back(test); } size_t getChildTestCount() const { return m_tests.size(); } void runTest() wxOVERRIDE { for ( size_t n = 0; n < m_tests.size(); ++n ) { m_tests[n]->runTest(); } } private: std::vector m_tests; wxDECLARE_NO_COPY_CLASS(TestSuite); }; } // namespace CppUnit } // namespace CatchCppUnit using namespace CatchCppUnit; // Helpers used in the implementation of the macros below. namespace wxPrivate { // An object which resets a string to its old value when going out of scope. class TempStringAssign { public: explicit TempStringAssign(std::string& str, const char* value) : m_str(str), m_orig(str) { str = value; } ~TempStringAssign() { m_str = m_orig; } private: std::string& m_str; const std::string m_orig; wxDECLARE_NO_COPY_CLASS(TempStringAssign); }; // These two strings are used to implement wxGetCurrentTestName() and must be // defined in the test driver. extern std::string wxTheCurrentTestClass, wxTheCurrentTestMethod; } // namespace wxPrivate inline std::string wxGetCurrentTestName() { std::string s = wxPrivate::wxTheCurrentTestClass; if ( !s.empty() && !wxPrivate::wxTheCurrentTestMethod.empty() ) s += "::"; s += wxPrivate::wxTheCurrentTestMethod; return s; } // Notice that the use of this macro unconditionally changes the protection for // everything that follows it to "public". This is necessary to allow taking // the address of the runTest() method in CPPUNIT_TEST_SUITE_REGISTRATION() // below and there just doesn't seem to be any way around it. #define CPPUNIT_TEST_SUITE(testclass) \ public: \ void runTest() wxOVERRIDE \ { \ using namespace wxPrivate; \ TempStringAssign setClass(wxTheCurrentTestClass, #testclass) #define CPPUNIT_TEST(testname) \ SECTION(#testname) \ { \ TempStringAssign setMethod(wxTheCurrentTestMethod, #testname); \ setUp(); \ try \ { \ testname(); \ } \ catch ( ... ) \ { \ tearDown(); \ throw; \ } \ tearDown(); \ } #define CPPUNIT_TEST_SUITE_END() \ } \ struct EatNextSemicolon #define wxREGISTER_UNIT_TEST_WITH_TAGS(testclass, tags) \ METHOD_AS_TEST_CASE(testclass::runTest, #testclass, tags) \ struct EatNextSemicolon #define CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(testclass, suitename) \ wxREGISTER_UNIT_TEST_WITH_TAGS(testclass, "[" suitename "]") // Existings tests always use both this macro and the named registration one // above, but we can't register the same test case twice with CATCH, so simply // ignore this one. #define CPPUNIT_TEST_SUITE_REGISTRATION(testclass) \ struct EatNextSemicolon // ---------------------------------------------------------------------------- // wxWidgets-specific macros // ---------------------------------------------------------------------------- // Convenient variant of INFO() which uses wxString::Format() internally. #define wxINFO_FMT_HELPER(fmt, ...) \ wxString::Format(fmt, __VA_ARGS__).ToStdString(wxConvUTF8) #define wxINFO_FMT(...) INFO(wxINFO_FMT_HELPER(__VA_ARGS__)) // Use this macro to assert with the given formatted message (it should contain // the format string and arguments in a separate pair of parentheses) #define WX_ASSERT_MESSAGE(msg, cond) \ CPPUNIT_ASSERT_MESSAGE(std::string(wxString::Format msg .mb_str(wxConvLibc)), (cond)) #define WX_ASSERT_EQUAL_MESSAGE(msg, expected, actual) \ CPPUNIT_ASSERT_EQUAL_MESSAGE(std::string(wxString::Format msg .mb_str(wxConvLibc)), \ (expected), (actual)) #endif // _WX_CATCH_CPPUNIT_H_