From 94528d67dcca132cbb285dacd23a2a7d340d7fc7 Mon Sep 17 00:00:00 2001
From: Ichthyostega
Date: Tue, 15 Aug 2023 02:23:40 +0200
Subject: [PATCH] Activity-Lang: complete first test verification tool
...now able to verify that arbitrary functions have been invoked
...all of this is still preparatory work
---
tests/vault/gear/activity-detector-test.cpp | 19 +--
tests/vault/gear/activity-detector.hpp | 122 ++++++++++----------
wiki/thinkPad.ichthyo.mm | 120 ++++++++++++++-----
3 files changed, 160 insertions(+), 101 deletions(-)
diff --git a/tests/vault/gear/activity-detector-test.cpp b/tests/vault/gear/activity-detector-test.cpp
index d7ee3da0b..47fe352f1 100644
--- a/tests/vault/gear/activity-detector-test.cpp
+++ b/tests/vault/gear/activity-detector-test.cpp
@@ -94,7 +94,10 @@ namespace test {
/** @test verify the setup and detection of instrumented invocations
- * @todo WIP 7/23 ✔ define ✔ implement
+ * - a _sequence number_ is embedded into the ActivityDetector
+ * - this sequence number is recorded into an attribute at each invocation
+ * - a DSL for verification is provided (based on the EventLog)
+ * - arguments and sequence numbers can be explicitly checked
*/
void
verifyMockInvocation()
@@ -107,7 +110,7 @@ namespace test {
CHECK (1 == detector.currSeq());
CHECK (detector.ensureNoInvocation ("funny"));
- detector.markSequence();
+ ++detector;
CHECK (2 == detector.currSeq());
CHECK (detector.verifySeqIncrement(2));
@@ -117,13 +120,13 @@ namespace test {
CHECK (detector.verifyInvocation ("funny").seq(2));
CHECK (detector.verifyInvocation ("funny").arg(rnd).seq(2));
CHECK (detector.verifyInvocation ("funny").seq(2).arg(rnd));
- CHECK (detector.ensureNoInvocation ("bunny"));
- CHECK (detector.ensureNoInvocation ("funny").arg());
- CHECK (detector.ensureNoInvocation ("funny").arg(-rnd));
- CHECK (detector.ensureNoInvocation ("funny").seq(5));
- CHECK (detector.ensureNoInvocation ("funny").arg(rnd).seq(1));
+ CHECK (detector.ensureNoInvocation ("bunny")); // wrong name
+ CHECK (detector.ensureNoInvocation ("funny").arg()); // fails since empty argument list expected
+ CHECK (detector.ensureNoInvocation ("funny").arg(rnd+5)); // expecting wrong argument
+ CHECK (detector.ensureNoInvocation ("funny").seq(5)); // expecting wrong sequence number
+ CHECK (detector.ensureNoInvocation ("funny").arg(rnd).seq(1)); // expecting correct argument, but wrong sequence
- detector.markSequence();
+ ++detector;
fun (rnd+1);
CHECK (detector.verifyInvocation ("funny").seq(2)
.beforeSeqIncrement(3)
diff --git a/tests/vault/gear/activity-detector.hpp b/tests/vault/gear/activity-detector.hpp
index 1c9ef062f..69de4fc08 100644
--- a/tests/vault/gear/activity-detector.hpp
+++ b/tests/vault/gear/activity-detector.hpp
@@ -31,12 +31,29 @@
** means of indirection and extension. As a remedy, a set of preconfigured
** _detector Activity records_ is provided, which drop off event log messages
** by side effect. These detector probes can be wired in as decorators into
- ** a otherwise valid Activity-Term, allowing to watch and verify patterns
+ ** an otherwise valid Activity-Term, allowing to watch and verify patterns
** of invocation -- which might even happen concurrently.
- **
- ** @todo WIP-WIP-WIP 7/2023 right now this is a rather immature attempt
- ** towards a scaffolding to propel the build-up of the scheduler.
+ **
+ ** # Usage
+ **
+ ** An ActivityDetector instance can be created in local storage to get an arsenal
+ ** of probing tools and detectors, which are internally wired to record activation
+ ** into an lib::test::EventLog embedded into the ActivityDetector instance. A
+ ** _verification DSL_ is provided, internally relying on the building blocks and
+ ** the chained-search mechanism known from the EventLog. To distinguish similar
+ ** invocations and activations, a common _sequence number_ is maintained within
+ ** the ActivityDetector instance, which can be incremented explicitly. All
+ ** relevant events also capture the current sequence number as an attribute
+ ** of the generated log record.
+ **
+ ** ## Observation tools
+ ** - ActivityDetector::buildDiadnosticFun(id) generates a functor object with
+ ** _arbitrary signature,_ which records any invocation and arguments.
+ ** The corresponding verification matcher is #verifyInvocation(id)
+ **
+ ** @todo WIP-WIP-WIP 8/2023 gradually gaining traction.
** @see SchedulerActivity_test
+ ** @see EventLog_test (demonstration of EventLog capbabilities)
*/
@@ -97,40 +114,6 @@ namespace test {
// using vault::gear::JobClosure;
- /** Marker for invocation sequence */
- class Seq
- {
- uint step_;
-
- public:
- Seq (uint start =0)
- : step_{start}
- { }
-
- operator uint() const
- {
- return step_;
- }
- operator string() const
- {
- return util::toString (step_);
- }
-
- uint
- operator++()
- {
- ++step_;
- return step_;
- }
-
- bool
- operator== (Seq const& o)
- {
- return step_ == o.step_;
- }
- };
-
-
namespace {// Event markers
const string MARK_INC{"IncSeq"};
const string MARK_SEQ{"Seq"};
@@ -139,6 +122,12 @@ namespace test {
class ActivityDetector;
+ /**
+ * @internal ongoing evaluation and match of observed activities.
+ * @remark this temporary object provides a builder API for creating
+ * chained verifications, similar to the usage of lib::test::EventLog.
+ * Moreover, it is convertible to `bool` to retrieve the verification result.
+ */
class ActivityMatch
: private lib::test::EventMatch
{
@@ -153,6 +142,10 @@ namespace test {
public:
// standard copy acceptable
+ /** final evaluation of the verification query,
+ * usually triggered from the unit test `CHECK()`.
+ * @note failure cause is printed to STDERR.
+ */
operator bool() const { return _Parent::operator bool(); }
@@ -178,7 +171,7 @@ namespace test {
// EventMatch& afterMatch (string regExp);
// EventMatch& afterEvent (string match);
// EventMatch& afterEvent (string classifier, string match);
-// EventMatch& afterCall (string match);
+ ActivityMatch& afterInvocation (string match) { return delegate (&EventMatch::afterCall, move(match)); }
/** qualifier: additionally match the function arguments */
template
@@ -196,6 +189,7 @@ namespace test {
return *this;
}
+ /** special query to match an increment of the sequence number */
ActivityMatch&
beforeSeqIncrement (uint seqNr)
{
@@ -203,7 +197,13 @@ namespace test {
return *this;
}
+
private:
+ /** @internal helper to delegate to the inherited matcher building blocks
+ * @note since ActivityMatch can only be created by ActivityDetector,
+ * we can be sure the EventMatch reference returned from these calls
+ * is actually a reference to `*this`, and can thus be downcasted.
+ * */
template
ActivityMatch&
delegate (_Parent& (_Parent::*fun) (ARGS...), ARGS&& ...args)
@@ -216,7 +216,11 @@ namespace test {
/**
* Diagnostic context to record and evaluate activations within the Scheduler.
- * @todo WIP-WIP-WIP 7/23 a new loopy hope
+ * The provided tools and detectors are wired back internally, such as to record
+ * any observations into an lib::test::EventLog instance. Thus, after performing
+ * rigged functionality, the expected activities and their order can be verified.
+ * @see ActivityDetector_test
+ * @todo WIP-WIP-WIP 8/23 gradually building the verification tools needed...
*/
class ActivityDetector
: util::NonCopyable
@@ -224,7 +228,7 @@ namespace test {
using EventLog = lib::test::EventLog;
EventLog eventLog_;
- Seq invocationSeq_;
+ uint invocationSeq_;
/**
* A Mock functor, logging all invocations into the EventLog
@@ -236,14 +240,14 @@ namespace test {
string id_;
EventLog* log_;
- Seq const* seqNr_;
+ uint const* seqNr_;
RetVal retVal_;
public:
- DiagnosticFun (string id, EventLog& masterLog, Seq const& seqNr)
+ DiagnosticFun (string id, EventLog& masterLog, uint const& invocationSeqNr)
: id_{id}
, log_{&masterLog}
- , seqNr_{&seqNr}
+ , seqNr_{&invocationSeqNr}
, retVal_{}
{ }
@@ -261,7 +265,7 @@ namespace test {
operator() (ARGS const& ...args)
{
log_->call (log_->getID(), id_, args...)
- .addAttrib (MARK_SEQ, *seqNr_);
+ .addAttrib (MARK_SEQ, util::toString(*seqNr_));
return *retVal_;
}
};
@@ -278,6 +282,15 @@ namespace test {
return util::join (eventLog_);
}
+ string
+ showLog() const
+ {
+ return "\n____Event-Log___________________________\n"
+ + util::join (eventLog_, "\n")
+ + "\n────╼━━━━━━━━╾──────────────────────────"
+ ;
+ }
+
void
clear(string newID)
{
@@ -287,11 +300,12 @@ namespace test {
eventLog_.clear (newID);
}
+ /** increment the internal invocation sequence number */
uint
operator++()
{
++invocationSeq_;
- eventLog_.event (MARK_INC, invocationSeq_);
+ eventLog_.event (MARK_INC, util::toString(invocationSeq_));
return invocationSeq_;
}
@@ -301,12 +315,6 @@ namespace test {
return invocationSeq_;
}
- uint
- markSequence()
- {
- return operator++();
- }
-
/**
* Generic testing helper: build a λ-mock, logging all invocations
@@ -348,16 +356,6 @@ namespace test {
}
- string
- showLog() const
- {
- return "\n____Event-Log___________________________\n"
- + util::join (eventLog_, "\n")
- + "\n────╼━━━━━━━━╾──────────────────────────"
- ;
- }
-
-
private:
};
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index a4daf9648..45f7e8ea6 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -79423,14 +79423,14 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
+
+
-
+
@@ -79439,11 +79439,19 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
-
+
+
+
+
+
+ ...paßt perfekt hier; muß lediglich die elementaren Verifikations-Primitive hier verpacken, um semantisch relevante Elemente direkt zu prüfen
+
+
+
-
@@ -81640,6 +81648,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
@@ -81673,7 +81682,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
@@ -81727,12 +81736,12 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
-
+
@@ -81740,18 +81749,23 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
+
+
+
-
+
-
-
+
+
+
+
@@ -81814,8 +81828,8 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
+
+
@@ -81834,12 +81848,12 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
- ...allerdings liegt das an der Natur der logischen Negation selber, nicht an der unterstützung im Verifkations-Framework; ein Check ist eine Existenz-Aussage, und daher müßte zur Negation eine All-Aussage geprüft werden, durch eine erschöpfende Suche aller möglichen alternativen Prüf-Ketten. Ich hatte Fälle, in denen das durchaus vorhandene Backtracking das nicht leisten konnte
+ ...allerdings liegt das an der Natur der logischen Negation selber, nicht an der unterstützung im Verifkations-Framework; ein Check ist eine Existenz-Aussage, und daher müßte zur Negation eine All-Aussage geprüft werden, durch eine erschöpfende Suche aller möglichen alternativen Prüf-Ketten. Anfangs habe ich das nicht gemacht, aber 9/2018 habe ich richtiges Backtracking eingebaut; damit sollte das nun korrekt funktionieren (was ich aber nie abschließend verifiziert habe, nur durch einzelne Testfälle geprüft)
@@ -81856,28 +81870,53 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+ ein Funktionsaufruf (typischwerweise von einem rigged-Functor)
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
-
+
-
+
-
-
+
+
@@ -81895,13 +81934,19 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
+
+
+
+
-
+
+
+
+
+
@@ -81934,10 +81979,11 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
-
+
+
+
+
@@ -81959,6 +82005,18 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
+
+
+
+
+
+
+
+
+
+
+