From ff4acb04d7b9a4ef94ae1151824477250335640b Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 13 Aug 2023 20:49:30 +0200 Subject: [PATCH] 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.... --- doc/technical/overview.txt | 2 +- src/lib/iter-chain-search.hpp | 21 ++++-- src/lib/iter-explorer.hpp | 2 +- src/lib/meta/duck-detector.hpp | 2 +- src/lib/test/event-log.cpp | 16 ++--- tests/vault/gear/activity-detector-test.cpp | 9 ++- tests/vault/gear/activity-detector.hpp | 73 ++++++++++++++++++++- wiki/thinkPad.ichthyo.mm | 64 ++++++++++++++++-- 8 files changed, 158 insertions(+), 31 deletions(-) diff --git a/doc/technical/overview.txt b/doc/technical/overview.txt index e8c798467..20f6becc2 100644 --- a/doc/technical/overview.txt +++ b/doc/technical/overview.txt @@ -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 diff --git a/src/lib/iter-chain-search.hpp b/src/lib/iter-chain-search.hpp index 475d81b1a..e87d7ad66 100644 --- a/src/lib/iter-chain-search.hpp +++ b/src/lib/iter-chain-search.hpp @@ -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 auto - buildExplorer (SRC&& dataSource) + buildChainExplorer (SRC&& dataSource) { return buildSearchFilter (forward (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()).asIterator() ); - using Pipeline = decltype( buildExplorer (std::declval()) ); + using Pipeline = decltype( buildChainExplorer (std::declval()) ); /** each step in the chain is a functor to reconfigure the underlying filter */ using StepFunctor = std::function; @@ -178,7 +185,7 @@ namespace iter { template explicit IterChainSearch (SEQ&& srcData) - : _Base{buildExplorer (forward (srcData))} + : _Base{buildChainExplorer (forward (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 (filterPredicate)}] (Filter& filter) - { // manipulate current filter configuration + { // manipulate current filter configuration filter.setNewFilter (predicate); }); return move(*this); diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index b4bebeffc..3f2ce9624 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -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 inline auto diff --git a/src/lib/meta/duck-detector.hpp b/src/lib/meta/duck-detector.hpp index 9ce701f4e..c7398cda8 100644 --- a/src/lib/meta/duck-detector.hpp +++ b/src/lib/meta/duck-detector.hpp @@ -92,7 +92,7 @@ ** types or RTTI ** ** @see util-foreach.hpp usage example - ** @see iter-tree-explorer.hpp (example: is_StateCore) + ** @see iter-explorer.hpp (example: is_StateCore) ** @see duck-detector-test.cpp ** @see duck-detector-extension-test.cpp ** diff --git a/src/lib/test/event-log.cpp b/src/lib/test/event-log.cpp index d359f34fb..9fdc8e0a0 100644 --- a/src/lib/test/event-log.cpp +++ b/src/lib/test/event-log.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 inline void - attachNextSerchStep (Filter& solution, COND&& filter, Direction direction) + attachNextSerchStep (Filter& solution, COND&& searchCond, Direction direction) { if (CURRENT == direction) - solution.search (forward (filter)); + solution.search (forward (searchCond)); else - solution.addStep ([predicate{forward (filter)}, direction] + solution.addStep ([predicate{forward (searchCond)}, direction] (auto& filter) { filter.reverse (BACKWARD == direction); @@ -238,9 +238,9 @@ namespace test{ template inline void - refineSerach (Filter& solution, COND&& additionalFilter) + refineSerach (Filter& solution, COND&& additionalCond) { - solution.addStep ([predicate{forward (additionalFilter)}] + solution.addStep ([predicate{forward (additionalCond)}] (auto& filter) { filter.andFilter (predicate); diff --git a/tests/vault/gear/activity-detector-test.cpp b/tests/vault/gear/activity-detector-test.cpp index 2e27279b3..82457260a 100644 --- a/tests/vault/gear/activity-detector-test.cpp +++ b/tests/vault/gear/activity-detector-test.cpp @@ -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")); diff --git a/tests/vault/gear/activity-detector.hpp b/tests/vault/gear/activity-detector.hpp index d2833df13..9d705cc89 100644 --- a/tests/vault/gear/activity-detector.hpp +++ b/tests/vault/gear/activity-detector.hpp @@ -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 + ActivityMatch& + arg (ARGS const& ...args) + { + return delegate (&EventMatch::arg, 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 + ActivityMatch& + delegate (_Parent& (_Parent::*fun) (ARGS...), ARGS&& ...args) + { + return static_cast ( + (this->*fun) (forward (args)...)); + } + }; + /** * Diagnostic context to record and evaluate activations within the Scheduler. @@ -246,6 +308,7 @@ namespace test { return Functor{id, eventLog_}; } + template 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))}; } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index ce8a21eb1..3c28461b3 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -81725,8 +81725,8 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + @@ -81735,25 +81735,79 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + - - + + + + + + + + + + + + +

+ 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... +

+ +
+
+ + + + + + + +

+ analog zu dem Muster für das allgemeine EventLog.... +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +