2015-11-27 19:24:00 +01:00
|
|
|
|
/*
|
2015-11-28 19:20:10 +01:00
|
|
|
|
EVENT-LOG.hpp - test facility to verify the occurrence of expected events
|
2015-11-27 19:24:00 +01:00
|
|
|
|
|
|
|
|
|
|
Copyright (C) Lumiera.org
|
2015-11-28 19:20:10 +01:00
|
|
|
|
2015, Hermann Vosseler <Ichthyostega@web.de>
|
2015-11-27 19:24:00 +01:00
|
|
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
|
|
|
|
modify it under the terms of the GNU General Public License as
|
|
|
|
|
|
published by the Free Software Foundation; either version 2 of
|
|
|
|
|
|
the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
|
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-11-28 19:20:10 +01:00
|
|
|
|
/** @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
|
2015-12-09 01:18:15 +01:00
|
|
|
|
** implemented as a "PImpl" to allow sharing of logs, which helps to trace
|
|
|
|
|
|
** events from transient UI elements and from destructor code. The front-end
|
|
|
|
|
|
** used for access offers a query facility, so the test code may express some
|
2015-11-28 19:20:10 +01:00
|
|
|
|
** expected patterns of incidence and verify match or non-match.
|
|
|
|
|
|
**
|
2015-12-09 01:18:15 +01:00
|
|
|
|
** Failure of match prints a detailed trace message to `STDERR`, in order to
|
2015-11-28 19:20:10 +01:00
|
|
|
|
** 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
|
|
|
|
|
|
**
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-11-28 18:36:35 +01:00
|
|
|
|
#ifndef LIB_TEST_EVENT_LOG_H
|
|
|
|
|
|
#define LIB_TEST_EVENT_LOG_H
|
2015-11-27 19:24:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
2015-11-28 19:20:10 +01:00
|
|
|
|
#include "lib/error.hpp"
|
2015-11-28 23:50:56 +01:00
|
|
|
|
#include "lib/idi/entry-id.hpp"
|
2015-12-06 03:35:07 +01:00
|
|
|
|
#include "lib/iter-adapter.hpp"
|
|
|
|
|
|
#include "lib/iter-cursor.hpp"
|
2015-12-05 03:13:01 +01:00
|
|
|
|
#include "lib/format-string.hpp"
|
|
|
|
|
|
#include "lib/format-util.hpp"
|
|
|
|
|
|
#include "lib/diff/record.hpp"
|
2015-12-12 01:01:46 +01:00
|
|
|
|
#include "lib/symbol.hpp"
|
2015-12-02 23:37:54 +01:00
|
|
|
|
#include "lib/util.hpp"
|
2015-11-27 19:24:00 +01:00
|
|
|
|
|
2015-11-28 19:20:10 +01:00
|
|
|
|
//#include <boost/lexical_cast.hpp>
|
2015-12-08 03:20:52 +01:00
|
|
|
|
#include <iostream>
|
2015-12-08 22:14:29 +01:00
|
|
|
|
#include <memory>
|
2015-11-28 23:50:56 +01:00
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <string>
|
2015-11-27 19:24:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace lib {
|
|
|
|
|
|
namespace test{
|
2015-12-02 23:37:54 +01:00
|
|
|
|
namespace error = lumiera::error;
|
2015-11-27 19:24:00 +01:00
|
|
|
|
|
2015-11-28 19:20:10 +01:00
|
|
|
|
// using lib::Literal;
|
2015-11-28 23:50:56 +01:00
|
|
|
|
using std::string;
|
2015-12-05 02:40:03 +01:00
|
|
|
|
using util::contains;
|
2015-12-02 23:37:54 +01:00
|
|
|
|
using util::isnil;
|
2015-12-05 03:13:01 +01:00
|
|
|
|
using util::_Fmt;
|
2015-12-12 01:01:46 +01:00
|
|
|
|
using lib::Symbol;
|
2015-11-28 19:20:10 +01:00
|
|
|
|
// using std::rand;
|
2015-11-27 19:24:00 +01:00
|
|
|
|
|
2015-12-12 01:01:46 +01:00
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
|
|
|
|
template<class CON>
|
|
|
|
|
|
inline void
|
|
|
|
|
|
stringify(CON&)
|
|
|
|
|
|
{
|
|
|
|
|
|
/* NOP */
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<class CON, typename X, typename...ARGS>
|
|
|
|
|
|
inline void
|
|
|
|
|
|
stringify(CON& container, X const& elm, ARGS const& ...args)
|
|
|
|
|
|
{
|
|
|
|
|
|
container.emplace_back (util::str(elm));
|
|
|
|
|
|
stringify (container, args...);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-11-28 23:50:56 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* @internal ongoing evaluation and match within an [EventLog].
|
|
|
|
|
|
* @throws error::Fatal when the expected match fails (error::LUMIERA_ERROR_ASSERTION)
|
|
|
|
|
|
*/
|
|
|
|
|
|
class EventMatch
|
|
|
|
|
|
{
|
2015-12-05 02:07:25 +01:00
|
|
|
|
using Entry = lib::diff::Record<string>;
|
|
|
|
|
|
using Log = std::vector<Entry>;
|
2015-12-06 03:35:07 +01:00
|
|
|
|
using Iter = lib::IterCursor<Log::const_iterator>;
|
2015-12-05 02:07:25 +01:00
|
|
|
|
using Filter = ExtensibleFilterIter<Iter>;
|
|
|
|
|
|
|
2015-12-05 03:13:01 +01:00
|
|
|
|
/** match predicate evaluator */
|
2015-12-05 02:07:25 +01:00
|
|
|
|
Filter solution_;
|
2015-12-02 23:37:54 +01:00
|
|
|
|
|
2015-12-05 03:13:01 +01:00
|
|
|
|
/** record last match for diagnostics */
|
|
|
|
|
|
string lastMatch_;
|
|
|
|
|
|
|
2015-12-08 01:10:02 +01:00
|
|
|
|
/** support for positive and negative queries.
|
|
|
|
|
|
* @note negative queries enforced in dtor */
|
2015-12-08 03:20:52 +01:00
|
|
|
|
bool look_for_match_;
|
|
|
|
|
|
|
|
|
|
|
|
string violation_;
|
2015-12-08 01:10:02 +01:00
|
|
|
|
|
2015-12-05 03:13:01 +01:00
|
|
|
|
/** core of the evaluation machinery:
|
|
|
|
|
|
* apply a filter predicate and then pull
|
|
|
|
|
|
* through the log to find a acceptable entry
|
|
|
|
|
|
*/
|
2015-12-02 23:37:54 +01:00
|
|
|
|
bool
|
2015-12-08 01:10:02 +01:00
|
|
|
|
foundSolution()
|
2015-12-02 23:37:54 +01:00
|
|
|
|
{
|
2015-12-05 02:07:25 +01:00
|
|
|
|
return !isnil (solution_);
|
2015-12-02 23:37:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-05 03:13:01 +01:00
|
|
|
|
/** this is actually called after each refinement
|
2015-12-08 03:20:52 +01:00
|
|
|
|
* of the filter and matching conditions. The effect is to search
|
|
|
|
|
|
* for an (intermediary) solution right away and to mark failure
|
|
|
|
|
|
* as soon as some condition can not be satisfied. Rationale is to
|
2015-12-05 03:13:01 +01:00
|
|
|
|
* indicate the point where a chained match fails
|
2015-12-08 03:20:52 +01:00
|
|
|
|
* @see ::operator bool() for extracting the result
|
2015-12-05 03:13:01 +01:00
|
|
|
|
* @par matchSpec diagnostics description of the predicate just being added
|
|
|
|
|
|
* @par rel indication of the searching relation / direction
|
|
|
|
|
|
*/
|
2015-12-02 23:37:54 +01:00
|
|
|
|
void
|
2015-12-08 03:20:52 +01:00
|
|
|
|
evaluateQuery (string matchSpec, Literal rel = "after")
|
2015-12-02 23:37:54 +01:00
|
|
|
|
{
|
2015-12-08 03:20:52 +01:00
|
|
|
|
if (look_for_match_ and not isnil (violation_)) return;
|
|
|
|
|
|
// already failed, no further check necessary
|
|
|
|
|
|
|
2015-12-08 01:10:02 +01:00
|
|
|
|
if (foundSolution())
|
2015-12-08 03:20:52 +01:00
|
|
|
|
{
|
|
|
|
|
|
lastMatch_ = matchSpec+" @ "+string(*solution_)
|
|
|
|
|
|
+ (isnil(lastMatch_)? ""
|
|
|
|
|
|
: "\n.."+rel+" "+lastMatch_);
|
|
|
|
|
|
if (not look_for_match_)
|
|
|
|
|
|
violation_ = "FOUND at least "+lastMatch_;
|
|
|
|
|
|
}
|
2015-12-08 01:10:02 +01:00
|
|
|
|
else
|
2015-12-08 03:20:52 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (look_for_match_)
|
|
|
|
|
|
violation_ = "FAILED to "+matchSpec
|
|
|
|
|
|
+ "\n.."+rel
|
|
|
|
|
|
+ " "+lastMatch_;
|
|
|
|
|
|
else
|
|
|
|
|
|
violation_ = "";
|
|
|
|
|
|
}
|
2015-12-02 23:37:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-08 03:20:52 +01:00
|
|
|
|
|
|
|
|
|
|
/** @internal for creating EventLog matchers */
|
2015-12-06 03:35:07 +01:00
|
|
|
|
EventMatch(Log const& srcSeq)
|
|
|
|
|
|
: solution_(Iter(srcSeq))
|
2015-12-05 03:13:01 +01:00
|
|
|
|
, lastMatch_("HEAD "+ solution_->get("ID"))
|
2015-12-08 03:20:52 +01:00
|
|
|
|
, look_for_match_(true)
|
|
|
|
|
|
, violation_()
|
2015-12-05 02:40:03 +01:00
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
friend class EventLog;
|
|
|
|
|
|
|
2015-11-28 23:50:56 +01:00
|
|
|
|
|
2015-12-05 02:40:03 +01:00
|
|
|
|
/* == elementary matchers == */
|
|
|
|
|
|
|
|
|
|
|
|
auto
|
|
|
|
|
|
find (string match)
|
|
|
|
|
|
{
|
2015-12-05 03:13:01 +01:00
|
|
|
|
return [=](Entry const& entry)
|
2015-12-05 02:40:03 +01:00
|
|
|
|
{
|
|
|
|
|
|
return contains (string(entry), match);
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2015-12-08 03:20:52 +01:00
|
|
|
|
/** final evaluation of the match query,
|
|
|
|
|
|
* usually triggered from the unit test `CHECK()`.
|
|
|
|
|
|
* @note failure cause is printed to STDERR.
|
|
|
|
|
|
*/
|
|
|
|
|
|
operator bool() const
|
2015-12-08 01:10:02 +01:00
|
|
|
|
{
|
2015-12-08 03:20:52 +01:00
|
|
|
|
if (!isnil (violation_))
|
|
|
|
|
|
{
|
|
|
|
|
|
std::cerr << "__Log_condition_violated__\n"+violation_ <<"\n";
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
2015-12-08 01:10:02 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-05 03:37:25 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* find a match (substring match) of the given text
|
|
|
|
|
|
* in an EventLog entry after the current position
|
|
|
|
|
|
* @remarks the name of this junctor might seem counter intuitive;
|
|
|
|
|
|
* it was chosen due to expected DSL usage: `log.verify("α").before("β")`.
|
|
|
|
|
|
* Operationally this means first to find a Record matching the substring "α"
|
|
|
|
|
|
* and then to forward from this point until hitting a record to match "β".
|
|
|
|
|
|
*/
|
2015-11-28 23:50:56 +01:00
|
|
|
|
EventMatch&
|
|
|
|
|
|
before (string match)
|
|
|
|
|
|
{
|
2015-12-06 03:35:07 +01:00
|
|
|
|
solution_.underlying().switchForwards();
|
2015-12-05 03:37:25 +01:00
|
|
|
|
solution_.setNewFilter(find(match));
|
2015-12-08 03:20:52 +01:00
|
|
|
|
evaluateQuery ("match(\""+match+"\")");
|
2015-12-05 03:37:25 +01:00
|
|
|
|
return *this;
|
2015-11-28 23:50:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
beforeMatch (string regExp)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process combined relational regular expression match");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
beforeEvent (string match)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process combined relational match");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-11 20:02:30 +01:00
|
|
|
|
EventMatch&
|
|
|
|
|
|
beforeEvent (string classifier, string match)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process combined relational match");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-11-28 23:50:56 +01:00
|
|
|
|
EventMatch&
|
|
|
|
|
|
beforeCall (string match)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process combined relational match");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
after (string match)
|
|
|
|
|
|
{
|
2015-12-06 03:35:07 +01:00
|
|
|
|
solution_.underlying().switchBackwards();
|
|
|
|
|
|
solution_.setNewFilter(find(match));
|
2015-12-08 03:20:52 +01:00
|
|
|
|
evaluateQuery ("match(\""+match+"\")", "before");
|
2015-12-06 03:35:07 +01:00
|
|
|
|
return *this;
|
2015-11-28 23:50:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
afterMatch (string regExp)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process combined relational regular expression match backwards");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
afterEvent (string match)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process combined relational match backwards");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-11 20:02:30 +01:00
|
|
|
|
EventMatch&
|
|
|
|
|
|
afterEvent (string classifier, string match)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process combined relational match backwards");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-11-28 23:50:56 +01:00
|
|
|
|
EventMatch&
|
|
|
|
|
|
afterCall (string match)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process combined relational match backwards");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
arg ()
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process match on no-arg call argument list");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename... MORE>
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
arg (string match, MORE...args)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process match on call argument list");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename... MORE>
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
argMatch (string regExp, MORE...args)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process regular expression match on call argument list");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
2015-12-11 20:02:30 +01:00
|
|
|
|
type (string typeID)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process additional filter on type of the log entry");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
key (string key)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process additional filter on the presence of a specific key");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
attrib (string key, string valueMatch)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process additional filter on a specific attribute of the log entry");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
id (string classifier)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process additional filter on ID classifier of log entry");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
on (string targetID)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process additional filter on source of log entry");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename X>
|
|
|
|
|
|
EventMatch&
|
|
|
|
|
|
on (const X *const targetObj)
|
2015-11-28 23:50:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("process additional filter on source of log entry");
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2015-11-27 19:24:00 +01:00
|
|
|
|
|
2015-12-09 01:18:15 +01:00
|
|
|
|
|
2015-11-28 19:20:10 +01:00
|
|
|
|
/**
|
|
|
|
|
|
* Helper to log and verify the occurrence of events.
|
2015-12-09 01:18:15 +01:00
|
|
|
|
* The EventLog object is a front-end handle, logging flexible
|
|
|
|
|
|
* [information records][lib::Record] into a possibly shared (vector)
|
|
|
|
|
|
* buffer in heap storage. An extended query DSL allows to write
|
|
|
|
|
|
* assertions to cover the occurrence of events in unit tests.
|
|
|
|
|
|
* @see TestEventLog_test
|
2015-11-27 19:24:00 +01:00
|
|
|
|
*/
|
2015-11-28 19:20:10 +01:00
|
|
|
|
class EventLog
|
|
|
|
|
|
{
|
2015-11-28 23:50:56 +01:00
|
|
|
|
using Entry = lib::diff::Record<string>;
|
|
|
|
|
|
using Log = std::vector<Entry>;
|
2015-12-05 02:40:03 +01:00
|
|
|
|
using Iter = lib::RangeIter<Log::const_iterator>;
|
2015-11-28 23:50:56 +01:00
|
|
|
|
|
2015-12-08 22:14:29 +01:00
|
|
|
|
std::shared_ptr<Log> log_;
|
2015-11-28 23:50:56 +01:00
|
|
|
|
|
2015-12-02 01:31:37 +01:00
|
|
|
|
void
|
2015-12-09 01:18:15 +01:00
|
|
|
|
log (std::initializer_list<string> const& ili)
|
2015-12-02 01:31:37 +01:00
|
|
|
|
{
|
2015-12-08 22:14:29 +01:00
|
|
|
|
log_->emplace_back(ili);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-12 01:01:46 +01:00
|
|
|
|
template<typename ATTR, typename ARGS>
|
|
|
|
|
|
void
|
|
|
|
|
|
log (Symbol typeID, ATTR&& attribs, ARGS&& args)
|
|
|
|
|
|
{
|
|
|
|
|
|
log_->emplace_back(typeID, std::forward<ATTR>(attribs)
|
|
|
|
|
|
, std::forward<ARGS>(args));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-08 22:14:29 +01:00
|
|
|
|
string
|
|
|
|
|
|
getID() const
|
|
|
|
|
|
{
|
2015-12-09 01:18:15 +01:00
|
|
|
|
return log_->front().get("ID");
|
2015-12-02 01:31:37 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-11-28 23:50:56 +01:00
|
|
|
|
public:
|
|
|
|
|
|
explicit
|
|
|
|
|
|
EventLog (string logID)
|
2015-12-08 22:14:29 +01:00
|
|
|
|
: log_(new Log)
|
2015-11-28 23:50:56 +01:00
|
|
|
|
{
|
2015-12-08 22:14:29 +01:00
|
|
|
|
log({"type=EventLogHeader", "ID="+logID});
|
2015-11-28 23:50:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-06 03:35:07 +01:00
|
|
|
|
explicit
|
|
|
|
|
|
EventLog (const char* logID)
|
|
|
|
|
|
: EventLog(string(logID))
|
|
|
|
|
|
{ }
|
|
|
|
|
|
|
2015-11-28 23:50:56 +01:00
|
|
|
|
template<class X>
|
|
|
|
|
|
explicit
|
2015-12-02 01:31:37 +01:00
|
|
|
|
EventLog (const X *const obj)
|
|
|
|
|
|
: EventLog(idi::instanceTypeID (obj))
|
2015-11-28 23:50:56 +01:00
|
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// standard copy operations acceptable
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-12-09 01:18:15 +01:00
|
|
|
|
/** Merge this log into another log, forming a combined log
|
|
|
|
|
|
* @param otherLog target to integrate this log's contents into.
|
|
|
|
|
|
* @return reference to the merged new log instance
|
|
|
|
|
|
* @remarks EventLog uses a heap based, sharable log storage,
|
|
|
|
|
|
* where the EventLog object is just a front-end (shared ptr).
|
|
|
|
|
|
* The `joinInto` operation both integrates this logs contents
|
|
|
|
|
|
* into the other log, and then disconnects from the old storage
|
|
|
|
|
|
* and connects to the storage of the combined log.
|
|
|
|
|
|
* @warning beware of clone copies. Since copying EventLog is always a
|
|
|
|
|
|
* shallow copy, all copied handles actually refer to the same
|
|
|
|
|
|
* log storage. If you invoke `joinInto` in such a situation,
|
|
|
|
|
|
* only the current EventLog front-end handle will be rewritten
|
|
|
|
|
|
* to point to the combined log, while any other clone will
|
|
|
|
|
|
* continue to point to the original log storage.
|
|
|
|
|
|
* @see TestEventLog_test::verify_logJoining()
|
|
|
|
|
|
*/
|
2015-11-28 23:50:56 +01:00
|
|
|
|
EventLog&
|
2015-12-09 01:18:15 +01:00
|
|
|
|
joinInto (EventLog& otherLog)
|
2015-11-28 23:50:56 +01:00
|
|
|
|
{
|
2015-12-09 01:18:15 +01:00
|
|
|
|
Log& target = *otherLog.log_;
|
|
|
|
|
|
target.reserve (target.size() + log_->size() + 1);
|
|
|
|
|
|
target.emplace_back (log_->front());
|
|
|
|
|
|
auto p = log_->begin();
|
|
|
|
|
|
while (++p != log_->end()) // move our log's content into the other log
|
|
|
|
|
|
target.emplace_back(std::move(*p));
|
|
|
|
|
|
this->log_->resize(1);
|
|
|
|
|
|
this->log({"type=joined", otherLog.getID()}); // leave a tag to indicate
|
|
|
|
|
|
otherLog.log({"type=logJoin", this->getID()}); // where the `joinInto` took place,
|
|
|
|
|
|
this->log_ = otherLog.log_; // connect this to the other storage
|
2015-11-28 23:50:56 +01:00
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ==== Logging API ==== */
|
|
|
|
|
|
|
2015-12-11 20:02:30 +01:00
|
|
|
|
using ArgSeq = lib::diff::RecordSetup<string>::Storage;
|
|
|
|
|
|
|
2015-11-28 23:50:56 +01:00
|
|
|
|
EventLog&
|
|
|
|
|
|
event (string text)
|
|
|
|
|
|
{
|
2015-12-02 01:31:37 +01:00
|
|
|
|
log({"type=event", text});
|
2015-11-28 23:50:56 +01:00
|
|
|
|
return *this;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-11 20:02:30 +01:00
|
|
|
|
EventLog&
|
|
|
|
|
|
event (string classifier, string text)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED ("Log event with additional classifier");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-12 01:01:46 +01:00
|
|
|
|
/** Log occurrence of a function call with no arguments.
|
|
|
|
|
|
* @param target the object or scope on which the function is invoked
|
|
|
|
|
|
* @param function name of the function being invoked
|
|
|
|
|
|
*/
|
2015-12-11 20:02:30 +01:00
|
|
|
|
EventLog&
|
|
|
|
|
|
call (string target, string function)
|
|
|
|
|
|
{
|
2015-12-12 01:01:46 +01:00
|
|
|
|
return call(target, function, ArgSeq());
|
2015-12-11 20:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-12 01:01:46 +01:00
|
|
|
|
/** Log a function call with a sequence of stringified arguments */
|
2015-12-11 20:02:30 +01:00
|
|
|
|
EventLog&
|
2015-12-12 01:01:46 +01:00
|
|
|
|
call (string target, string function, ArgSeq&& args)
|
2015-12-11 20:02:30 +01:00
|
|
|
|
{
|
2015-12-12 01:01:46 +01:00
|
|
|
|
log ("call", ArgSeq{"fun="+function, "this="+target}, std::forward<ArgSeq>(args));
|
|
|
|
|
|
return *this;
|
2015-12-11 20:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-12 01:01:46 +01:00
|
|
|
|
/** Log a function call with arbitrary arguments */
|
2015-12-11 20:02:30 +01:00
|
|
|
|
template<typename...ARGS>
|
|
|
|
|
|
EventLog&
|
2015-12-12 01:01:46 +01:00
|
|
|
|
call (string target, string function, ARGS const& ...args)
|
2015-12-11 20:02:30 +01:00
|
|
|
|
{
|
2015-12-12 01:01:46 +01:00
|
|
|
|
ArgSeq argSeq;
|
|
|
|
|
|
argSeq.reserve(sizeof...(ARGS));
|
2015-12-12 01:24:52 +01:00
|
|
|
|
stringify (argSeq, args...);
|
2015-12-12 01:01:46 +01:00
|
|
|
|
return call (target, function, std::move(argSeq));
|
2015-12-11 20:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-12 01:01:46 +01:00
|
|
|
|
/** Log a function call on given object ("`this`")... */
|
2015-12-11 20:02:30 +01:00
|
|
|
|
template<class X, typename...ARGS>
|
|
|
|
|
|
EventLog&
|
2015-12-12 01:01:46 +01:00
|
|
|
|
call (const X *const targetObj, string function, ARGS const& ...args)
|
|
|
|
|
|
{
|
|
|
|
|
|
return call (idi::instanceTypeID (targetObj), function, args...);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename...ARGS>
|
|
|
|
|
|
EventLog&
|
|
|
|
|
|
call (const char* target, string function, ARGS const& ...args)
|
2015-12-11 20:02:30 +01:00
|
|
|
|
{
|
2015-12-12 01:01:46 +01:00
|
|
|
|
return call (string(target), function, args...);
|
2015-12-11 20:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
template<typename...ELMS>
|
|
|
|
|
|
EventLog&
|
2015-12-12 01:24:52 +01:00
|
|
|
|
note (ELMS const& ...initialiser)
|
2015-12-11 20:02:30 +01:00
|
|
|
|
{
|
2015-12-12 01:24:52 +01:00
|
|
|
|
ArgSeq args;
|
|
|
|
|
|
args.reserve(sizeof...(ELMS));
|
|
|
|
|
|
stringify (args, initialiser...);
|
|
|
|
|
|
log_->emplace_back(args);
|
|
|
|
|
|
return *this;
|
2015-12-11 20:02:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EventLog&
|
|
|
|
|
|
warn (string text)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED ("Log a warning entry");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EventLog&
|
|
|
|
|
|
error (string text)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED ("Log an error note");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EventLog&
|
|
|
|
|
|
fatal (string text)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED ("Log a fatal failure");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EventLog&
|
|
|
|
|
|
create (string text)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED ("Log the creation of an object");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EventLog&
|
|
|
|
|
|
destroy (string text)
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED ("Log the destruction of an object");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-11-28 23:50:56 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ==== Iteration ==== */
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
|
empty() const
|
|
|
|
|
|
{
|
2015-12-08 22:14:29 +01:00
|
|
|
|
return 1 >= log_->size();
|
2015-11-28 23:50:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
typedef Iter const_iterator;
|
|
|
|
|
|
typedef const Entry value_type;
|
|
|
|
|
|
|
2015-12-08 22:14:29 +01:00
|
|
|
|
const_iterator begin() const { return Iter(log_->begin(), log_->end()); }
|
2015-12-05 02:40:03 +01:00
|
|
|
|
const_iterator end() const { return Iter(); }
|
2015-11-28 23:50:56 +01:00
|
|
|
|
|
|
|
|
|
|
friend const_iterator begin (EventLog const& log) { return log.begin(); }
|
|
|
|
|
|
friend const_iterator end (EventLog const& log) { return log.end(); }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* ==== Query/Verification API ==== */
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch
|
2015-12-06 04:37:41 +01:00
|
|
|
|
verify (string match) const
|
2015-11-28 23:50:56 +01:00
|
|
|
|
{
|
2015-12-08 22:14:29 +01:00
|
|
|
|
EventMatch matcher(*log_);
|
2015-12-05 03:37:25 +01:00
|
|
|
|
matcher.before (match);
|
2015-12-05 02:40:03 +01:00
|
|
|
|
return matcher;
|
2015-11-28 23:50:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch
|
2015-12-06 04:37:41 +01:00
|
|
|
|
verifyMatch (string regExp) const
|
2015-11-28 23:50:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("start matching sequence for regular expression match");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
EventMatch
|
2015-12-06 04:37:41 +01:00
|
|
|
|
verifyEvent (string match) const
|
2015-11-28 23:50:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("start matching sequence");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-12-11 20:02:30 +01:00
|
|
|
|
EventMatch
|
|
|
|
|
|
verifyEvent (string classifier, string match) const
|
|
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("start matching sequence");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2015-11-28 23:50:56 +01:00
|
|
|
|
EventMatch
|
2015-12-06 04:37:41 +01:00
|
|
|
|
verifyCall (string match) const
|
2015-11-28 23:50:56 +01:00
|
|
|
|
{
|
|
|
|
|
|
UNIMPLEMENTED("start matching sequence");
|
|
|
|
|
|
}
|
2015-11-28 19:20:10 +01:00
|
|
|
|
|
2015-11-28 23:50:56 +01:00
|
|
|
|
EventMatch
|
2015-12-06 04:37:41 +01:00
|
|
|
|
ensureNot (string match) const
|
2015-11-28 23:50:56 +01:00
|
|
|
|
{
|
2015-12-08 22:14:29 +01:00
|
|
|
|
EventMatch matcher(*log_);
|
2015-12-08 03:20:52 +01:00
|
|
|
|
matcher.look_for_match_ = false; // flip logic; fail if match succeeds
|
2015-12-08 01:10:02 +01:00
|
|
|
|
matcher.before (match);
|
|
|
|
|
|
return matcher;
|
2015-11-28 23:50:56 +01:00
|
|
|
|
}
|
2015-12-06 04:21:35 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** equality comparison is based on the actual log contents */
|
|
|
|
|
|
friend bool
|
|
|
|
|
|
operator== (EventLog const& l1, EventLog const& l2)
|
|
|
|
|
|
{
|
2015-12-08 22:14:29 +01:00
|
|
|
|
return l1.log_ == l2.log_
|
|
|
|
|
|
or (l1.log_ and l2.log_
|
|
|
|
|
|
and *l1.log_ == *l2.log_);
|
2015-12-06 04:21:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
friend bool
|
|
|
|
|
|
operator!= (EventLog const& l1, EventLog const& l2)
|
|
|
|
|
|
{
|
|
|
|
|
|
return not (l1 == l2);
|
|
|
|
|
|
}
|
2015-11-28 19:20:10 +01:00
|
|
|
|
};
|
2015-11-27 19:24:00 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}} // namespace lib::test
|
2015-11-28 19:20:10 +01:00
|
|
|
|
#endif /*LIB_TEST_EVENT_LOG_H*/
|