Activity-Lang: investigate ways to verify invocation sequences
The EventLog seems to provide all the building blocks, but we need some higher level special matchers (and maybe we also want to hide some of the basic EventLog matchers). A soulution might be to wrap the EventMatcher and delegate all follow-up builder calls. This seems adequate, since the EventLog-Matcher is basically used as black box, building up more elaborate matchers from the provided basic matchers... Spent some time again to understand how EventLog matching works. My feelings towards this piece of code are always the same: it is somewhat too "tricky", but I am not aware of any other technique to get this degree of elaborate chained matching on structured records, short of building a dedicated matching engine from scratch. The other alternative would be to use a flat textual log (instead of the structured log records from EventLog), but then we'd have to generate quite intricate regular expressions from the builder, and I'm really doubtful it would be easier and clearer....
This commit is contained in:
parent
6e42e81546
commit
ff4acb04d7
8 changed files with 158 additions and 31 deletions
|
|
@ -794,7 +794,7 @@ provided as Lambda, and finally package all together into a self contained new
|
|||
iterator (value object). This is a rather new feature [small]#(as of 2017)# and
|
||||
we still need to determine its viability, yet it proved helpful when implementing
|
||||
certain kinds of search algorithms on top of an abstracted data source exposed
|
||||
as iterator. -> 'lib/iter-tree-explorer.hpp'
|
||||
as iterator. -> 'lib/iter-explorer.hpp'
|
||||
|
||||
|
||||
Front-end for boost::format
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
** # Design
|
||||
**
|
||||
** The IterChainSearch component is built as a processing pipeline, based on the
|
||||
** [Tree-Explorer framework](\ref iter-tree-explorer.hpp). This yields several benefits, but
|
||||
** [Iterator-Explorer framework](\ref iter-explorer.hpp). This yields several benefits, but
|
||||
** imposes some drawbacks. Without much effort, we get an extremely flexible and configurable
|
||||
** solution, with acceptable / moderate performance. The result automatically adapts to a huge
|
||||
** selection of data sources; it is possible (and is even intended) to attach it on top of an
|
||||
|
|
@ -67,7 +67,7 @@
|
|||
**
|
||||
** @see IterChainSearch_test
|
||||
** @see IterCursor_test
|
||||
** @see iter-tree-explorer.hpp
|
||||
** @see iter-explorer.hpp
|
||||
** @see [usage example](\ref event-log.hpp)
|
||||
**
|
||||
*/
|
||||
|
|
@ -106,9 +106,16 @@ namespace iter {
|
|||
.mutableFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* define the chained-search mechanism:
|
||||
* invoking `filter.expandChildren()` adds a new layer with the _copy_ of current
|
||||
* iteration (search) state, which is a _mutableFilter_ and can thus be augmented.
|
||||
* @remark the `expandChildren()` invocation happens from IterChainSearch::iterNext(),
|
||||
* which ensures the chained layers are expanded and matched until exhaustion.
|
||||
*/
|
||||
template<class SRC>
|
||||
auto
|
||||
buildExplorer (SRC&& dataSource)
|
||||
buildChainExplorer (SRC&& dataSource)
|
||||
{
|
||||
return buildSearchFilter (forward<SRC> (dataSource))
|
||||
.expand ([](auto it){ return it; }); // child iterator starts as copy of current level iterator
|
||||
|
|
@ -127,7 +134,7 @@ namespace iter {
|
|||
struct _IterChainSetup
|
||||
{
|
||||
using Filter = decltype( buildSearchFilter(std::declval<SRC>()).asIterator() );
|
||||
using Pipeline = decltype( buildExplorer (std::declval<SRC>()) );
|
||||
using Pipeline = decltype( buildChainExplorer (std::declval<SRC>()) );
|
||||
|
||||
/** each step in the chain is a functor to reconfigure the underlying filter */
|
||||
using StepFunctor = std::function<void(Filter&)>;
|
||||
|
|
@ -178,7 +185,7 @@ namespace iter {
|
|||
template<class SEQ>
|
||||
explicit
|
||||
IterChainSearch (SEQ&& srcData)
|
||||
: _Base{buildExplorer (forward<SEQ> (srcData))}
|
||||
: _Base{buildChainExplorer (forward<SEQ> (srcData))}
|
||||
{ // mark initial pristine state
|
||||
_Base::disableFilter();
|
||||
}
|
||||
|
|
@ -222,7 +229,7 @@ namespace iter {
|
|||
* @remarks the additional chained search condition given here will be applied _after_
|
||||
* matching all other conditions already in the filter chain. Each such condition
|
||||
* is used to _filter_ the underlying source iterator, i.e. pull it until finding
|
||||
* and element to match the condition. Basically these conditions are _not_ used in
|
||||
* an element to match the condition. Basically these conditions are _not_ used in
|
||||
* conjunction, but rather one after another. But since each such step in the chain
|
||||
* is defined by a functor, which gets the previous filter configuration as argument,
|
||||
* it is _possible_ to build a step which _extends_ or sharpens the preceding condition.
|
||||
|
|
@ -259,7 +266,7 @@ namespace iter {
|
|||
{
|
||||
addStep ([predicate{forward<FUN> (filterPredicate)}]
|
||||
(Filter& filter)
|
||||
{ // manipulate current filter configuration
|
||||
{ // manipulate current filter configuration
|
||||
filter.setNewFilter (predicate);
|
||||
});
|
||||
return move(*this);
|
||||
|
|
|
|||
|
|
@ -1674,7 +1674,7 @@ namespace lib {
|
|||
* CHECK (12 == *ii);
|
||||
* ++ii;
|
||||
* \endcode
|
||||
* In this example, a `CountDown` _state core_ is used, as defined in iter-tree-explorer-test.cpp
|
||||
* In this example, a `CountDown` _state core_ is used, as defined in iter-explorer-test.cpp
|
||||
*/
|
||||
template<class IT>
|
||||
inline auto
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@
|
|||
** types or RTTI
|
||||
**
|
||||
** @see util-foreach.hpp usage example
|
||||
** @see iter-tree-explorer.hpp (example: is_StateCore<SRC>)
|
||||
** @see iter-explorer.hpp (example: is_StateCore<SRC>)
|
||||
** @see duck-detector-test.cpp
|
||||
** @see duck-detector-extension-test.cpp
|
||||
**
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
**
|
||||
** @see TestEventLog_test
|
||||
** @see IterChainSearch_test
|
||||
** @see iter-tree-explorer.hpp
|
||||
** @see iter-explorer.hpp
|
||||
**
|
||||
*/
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ namespace test{
|
|||
or entry.getType() == "destroy"
|
||||
or entry.getType() == "logJoin"
|
||||
)
|
||||
and !isnil(entry.scope())
|
||||
and not isnil(entry.scope())
|
||||
and contains (*entry.scope(), match);
|
||||
};
|
||||
}
|
||||
|
|
@ -110,7 +110,7 @@ namespace test{
|
|||
return ( entry.getType() == classifier
|
||||
or (entry.hasAttribute("ID") and contains (entry.get("ID"), classifier))
|
||||
)
|
||||
and !isnil(entry.scope())
|
||||
and not isnil(entry.scope())
|
||||
and contains (*entry.scope(), match);
|
||||
};
|
||||
}
|
||||
|
|
@ -221,12 +221,12 @@ namespace test{
|
|||
|
||||
template<typename COND>
|
||||
inline void
|
||||
attachNextSerchStep (Filter& solution, COND&& filter, Direction direction)
|
||||
attachNextSerchStep (Filter& solution, COND&& searchCond, Direction direction)
|
||||
{
|
||||
if (CURRENT == direction)
|
||||
solution.search (forward<COND> (filter));
|
||||
solution.search (forward<COND> (searchCond));
|
||||
else
|
||||
solution.addStep ([predicate{forward<COND> (filter)}, direction]
|
||||
solution.addStep ([predicate{forward<COND> (searchCond)}, direction]
|
||||
(auto& filter)
|
||||
{
|
||||
filter.reverse (BACKWARD == direction);
|
||||
|
|
@ -238,9 +238,9 @@ namespace test{
|
|||
|
||||
template<typename COND>
|
||||
inline void
|
||||
refineSerach (Filter& solution, COND&& additionalFilter)
|
||||
refineSerach (Filter& solution, COND&& additionalCond)
|
||||
{
|
||||
solution.addStep ([predicate{forward<COND> (additionalFilter)}]
|
||||
solution.addStep ([predicate{forward<COND> (additionalCond)}]
|
||||
(auto& filter)
|
||||
{
|
||||
filter.andFilter (predicate);
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ namespace test {
|
|||
|
||||
|
||||
/** @test TODO demonstrate a simple usage scenario
|
||||
* @todo WIP 7/23 ⟶ define ⟶ implement
|
||||
* @todo WIP 7/23 🔁 define 🔁 implement
|
||||
*/
|
||||
void
|
||||
simpleUsage()
|
||||
|
|
@ -94,7 +94,7 @@ namespace test {
|
|||
|
||||
|
||||
/** @test verify the setup and detection of instrumented invocations
|
||||
* @todo WIP 7/23 ⟶ define ⟶ implement
|
||||
* @todo WIP 7/23 ✔ define 🔁 implement
|
||||
*/
|
||||
void
|
||||
verifyMockInvocation()
|
||||
|
|
@ -109,9 +109,8 @@ namespace test {
|
|||
|
||||
detector.markSequence();
|
||||
fun(rnd);
|
||||
detector.markSequence();
|
||||
CHECK (detector.verifyInvocation ("funny"));
|
||||
CHECK (detector.verifyInvocation ("funny", rnd));
|
||||
CHECK (detector.verifyCall ("funny"));
|
||||
CHECK (detector.verifyCall ("funny").arg(rnd));
|
||||
CHECK (detector.verifyInvocation ("funny", Seq(1)));
|
||||
CHECK (detector.verifyInvocation ("funny", Seq(1), rnd));
|
||||
CHECK (not detector.verifyInvocation ("bunny"));
|
||||
|
|
|
|||
|
|
@ -88,6 +88,8 @@ namespace test {
|
|||
// using lib::HashVal;
|
||||
using lib::meta::RebindVariadic;
|
||||
using util::isnil;
|
||||
using std::forward;
|
||||
using std::move;
|
||||
// using util::isSameObject;
|
||||
// using fixture::Segmentation;
|
||||
// using vault::RealClock;
|
||||
|
|
@ -134,6 +136,66 @@ namespace test {
|
|||
const string MARK_SEQ{"Seq"};
|
||||
}
|
||||
|
||||
class ActivityDetector;
|
||||
|
||||
|
||||
class ActivityMatch
|
||||
: private lib::test::EventMatch
|
||||
{
|
||||
using _Parent = lib::test::EventMatch;
|
||||
|
||||
ActivityMatch (lib::test::EventMatch&& matcher)
|
||||
: _Parent{move (matcher)}
|
||||
{ }
|
||||
|
||||
friend class ActivityDetector;
|
||||
|
||||
public:
|
||||
// standard copy acceptable
|
||||
|
||||
operator bool() const { return _Parent::operator bool(); }
|
||||
|
||||
template<typename...ARGS>
|
||||
ActivityMatch&
|
||||
arg (ARGS const& ...args)
|
||||
{
|
||||
return delegate (&EventMatch::arg<ARGS...>, args...);
|
||||
}
|
||||
|
||||
// EventMatch& locate (string match);
|
||||
// EventMatch& locateMatch (string regExp);
|
||||
// EventMatch& locateEvent (string match);
|
||||
// EventMatch& locateEvent (string classifier, string match);
|
||||
// EventMatch& locateCall (string match);
|
||||
//
|
||||
//
|
||||
// /* query builders to find a match stepping forwards */
|
||||
//
|
||||
// EventMatch& before (string match);
|
||||
// EventMatch& beforeMatch (string regExp);
|
||||
// EventMatch& beforeEvent (string match);
|
||||
// EventMatch& beforeEvent (string classifier, string match);
|
||||
ActivityMatch& beforeCall (string match) { return delegate (&EventMatch::beforeCall, move(match)); }
|
||||
//
|
||||
//
|
||||
// /* query builders to find a match stepping backwards */
|
||||
//
|
||||
// EventMatch& after (string match);
|
||||
// EventMatch& afterMatch (string regExp);
|
||||
// EventMatch& afterEvent (string match);
|
||||
// EventMatch& afterEvent (string classifier, string match);
|
||||
// EventMatch& afterCall (string match);
|
||||
|
||||
private:
|
||||
template<typename...ARGS>
|
||||
ActivityMatch&
|
||||
delegate (_Parent& (_Parent::*fun) (ARGS...), ARGS&& ...args)
|
||||
{
|
||||
return static_cast<ActivityMatch&> (
|
||||
(this->*fun) (forward<ARGS> (args)...));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Diagnostic context to record and evaluate activations within the Scheduler.
|
||||
|
|
@ -246,6 +308,7 @@ namespace test {
|
|||
return Functor{id, eventLog_};
|
||||
}
|
||||
|
||||
|
||||
template<typename...ARGS>
|
||||
bool
|
||||
verifyInvocation (string fun, Seq const& seq, ARGS const& ...args)
|
||||
|
|
@ -270,9 +333,13 @@ namespace test {
|
|||
bool
|
||||
verifyInvocation (string fun, ARGS const& ...args)
|
||||
{
|
||||
Seq currentEventSeq = invocationSeq_;
|
||||
markSequence(); // NOTE: incrementing here
|
||||
return verifyInvocation (fun, currentEventSeq, args...);
|
||||
return verifyInvocation (fun, invocationSeq_, args...);
|
||||
}
|
||||
|
||||
ActivityMatch
|
||||
verifyCall (string fun)
|
||||
{
|
||||
return ActivityMatch{move (eventLog_.verifyCall(fun))};
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -81725,8 +81725,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1689205029658" ID="ID_1144318045" MODIFIED="1689205033146" TEXT="Verifikationen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1689205029658" ID="ID_1144318045" MODIFIED="1690918967997" TEXT="Verifikationen">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1690899802680" ID="ID_383665548" MODIFIED="1690899840591" TEXT="Problem: zweifelsfreie Verifikation im Log">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1690899845778" ID="ID_527585169" MODIFIED="1690899886750" TEXT="muß fehl-Matches vermeiden">
|
||||
|
|
@ -81735,25 +81735,79 @@ Date:   Thu Apr 20 18:53:17 2023 +0200<br/>
|
|||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1690899868436" ID="ID_1034519064" MODIFIED="1690899890797" TEXT="Log sollte idealerweise komplett verifizierbar sein">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1690899789651" ID="ID_495093897" MODIFIED="1690899844507" TEXT="eine Sequenz-Nummer einführen">
|
||||
<node COLOR="#435e98" CREATED="1690899789651" ID="ID_495093897" MODIFIED="1690918956649" TEXT="eine Sequenz-Nummer einführen">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1690899895228" ID="ID_689471700" MODIFIED="1690899900231" TEXT="an die Instanz gebunden"/>
|
||||
<node CREATED="1690899900699" ID="ID_1842080361" MODIFIED="1690899905547" TEXT="damit 100% reproduzierbar"/>
|
||||
<node CREATED="1690899906154" ID="ID_350339048" MODIFIED="1690899917269" TEXT="impliziert eine Aufruf-Sequenz"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1690900159528" ID="ID_689193567" MODIFIED="1690900179719" TEXT="Wrapper-Typ verwenden (keinen primitiven Typ)">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1690900159528" ID="ID_689193567" MODIFIED="1690918955037" TEXT="Wrapper-Typ verwenden (keinen primitiven Typ)">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1690899920322" ID="ID_67461983" MODIFIED="1690899953072" TEXT="den Einzel-Aufruf mit der Nummer einklammern">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1691876350208" ID="ID_105178925" MODIFIED="1691876353502" TEXT="Test-DSL">
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1691876526510" ID="ID_1716550315" MODIFIED="1691876762417" TEXT="RLY?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1691876547403" ID="ID_513238141" MODIFIED="1691876555015" TEXT="das hier ist ein single-use-setup">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node CREATED="1691876586636" ID="ID_853990940" MODIFIED="1691876687983" TEXT="aber ich wüßte nicht wie ich's sonst testen könnte...">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
einfache Lösungen wie State-Flags oder Checksummen genügen nicht, denn es muß stets geprüft werden, daß mehrere Vorgänge insgesamt stattgefunden haben, und das in der richtigen Reihenfolge...
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1691876688987" ID="ID_1053525826" MODIFIED="1691876722777" TEXT="für einen dedizierten Verifikator müßte eine Struktur analog zum EventLog aufgebaut werden"/>
|
||||
<node CREATED="1691876724045" ID="ID_599766465" MODIFIED="1691876757873" TEXT="insofern ist es eine „Abkürzung“, das EventLog hierfür einzusetzen"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1691876355275" ID="ID_165347645" MODIFIED="1691876406949" TEXT="brauche komplexere verknüpfte Matches">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
analog zu dem Muster für das allgemeine EventLog....
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1691876450951" ID="ID_523328696" MODIFIED="1691876470288" TEXT="Matches für die Meßpunkte"/>
|
||||
<node CREATED="1691876811575" ID="ID_1724222058" MODIFIED="1691876823546" TEXT="jeweils in Reihenfolge und mit Sequenznumer"/>
|
||||
<node CREATED="1691876841330" ID="ID_1105562804" MODIFIED="1691877303577" TEXT="die EventLog-DSL verwendet dafür einen EventMatch(Builder)">
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1691877252417" ID="ID_1984871622" MODIFIED="1691951648662" TEXT="diesen könnte man dekorieren und erweitern">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1690899978840" ID="ID_70827658" MODIFIED="1690899987515" TEXT="einen Funktionsaufruf verifizieren">
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1690900023906" ID="ID_1470650457" MODIFIED="1690918796008" TEXT="markSequence()">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1690900069493" ID="ID_917256156" MODIFIED="1690900150148" TEXT="verifyInvocation(fun, Seq(i), args...)">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1691622557790" ID="ID_307528565" MODIFIED="1691622593416" TEXT="lenient arguments?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1691622603918" ID="ID_85657456" MODIFIED="1691622614224" TEXT="manchmal sind die Argumente egal"/>
|
||||
<node CREATED="1691622615042" ID="ID_1956964914" MODIFIED="1691622630486" TEXT="oder man weiß sie nicht einmal..."/>
|
||||
<node CREATED="1691622633914" ID="ID_1240512860" MODIFIED="1691622650564" TEXT="Lösung-1">
|
||||
<node CREATED="1691622651640" ID="ID_304481372" MODIFIED="1691622675769" TEXT="leere Argument-Paremeter ⟹ lenient"/>
|
||||
<node CREATED="1691622676564" ID="ID_874397643" MODIFIED="1691622709844" TEXT="Nachteil: man kann nicht explizit auf leere Argumentliste prüfen"/>
|
||||
</node>
|
||||
<node CREATED="1691622712487" ID="ID_1523121299" MODIFIED="1691622717827" TEXT="Lösung-2">
|
||||
<node CREATED="1691622718751" ID="ID_293626365" MODIFIED="1691622729350" TEXT="wildcard-Zeichen explizit angeben"/>
|
||||
<node CREATED="1691623021008" ID="ID_397525932" MODIFIED="1691623047537" TEXT="das wäre dann eine erst mal nicht offensichtliche spezielle Konvention">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
Loading…
Reference in a new issue