From 1c6ee62c1a49cdeaedcf5ac4ee1e196c92b01048 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Tue, 15 Aug 2023 20:03:01 +0200 Subject: [PATCH] Activity-Lang: allow to verify invocation param in test requires to supplement EventLog matching primitives to pick and verify a specific positional argument. Moreover, it is more or less arbitrary which job invocation parameters are unpacked and exposed for verification; we'll have to see what is actually required for writing tests... --- src/lib/test/event-log.cpp | 23 +++++++++++++++++- src/lib/test/event-log.hpp | 9 +++++++ tests/library/test/event-log-test.cpp | 11 ++++++++- tests/vault/gear/activity-detector-test.cpp | 22 +++++++++++++---- tests/vault/gear/activity-detector.hpp | 26 +++++++++++++++------ wiki/thinkPad.ichthyo.mm | 19 +++++++++------ 6 files changed, 90 insertions(+), 20 deletions(-) diff --git a/src/lib/test/event-log.cpp b/src/lib/test/event-log.cpp index 44d409c8c..c4f5c15ae 100644 --- a/src/lib/test/event-log.cpp +++ b/src/lib/test/event-log.cpp @@ -150,6 +150,17 @@ namespace test{ }; // otherwise the sizes do not match... } + /** refinement filter to match a specific positional argument */ + inline auto + matchArgument (size_t idx, string match) + { + return [=](Entry const& entry) + { + return idx < entry.childSize() + and contains (entry.child(idx), match); + }; + } + /** refinement filter, to cover all arguments by regular expression(s) * @param regExpSeq several regular expressions, which, when applied @@ -494,7 +505,17 @@ namespace test{ /** - * @internal refine filter condition additionally to match the call arguments. + * @internal refine filter condition additionally to match a specific positional call argument. + */ + void + EventMatch::refineSerach_matchArgument (size_t idx, string match) + { + refineSerach (solution_, matchArgument(idx, match)); + evaluateQuery ("match-argument(["+util::toString(idx)+"]="+match+")"); + } + + /** + * @internal refine filter condition additionally to match a sequence of call arguments. */ void EventMatch::refineSerach_matchArguments (ArgSeq&& argSeq) diff --git a/src/lib/test/event-log.hpp b/src/lib/test/event-log.hpp index 10495b62c..52da8cf5f 100644 --- a/src/lib/test/event-log.hpp +++ b/src/lib/test/event-log.hpp @@ -230,6 +230,14 @@ namespace test{ return *this; } + /** refine filter to additionally require match on a specific positional argument */ + template + EventMatch& + argPos (size_t idx, ARG const& arg) + { + refineSerach_matchArgument (idx, util::toString(arg)); + return *this; + } /* query builders to augment and refine the currently defined search condition*/ @@ -252,6 +260,7 @@ namespace test{ bool foundSolution(); void evaluateQuery (string matchSpec, Literal rel = "after"); + void refineSerach_matchArgument (size_t idx, string match); void refineSerach_matchArguments (ArgSeq&& argSeq); void refineSerach_matchArgsRegExp (RExSeq&& regExpSeq, string rendered_regExps); }; diff --git a/tests/library/test/event-log-test.cpp b/tests/library/test/event-log-test.cpp index 919a75e86..aeb36bce1 100644 --- a/tests/library/test/event-log-test.cpp +++ b/tests/library/test/event-log-test.cpp @@ -236,10 +236,19 @@ namespace test{ CHECK (log.verifyCall("fun3").arg("facts", 3.2, 1)); CHECK (log.verifyCall("fun3").arg(string("facts"), 3.2f, int64_t(1))); CHECK (log.verifyCall("fun3").arg("facts", "3.2", "1")); + CHECK (log.verifyCall("fun3").argPos(0, "facts")); + CHECK (log.verifyCall("fun3").argPos(0, "act")); + CHECK (log.verifyCall("fun3").argPos(1, ".2")); + CHECK (log.verifyCall("fun3").argPos(1, 3.2)); + CHECK (log.verifyCall("fun3").argPos(2, 1u)); - CHECK (log.ensureNot("fun").arg(" facts ","3.2", "1")); + CHECK (log.ensureNot("fun").arg(" facts ","3.2", "1")); // the match is on the exact textual representation... CHECK (log.ensureNot("fun").arg("facts", "3.20","1")); CHECK (log.ensureNot("fun").arg("facts", "3.2", "1L")); + CHECK (log.ensureNot("fun").argPos(1, "anything")); // matches first invocation, which has no arguments + CHECK (log.ensureNot("fun3").argPos(3, 5555)); // the "fun3" invocation has only 3 arguments + CHECK (log.ensureNot("fun3").argPos(1, 3.3)); // the second argument is 2.3, not 3.3 + CHECK (log.ensureNot("fun3").argPos(2, 5)); // the last argument is 1, not 5 CHECK (log.verifyCall("fun1").arg()); CHECK (log.verifyCall("fun2").arg()); diff --git a/tests/vault/gear/activity-detector-test.cpp b/tests/vault/gear/activity-detector-test.cpp index 2bb20f648..0ef03f51b 100644 --- a/tests/vault/gear/activity-detector-test.cpp +++ b/tests/vault/gear/activity-detector-test.cpp @@ -142,15 +142,19 @@ namespace test { - /** @test TODO diagnostic setup to detect a JobFunctor activation - * @todo WIP 7/23 🔁 define 🔁 implement + /** @test diagnostic setup to detect a JobFunctor activation + * - the ActivityDetector provides specifically rigged JobFunctor instances + * - these capture all invocations, based on generic invocation logging + * - special match qualifier to verify the job's nominal invocation time parameter + * - event verification can be combined with other verifications to cover + * complex invocation sequences */ void verifyMockJobFunctor() { ActivityDetector detector; InvocationInstanceID invoKey; - TimeVar nominal{FSecs{5,2}}; + Time nominal{FSecs{5,2}}; invoKey.part.a = 55; Job dummyJob{detector.buildMockJobFunctor ("mockJob") @@ -160,7 +164,17 @@ namespace test { CHECK (detector.ensureNoInvocation ("mockJob")); dummyJob.triggerJob(); CHECK (detector.verifyInvocation ("mockJob")); - CHECK (detector.verifyInvocation ("mockJob").arg(nominal, invoKey.part.a)); + CHECK (detector.verifyInvocation ("mockJob").arg(TimeValue{nominal}, invoKey.part.a)); + CHECK (detector.verifyInvocation ("mockJob").nominalTime(nominal)); + + ++detector; // note: sequence number incremented between invocations + dummyJob.parameter.nominalTime += 5 * Time::SCALE; // different job parameter (later nominal time point) + dummyJob.triggerJob(); + + CHECK (detector.verifyInvocation ("mockJob").nominalTime(nominal).seq(0) + .beforeInvocation ("mockJob").nominalTime(nominal + Time{FSecs{5}}) // matching first invocation and then second... + .afterSeqIncrement(1) // note: searching backwards from the 2nd invocation + ); } diff --git a/tests/vault/gear/activity-detector.hpp b/tests/vault/gear/activity-detector.hpp index f92a58060..0a286d424 100644 --- a/tests/vault/gear/activity-detector.hpp +++ b/tests/vault/gear/activity-detector.hpp @@ -118,9 +118,12 @@ namespace test { // using vault::gear::JobClosure; - namespace {// Event markers + namespace {// Diagnostic markers const string MARK_INC{"IncSeq"}; const string MARK_SEQ{"Seq"}; + + using SIG_JobDiagnostic = void(TimeValue, int32_t); + const size_t JOB_ARG_POS_TIME = 0; } class ActivityDetector; @@ -200,6 +203,19 @@ namespace test { _Parent::beforeEvent(MARK_INC, util::toString(seqNr)); return *this; } + ActivityMatch& + afterSeqIncrement (uint seqNr) + { + _Parent::afterEvent(MARK_INC, util::toString(seqNr)); + return *this; + } + + /** qualifier: additionally match the nominal time argument of JobFunctor invocation */ + ActivityMatch& + nominalTime (TimeValue const& time) + { + return delegate (&EventMatch::argPos, size_t(JOB_ARG_POS_TIME), time); + } private: @@ -293,11 +309,7 @@ namespace test { class MockJobFunctor : public NopJobFunctor { - public: - using SIG_Diagnostic = void(TimeValue, int32_t); - - private: - using MockOp = typename _DiagnosticFun::Type; + using MockOp = typename _DiagnosticFun::Type; MockOp mockOperation_; @@ -385,7 +397,7 @@ namespace test { buildMockJobFunctor (string id) { return mockOps_.emplace_back ( - buildDiagnosticFun (id)); + buildDiagnosticFun (id)); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index fc7483c79..13ec52e55 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -81792,7 +81792,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -82034,23 +82034,23 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- - + + - - + + - + - + @@ -82092,6 +82092,11 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + + + +