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:
Fischlurch 2023-08-13 20:49:30 +02:00
parent 6e42e81546
commit ff4acb04d7
8 changed files with 158 additions and 31 deletions

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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
**

View file

@ -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);

View file

@ -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"));

View file

@ -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))};
}

View file

@ -81725,8 +81725,8 @@ Date:&#160;&#160;&#160;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&#xdf; fehl-Matches vermeiden">
@ -81735,25 +81735,79 @@ Date:&#160;&#160;&#160;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&#xfc;hren">
<node COLOR="#435e98" CREATED="1690899789651" ID="ID_495093897" MODIFIED="1690918956649" TEXT="eine Sequenz-Nummer einf&#xfc;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&#xfc;&#xdf;te nicht wie ich&apos;s sonst testen k&#xf6;nnte...">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
einfache L&#246;sungen wie State-Flags oder Checksummen gen&#252;gen nicht, denn es mu&#223; stets gepr&#252;ft werden, da&#223; mehrere Vorg&#228;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&#xfc;r einen dedizierten Verifikator m&#xfc;&#xdf;te eine Struktur analog zum EventLog aufgebaut werden"/>
<node CREATED="1691876724045" ID="ID_599766465" MODIFIED="1691876757873" TEXT="insofern ist es eine &#x201e;Abk&#xfc;rzung&#x201c;, das EventLog hierf&#xfc;r einzusetzen"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1691876355275" ID="ID_165347645" MODIFIED="1691876406949" TEXT="brauche komplexere verkn&#xfc;pfte Matches">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
analog zu dem Muster f&#252;r das allgemeine EventLog....
</p>
</body>
</html></richcontent>
<icon BUILTIN="yes"/>
<node CREATED="1691876450951" ID="ID_523328696" MODIFIED="1691876470288" TEXT="Matches f&#xfc;r die Me&#xdf;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&#xfc;r einen EventMatch(Builder)">
<icon BUILTIN="info"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1691877252417" ID="ID_1984871622" MODIFIED="1691951648662" TEXT="diesen k&#xf6;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&#xdf; sie nicht einmal..."/>
<node CREATED="1691622633914" ID="ID_1240512860" MODIFIED="1691622650564" TEXT="L&#xf6;sung-1">
<node CREATED="1691622651640" ID="ID_304481372" MODIFIED="1691622675769" TEXT="leere Argument-Paremeter &#x27f9; lenient"/>
<node CREATED="1691622676564" ID="ID_874397643" MODIFIED="1691622709844" TEXT="Nachteil: man kann nicht explizit auf leere Argumentliste pr&#xfc;fen"/>
</node>
<node CREATED="1691622712487" ID="ID_1523121299" MODIFIED="1691622717827" TEXT="L&#xf6;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&#xe4;re dann eine erst mal nicht offensichtliche spezielle Konvention">
<icon BUILTIN="stop-sign"/>
</node>
</node>
</node>
</node>
</node>
</node>