diff --git a/src/lib/test/event-log.cpp b/src/lib/test/event-log.cpp index a198dcff0..7f7ee60d9 100644 --- a/src/lib/test/event-log.cpp +++ b/src/lib/test/event-log.cpp @@ -1,8 +1,8 @@ /* - Test-Helper - collection of functions supporting unit testing + EventLog - test facility to verify the occurrence of expected events Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2015, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,111 +21,24 @@ * *****************************************************/ -#include "lib/test/test-helper.hpp" -#include "lib/test/testdummy.hpp" -#include "lib/format-string.hpp" -#include "lib/unique-malloc-owner.hpp" +#include "lib/test/event-log.hpp" +//#include "lib/format-string.hpp" -#ifdef __GNUG__ -#include -#endif +//#include -#include - -using std::string; +//using std::string; namespace lib { namespace test{ - - string - showSizeof (size_t siz, const char* name) - { - static util::_Fmt fmt ("sizeof( %s ) %|30t|= %3d"); - return string (fmt % (name? name:"?") % siz); - } - - -#ifdef __GNUG__ - /** - * \par Implementation notes - * GCC / G++ subscribes to a cross-vendor ABI for C++, sometimes called the IA64 ABI - * because it happens to be the native ABI for that platform. It is summarised at - * \link http://www.codesourcery.com/cxx-abi/ mentor-embedded \endlink - * along with the current specification. For users of GCC greater than or equal to 3.x, - * entry points are exposed through the standard library in \c - * - * This implementation relies on a vendor neutral ABI for C++ compiled programs - * - * char* abi::__cxa_demangle(const char* mangled_name, - * char* output_buffer, size_t* length, - * int* status) - * - * Parameters: - * - \c mangled_name - * NUL-terminated character string containing the name to be demangled. - * - \c output_buffer - * region of memory, allocated with \c malloc, of `*length` bytes, - * into which the demangled name is stored. If \c output_buffer is not long enough, - * it is expanded using \c realloc. output_buffer may instead be NULL; in that case, - * the demangled name is placed in a region of memory allocated with \c malloc. - * - \c length - * If length is non-NULL, the length of the buffer containing the demangled name is placed in `*length`. - * - \c status - * error flag: `*status` is set to one of the following values: - * - * 0: The demangling operation succeeded. - * -1: A memory allocation failure occurred. - * -2: mangled_name is not a valid name under the C++ ABI mangling rules. - * -3: One of the arguments is invalid. - * - * The function returns a pointer to the start of the NUL-terminated demangled name, - * or NULL if the demangling fails. The caller is responsible for deallocating - * this memory using \c free. - */ - string - demangleCxx (Literal rawName) - { - int error = -4; - UniqueMallocOwner demangled (abi::__cxa_demangle (rawName, - NULL, - NULL, - &error)); - return 0==error? demangled.get() - : string(rawName); - } - -#else - string - demangleCxx (Literal rawName) - { - return string (rawName); - } -#endif - - - - /** @todo probably this can be done in a more clever way. Anyone...? - */ - string - randStr (size_t len) - { - static const string alpha ("aaaabbccddeeeeffgghiiiijjkkllmmnnooooppqqrrssttuuuuvvwwxxyyyyzz0123456789"); - static const size_t MAXAL (alpha.size()); + namespace { // internal details - string garbage(len,'\0'); - size_t p = len; - while (p) - garbage[--p] = alpha[rand() % MAXAL]; - return garbage; - } + } // internal details - /** storage for test-dummy flags */ - - long Dummy::_local_checksum = 0; - bool Dummy::_throw_in_ctor = false; +//Tangible::~Tangible() { } // Emit VTables here... + }} // namespace lib::test diff --git a/src/lib/test/event-log.hpp b/src/lib/test/event-log.hpp index cd8cb05bc..53dd1434d 100644 --- a/src/lib/test/event-log.hpp +++ b/src/lib/test/event-log.hpp @@ -1,8 +1,8 @@ /* - TEST-HELPER.hpp - collection of functions supporting unit testing + EVENT-LOG.hpp - test facility to verify the occurrence of expected events Copyright (C) Lumiera.org - 2009, Hermann Vosseler + 2015, Hermann Vosseler This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as @@ -21,198 +21,53 @@ */ +/** @file event-log.hpp + ** Support for verifying the occurrence of events from unit tests. + ** Typically used within special rigging and instrumentation for tests, + ** the [EventLog] allows to record invocations and similar events. It is + ** implemented as a "PImpl" to allow sharing of logs. The front-end used + ** for access offers a query facility, so the test code may express some + ** expected patterns of incidence and verify match or non-match. + ** + ** Failure of match deliberately throws an assertion failure, in order to + ** deliver a precise indication what part of the condition failed. + ** + ** @todo as of 11/2015 this is complete WIP-WIP-WIP + ** + ** @see ////TODO_test usage example + ** + */ + + #ifndef LIB_TEST_EVENT_LOG_H #define LIB_TEST_EVENT_LOG_H -#include "lib/symbol.hpp" -#include "lib/time/timevalue.hpp" +#include "lib/error.hpp" +//#include "lib/time/timevalue.hpp" -#include -#include -#include -#include +//#include +//#include namespace lib { namespace test{ - using lib::Literal; - using std::string; - using std::rand; +// using lib::Literal; +// using std::string; +// using std::rand; - - /** get a sensible display for a type or object - * @param obj object of the type in question - * @param name optional name to be used literally - * @return either the literal name without any further magic, - * or the result of compile-time or run time - * type identification as implemented by the compiler. + /** + * Helper to log and verify the occurrence of events. */ - template - inline Literal - showType (T const& obj, Literal name=0) - { - return name? name : Literal(typeid(obj).name()); - } + class EventLog + { + + }; - /** get a sensible display for a type - * @param name optional name to be used literally - * @return either the literal name without any further magic, - * or the result of compile-time or run time - * type identification as implemented by the compiler. - */ - template - inline Literal - showType (Literal name=0) - { - return name? name : Literal(typeid(T).name()); - } - - - /** reverse the effect of C++ name mangling. - * @return string in language-level form of a C++ type or object name, - * or a string with the original input if demangling fails. - * @warning implementation relies on the cross vendor C++ ABI in use - * by GCC and compatible compilers, so portability is limited. - * The implementation is accessed through libStdC++ - * Name representation in emitted object code and type IDs is - * essentially an implementation detail and subject to change. - */ - string - demangleCxx (Literal rawName); - - - /** short yet distinct name identifying the given type. - * @return demangled type-id without any scopes. */ - template - string - tyAbbr() - { - string typeStr = demangleCxx (showType()); - size_t pos = typeStr.rfind("::"); - if (pos != string::npos) - typeStr = typeStr.substr(pos+2); - return typeStr; - } - - template - string - tyAbbr(TY&&) - { - return tyAbbr(); - } - - - /** for printing sizeof(). - * prints the given size and name literally, without any further magic */ - string - showSizeof (size_t siz, const char* name); - - /** for printing sizeof(), trying to figure out the type name automatically */ - template - inline string - showSizeof(const char* name=0) - { - return showSizeof (sizeof (T), showType (name)); - } - - template - inline string - showSizeof(T const& obj, const char* name=0) - { - return showSizeof (sizeof (obj), showType (obj,name)); - } - - template - inline string - showSizeof(T *obj, const char* name=0) - { - return obj? showSizeof (*obj, name) - : showSizeof (name); - } - - - - /** helper to discern the kind of reference of the argument type */ - template - string - showRefKind() - { - return std::is_lvalue_reference::value? "REF" - : std::is_rvalue_reference::value? "MOV" - : "VAL"; - } - - /** helper for investigating a variadic argument pack - * @warning always spell out the template arguments explicitly - * when invoking this diagnostics, e.g. \c showVariadicTypes(args...) - * otherwise the template argument matching for functions might mess up the - * kind of reference you'll see in the diagnostics. - * @see test-helper-variadic-test.cpp - */ - template - inline string - showVariadicTypes () - { - return " :."; - } - - template - inline string - showVariadicTypes (X const& x, XS const&... xs) - { - return " :---#" - + boost::lexical_cast(1 + sizeof...(xs)) - + " -- Type: " + showType() - + " " + showRefKind() - + " Address* " + boost::lexical_cast(&x) - + "\n" - + showVariadicTypes (xs...); - } - - - - - - /** create a random but not insane Time value */ - inline lib::time::Time - randTime () - { - return lib::time::Time (500 * (rand() % 2), (rand() % 600) + 1); - } - - /** create garbage string of given length - * @return string containing arbitrary lower case letters and numbers - */ - string randStr (size_t len); }} // namespace lib::test - - - - -/* === test helper macros === */ - -/** - * Macro to verify a statement indeed raises an exception. - * If no exception is thrown, the #NOTREACHED macro will trigger - * an assertion failure. In case of an exception, the #lumiera_error - * state is checked, cleared and verified. - */ -#define VERIFY_ERROR(ERROR_ID, ERRONEOUS_STATEMENT) \ - try \ - { \ - ERRONEOUS_STATEMENT ; \ - NOTREACHED("expected '%s' failure in: %s", \ - #ERROR_ID, #ERRONEOUS_STATEMENT); \ - } \ - catch (...) \ - { \ - CHECK (lumiera_error_expect (LUMIERA_ERROR_##ERROR_ID));\ - } - -#endif +#endif /*LIB_TEST_EVENT_LOG_H*/ diff --git a/tests/library/test/test-event-log-test.cpp b/tests/library/test/test-event-log-test.cpp index 8fb41e6a8..dcba18388 100644 --- a/tests/library/test/test-event-log-test.cpp +++ b/tests/library/test/test-event-log-test.cpp @@ -23,22 +23,15 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" +#include "lib/format-util.hpp" #include "lib/error.hpp" -#include "lib/util-foreach.hpp" -#include -#include #include #include -using util::for_each; -using lumiera::Error; -using lumiera::LUMIERA_ERROR_EXCEPTION; +using util::join; using lumiera::error::LUMIERA_ERROR_ASSERTION; -using boost::algorithm::is_lower; -using boost::algorithm::is_digit; -using std::function; using std::string; using std::cout; using std::endl; @@ -61,88 +54,117 @@ namespace test{ int dontThrow() { return 2+2; } - /*********************************************//** - * verifies the proper working of helper functions - * frequently used within the Lumiera testsuite. - * @see test-helper.hpp + /***********************************************************//** + * @test verify a logging facility, which can be used to ensure + * some events happened while running test code. + * + * @see event-log.hpp */ class TestEventLog_test : public Test { void run (Arg) { - checkGarbageStr(); - checkTypeDisplay(); - checkThrowChecker(); + verify_simpleUsage(); } - /** @test prints "sizeof()" including some type name. */ - void - checkTypeDisplay () - { - std::cout << "Displaying types and sizes....\n"; - - typedef Wrmrmpft Wrmpf1; - typedef Wrmrmpft Wrmpf2; - typedef Wrmrmpft Wrmpf3; - - Wrmpf1 rmpf1; - Wrmpf2 rmpf2; - Wrmpf3 rmpf3; - Murpf murpf; - - CHECK (1 == sizeof (rmpf1)); - CHECK (2 == sizeof (rmpf2)); - CHECK (3 == sizeof (rmpf3)); - - cout << showSizeof((size_t)42, "theUniverse") << endl; - cout << showSizeof("just a char") << endl; - cout << showSizeof(murpf) << endl; - cout << showSizeof(rmpf1) << endl; - cout << showSizeof(rmpf2) << endl; - cout << showSizeof() << endl; - - Wrmpf1 *p1 = &rmpf1; - Wrmpf1 *p2 = 0; - cout << showSizeof(p1) << endl; - cout << showSizeof(p2) << endl; - } - - - - void - checkGarbageStr() + verify_simpleUsage () { - string garN = randStr(0); - CHECK (0 == garN.size()); + EventLog log(this); + CHECK (isnil (log)); - typedef function ChPredicate; - ChPredicate is_OK (is_lower() || is_digit()); + log.event("α"); + log.event("β"); + CHECK (!isnil(log)); - string garM = randStr(1000000); - for_each (garM, is_OK); + log.verify("α"); + log.verify("β"); + VERIFY_ERROR (ASSERTION, log.verify("γ")); - cout << randStr(80) << endl; + log.verify("α").before("β"); + VERIFY_ERROR (ASSERTION, verify("β").before("α")); + + CHECK (join(log) == "LOG::TestEventLog_test"+Log::instanceHash(this) + + ", Rec(event| |{α}), Rec(event| |{β})"); } - /** @test check the VERIFY_ERROR macro, - * which ensures a given error is raised. - */ + /** @test prints TODO */ void - checkThrowChecker() + checkTODO () { - // verify the exception is indeed raised - VERIFY_ERROR (EXCEPTION, doThrow() ); +#if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #975 + log.verifyEvent("ctor"); + log.verify("ctor").arg("dummy"); -#if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT - // and when actually no exception is raised, this is an ASSERTION failure - VERIFY_ERROR (ASSERTION, VERIFY_ERROR (EXCEPTION, dontThrow() )); -#endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT + CHECK ("dummy" == log.getID().getSym()); + CHECK ("ID-dummy" = string(log.getID())); + + VERIFY_ERROR (ASSERTION, log.verifyCall("reset")); + + log.reset(); + log.verify("reset"); + log.verifyCall("reset"); + log.verifyEvent("reset"); + log.verify("reset").after("ctor"); + log.verify("ctor").before("reset"); + VERIFY_ERROR (ASSERTION, log.verify("reset").before("ctor")); + VERIFY_ERROR (ASSERTION, log.verify("ctor").after("reset")); + + log.verify("reset").before("reset"); + log.verify("reset").beforeEvent("reset"); + log.verifyCall("reset").before("reset"); + log.verifyCall("reset").beforeEvent("reset"); + VERIFY_ERROR (ASSERTION, log.verifyCall("reset").afterCall("reset")); + VERIFY_ERROR (ASSERTION, log.verifyCall("reset").afterEvent("reset")); + VERIFY_ERROR (ASSERTION, log.verifyEvent("reset").afterEvent("reset")); + + CHECK (!log.isTouched()); + CHECK (!log.isExpanded()); + + log.noteMsg("dolorem ipsum quia dolor sit amet consectetur adipisci velit."); + log.verifyNote("Msg"); + log.verifyCall("noteMsg"); + log.verifyCall("noteMsg").arg("lorem ipsum"); + log.verifyCall("noteMsg").argMatch("dolor.+dolor\\s+"); + log.verifyMatch("Rec\\(note.+kind = Msg.+msg = dolorem ipsum"); + + EventLog log = log.getLog(); + log.verify("ctor") + .before("reset") + .before("lorem ipsum"); + + MockElm foo("foo"), bar; + foo.verify("ctor").arg("foo"); + bar.verify("ctor").arg(); + + bar.ensureNot("foo"); + log.ensureNot("foo"); + log.ensureNot("foo"); + VERIFY_ERROR (ASSERTION, foo.ensureNot("foo")); + + log.join(bar).join(foo); + log.verifyEvent("logJoin").arg(bar.getID()) + .beforeEvent("logJoin").arg("foo"); + + log.verifyEvent("logJoin").arg(bar.getID()) + .beforeEvent("logJoin").arg("foo"); + log.verify("ctor").arg("foo"); + log.verify("ctor").arg("foo"); + log.verify("ctor").arg("dummy") + .before("ctor").arg(bar.getID()) + .before("ctor").arg("foo"); + + log.kill(); + foo.noteMsg("dummy killed"); + log.verifyEvent("dtor").on("dummy") + .beforeCall("noteMsg").on("foo"); + // and when actually no exception is raised, this is an ASSERTION failure + VERIFY_ERROR (ASSERTION, VERIFY_ERROR (EXCEPTION, dontThrow() )); +#endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #975 } - }; LAUNCHER (TestEventLog_test, "unit common");