2023-07-13 01:51:21 +02:00
|
|
|
|
/*
|
|
|
|
|
|
ActivityDetector(Test) - verify diagnostic setup to watch scheduler activities
|
|
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
Copyright (C)
|
|
|
|
|
|
2023, Hermann Vosseler <Ichthyostega@web.de>
|
2023-07-13 01:51:21 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
**Lumiera** is free software; you can redistribute it and/or modify it
|
|
|
|
|
|
under the terms of the GNU General Public License as published by the
|
|
|
|
|
|
Free Software Foundation; either version 2 of the License, or (at your
|
|
|
|
|
|
option) any later version. See the file COPYING for further details.
|
2023-07-13 01:51:21 +02:00
|
|
|
|
|
Copyright: clarify and simplify the file headers
* Lumiera source code always was copyrighted by individual contributors
* there is no entity "Lumiera.org" which holds any copyrights
* Lumiera source code is provided under the GPL Version 2+
== Explanations ==
Lumiera as a whole is distributed under Copyleft, GNU General Public License Version 2 or above.
For this to become legally effective, the ''File COPYING in the root directory is sufficient.''
The licensing header in each file is not strictly necessary, yet considered good practice;
attaching a licence notice increases the likeliness that this information is retained
in case someone extracts individual code files. However, it is not by the presence of some
text, that legally binding licensing terms become effective; rather the fact matters that a
given piece of code was provably copyrighted and published under a license. Even reformatting
the code, renaming some variables or deleting parts of the code will not alter this legal
situation, but rather creates a derivative work, which is likewise covered by the GPL!
The most relevant information in the file header is the notice regarding the
time of the first individual copyright claim. By virtue of this initial copyright,
the first author is entitled to choose the terms of licensing. All further
modifications are permitted and covered by the License. The specific wording
or format of the copyright header is not legally relevant, as long as the
intention to publish under the GPL remains clear. The extended wording was
based on a recommendation by the FSF. It can be shortened, because the full terms
of the license are provided alongside the distribution, in the file COPYING.
2024-11-17 23:42:55 +01:00
|
|
|
|
* *****************************************************************/
|
2023-07-13 01:51:21 +02:00
|
|
|
|
|
|
|
|
|
|
/** @file activity-detector-test.cpp
|
|
|
|
|
|
** unit test \ref ActivityDetector_test
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "lib/test/run.hpp"
|
2023-07-31 21:53:16 +02:00
|
|
|
|
#include "lib/test/test-helper.hpp"
|
2023-08-19 17:48:38 +02:00
|
|
|
|
#include "activity-detector.hpp"
|
|
|
|
|
|
#include "vault/real-clock.hpp"
|
2023-07-31 21:53:16 +02:00
|
|
|
|
#include "lib/time/timevalue.hpp"
|
2023-09-03 01:50:50 +02:00
|
|
|
|
#include "lib/format-cout.hpp"
|
2023-08-19 23:59:18 +02:00
|
|
|
|
#include "lib/util.hpp"
|
2023-07-13 01:51:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
2023-09-03 01:50:50 +02:00
|
|
|
|
using lib::time::Time;
|
|
|
|
|
|
using lib::time::FSecs;
|
|
|
|
|
|
|
2023-08-19 23:59:18 +02:00
|
|
|
|
using util::isSameObject;
|
2023-08-18 19:37:44 +02:00
|
|
|
|
using lib::test::randStr;
|
|
|
|
|
|
using lib::test::randTime;
|
2023-07-13 01:51:21 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace vault{
|
|
|
|
|
|
namespace gear {
|
|
|
|
|
|
namespace test {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************//**
|
|
|
|
|
|
* @test verify instrumentation setup to watch scheduler Activities.
|
|
|
|
|
|
* @see SchedulerActivity_test
|
|
|
|
|
|
* @see SchedulerUsage_test
|
|
|
|
|
|
*/
|
|
|
|
|
|
class ActivityDetector_test : public Test
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
virtual void
|
|
|
|
|
|
run (Arg)
|
|
|
|
|
|
{
|
2024-11-13 02:23:23 +01:00
|
|
|
|
seedRand();
|
|
|
|
|
|
|
2023-07-13 01:51:21 +02:00
|
|
|
|
simpleUsage();
|
|
|
|
|
|
|
2023-08-01 17:53:42 +02:00
|
|
|
|
verifyMockInvocation();
|
2023-09-01 21:59:25 +02:00
|
|
|
|
verifyFakeInvocation();
|
2023-08-15 17:18:30 +02:00
|
|
|
|
verifyMockJobFunctor();
|
2023-08-17 19:37:38 +02:00
|
|
|
|
verifyFakeExeContext();
|
2023-10-18 15:50:11 +02:00
|
|
|
|
watch_ActivationProbe();
|
2023-08-19 23:59:18 +02:00
|
|
|
|
watch_ActivationTap();
|
|
|
|
|
|
insert_ActivationTap();
|
2023-08-19 17:48:38 +02:00
|
|
|
|
watch_notification();
|
|
|
|
|
|
watch_gate();
|
2023-07-13 01:51:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-09-03 01:50:50 +02:00
|
|
|
|
/** @test demonstrate a simple usage scenario of this test support facility
|
2023-07-13 01:51:21 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
simpleUsage()
|
|
|
|
|
|
{
|
2023-07-31 21:53:16 +02:00
|
|
|
|
ActivityDetector detector("spectre");
|
|
|
|
|
|
|
|
|
|
|
|
auto trap = detector.buildDiagnosticFun<int(double,Time)>("trap")
|
|
|
|
|
|
.returning(55);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (55 == trap (1.23, Time{FSecs{3,2}}));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (detector == "Rec(EventLogHeader| this = ActivityDetector(spectre) ), "
|
2023-08-13 23:42:04 +02:00
|
|
|
|
"Rec(call| fun = trap, this = ActivityDetector(spectre), Seq = 0 |{1.23, 0:00:01.500})"_expect);
|
2023-07-13 01:51:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-01 17:53:42 +02:00
|
|
|
|
/** @test verify the setup and detection of instrumented invocations
|
2023-08-15 02:23:40 +02:00
|
|
|
|
* - 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
|
2023-08-01 17:53:42 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
verifyMockInvocation()
|
|
|
|
|
|
{
|
|
|
|
|
|
ActivityDetector detector;
|
2023-08-15 17:18:30 +02:00
|
|
|
|
auto fun = detector.buildDiagnosticFun<void(uint)> ("funny");
|
2024-11-13 02:23:23 +01:00
|
|
|
|
uint rnd = rani(10000);
|
2023-08-01 17:53:42 +02:00
|
|
|
|
|
2023-08-22 20:13:13 +02:00
|
|
|
|
detector.incrementSeq();
|
2023-08-01 17:53:42 +02:00
|
|
|
|
CHECK (1 == detector.currSeq());
|
2023-08-14 19:22:18 +02:00
|
|
|
|
CHECK (detector.ensureNoInvocation ("funny"));
|
2023-08-01 17:53:42 +02:00
|
|
|
|
|
2023-08-22 20:13:13 +02:00
|
|
|
|
detector.incrementSeq();
|
2023-08-14 19:22:18 +02:00
|
|
|
|
CHECK (2 == detector.currSeq());
|
|
|
|
|
|
CHECK (detector.verifySeqIncrement(2));
|
|
|
|
|
|
|
|
|
|
|
|
fun (rnd);
|
2023-08-13 23:42:04 +02:00
|
|
|
|
CHECK (detector.verifyInvocation ("funny"));
|
|
|
|
|
|
CHECK (detector.verifyInvocation ("funny").arg(rnd));
|
|
|
|
|
|
CHECK (detector.verifyInvocation ("funny").seq(2));
|
|
|
|
|
|
CHECK (detector.verifyInvocation ("funny").arg(rnd).seq(2));
|
|
|
|
|
|
CHECK (detector.verifyInvocation ("funny").seq(2).arg(rnd));
|
2023-08-15 02:23:40 +02:00
|
|
|
|
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
|
2023-08-14 19:22:18 +02:00
|
|
|
|
|
2023-08-22 20:13:13 +02:00
|
|
|
|
detector.incrementSeq();
|
2023-08-14 19:22:18 +02:00
|
|
|
|
fun (rnd+1);
|
|
|
|
|
|
CHECK (detector.verifyInvocation ("funny").seq(2)
|
|
|
|
|
|
.beforeSeqIncrement(3)
|
|
|
|
|
|
.beforeInvocation ("funny").seq(3).arg(rnd+1));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (detector == "Rec(EventLogHeader| this = ActivityDetector )"
|
|
|
|
|
|
", Rec(event| ID = IncSeq |{1})"
|
|
|
|
|
|
", Rec(event| ID = IncSeq |{2})"
|
|
|
|
|
|
", Rec(call| fun = funny, this = ActivityDetector, Seq = 2 |{"+util::toString(rnd)+"})"
|
|
|
|
|
|
", Rec(event| ID = IncSeq |{3})"
|
|
|
|
|
|
", Rec(call| fun = funny, this = ActivityDetector, Seq = 3 |{"+util::toString(rnd+1)+"})"_expect);
|
2023-08-01 17:53:42 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-09-01 21:59:25 +02:00
|
|
|
|
/** @test verify a variation of the instrumented functor
|
|
|
|
|
|
* to call into a custom provided _fake implementation._
|
|
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
verifyFakeInvocation()
|
|
|
|
|
|
{
|
|
|
|
|
|
ActivityDetector detector;
|
|
|
|
|
|
auto fun = detector.buildDiagnosticFun<int(uint)> ("fakeFun");
|
2024-11-13 02:23:23 +01:00
|
|
|
|
uint rnd = rani(10000);
|
2023-09-01 21:59:25 +02:00
|
|
|
|
|
|
|
|
|
|
CHECK (0 == fun (rnd));
|
|
|
|
|
|
|
|
|
|
|
|
fun.returning(42);
|
|
|
|
|
|
detector.incrementSeq();
|
|
|
|
|
|
CHECK (42 == fun (rnd));
|
|
|
|
|
|
|
|
|
|
|
|
fun.implementedAs ([](uint i){ return -i; });
|
|
|
|
|
|
detector.incrementSeq();
|
|
|
|
|
|
CHECK (-int(rnd) == fun (rnd));
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (detector.verifyInvocation("fakeFun").seq(0)
|
|
|
|
|
|
.beforeInvocation("fakeFun").seq(1)
|
|
|
|
|
|
.beforeInvocation("fakeFun").seq(2));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-15 20:03:01 +02:00
|
|
|
|
/** @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
|
2023-07-13 01:51:21 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
2023-08-15 17:18:30 +02:00
|
|
|
|
verifyMockJobFunctor()
|
2023-07-13 01:51:21 +02:00
|
|
|
|
{
|
2023-08-15 17:18:30 +02:00
|
|
|
|
ActivityDetector detector;
|
2023-08-15 18:52:51 +02:00
|
|
|
|
InvocationInstanceID invoKey;
|
2023-08-15 20:03:01 +02:00
|
|
|
|
Time nominal{FSecs{5,2}};
|
2023-08-15 18:52:51 +02:00
|
|
|
|
invoKey.part.a = 55;
|
|
|
|
|
|
|
|
|
|
|
|
Job dummyJob{detector.buildMockJobFunctor ("mockJob")
|
|
|
|
|
|
,invoKey
|
|
|
|
|
|
,nominal};
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (detector.ensureNoInvocation ("mockJob"));
|
|
|
|
|
|
dummyJob.triggerJob();
|
|
|
|
|
|
CHECK (detector.verifyInvocation ("mockJob"));
|
2023-08-18 04:00:21 +02:00
|
|
|
|
CHECK (detector.verifyInvocation ("mockJob").arg(nominal, invoKey.part.a));
|
2023-08-19 17:48:38 +02:00
|
|
|
|
CHECK (detector.verifyInvocation ("mockJob").timeArg(nominal));
|
2023-08-15 20:03:01 +02:00
|
|
|
|
|
2023-08-22 20:13:13 +02:00
|
|
|
|
detector.incrementSeq(); // note: sequence number incremented between invocations
|
2023-08-15 20:03:01 +02:00
|
|
|
|
dummyJob.parameter.nominalTime += 5 * Time::SCALE; // different job parameter (later nominal time point)
|
|
|
|
|
|
dummyJob.triggerJob();
|
|
|
|
|
|
|
2023-08-19 17:48:38 +02:00
|
|
|
|
CHECK (detector.verifyInvocation ("mockJob").timeArg(nominal).seq(0)
|
|
|
|
|
|
.beforeInvocation ("mockJob").timeArg(nominal + Time{FSecs{5}}) // matching first invocation and then second...
|
2023-08-15 20:03:01 +02:00
|
|
|
|
.afterSeqIncrement(1) // note: searching backwards from the 2nd invocation
|
|
|
|
|
|
);
|
2023-08-21 18:23:57 +02:00
|
|
|
|
// cout << detector.showLog()<<endl; // HINT: use this for investigation...
|
2023-07-13 01:51:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-17 19:37:38 +02:00
|
|
|
|
/** @test faked execution context to perform Activity activation
|
2023-08-18 04:00:21 +02:00
|
|
|
|
* - wired internally to report each invocation into the EventLog
|
|
|
|
|
|
* - by default response of `post` and `tick` is `PASS`, but can be reconfigured
|
2023-10-18 15:50:11 +02:00
|
|
|
|
* - invocation sequence can be verified by matching internally logged events
|
2023-08-17 19:37:38 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
verifyFakeExeContext()
|
|
|
|
|
|
{
|
|
|
|
|
|
ActivityDetector detector;
|
|
|
|
|
|
auto& ctx = detector.executionCtx;
|
|
|
|
|
|
// an otherwise opaque object fulfilling the "Concept"
|
|
|
|
|
|
activity::_verify_usable_as_ExecutionContext<decltype(detector.executionCtx)>();
|
|
|
|
|
|
|
2023-08-18 19:37:44 +02:00
|
|
|
|
Time t = randTime();
|
2023-12-13 19:42:38 +01:00
|
|
|
|
Time td{t+Time(0,1)};
|
2024-11-13 02:23:23 +01:00
|
|
|
|
size_t x = rani();
|
2023-08-18 04:00:21 +02:00
|
|
|
|
Activity a;
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (detector.ensureNoInvocation(CTX_WORK));
|
|
|
|
|
|
CHECK (detector.ensureNoInvocation(CTX_POST));
|
|
|
|
|
|
CHECK (detector.ensureNoInvocation(CTX_DONE));
|
|
|
|
|
|
CHECK (detector.ensureNoInvocation(CTX_TICK));
|
|
|
|
|
|
|
|
|
|
|
|
ctx.work (t,x);
|
|
|
|
|
|
CHECK (detector.verifyInvocation(CTX_WORK).arg(t,x));
|
|
|
|
|
|
|
|
|
|
|
|
ctx.done (t,x);
|
|
|
|
|
|
CHECK (detector.verifyInvocation(CTX_DONE).arg(t,x));
|
|
|
|
|
|
|
2023-12-13 19:42:38 +01:00
|
|
|
|
CHECK (activity::PASS == ctx.post (t,td, &a, ctx));
|
|
|
|
|
|
CHECK (detector.verifyInvocation(CTX_POST).arg(t,td,&a,ctx));
|
2023-08-17 19:37:38 +02:00
|
|
|
|
|
|
|
|
|
|
CHECK (activity::PASS == ctx.tick(t));
|
2023-08-18 04:00:21 +02:00
|
|
|
|
CHECK (detector.verifyInvocation(CTX_TICK).arg(t));
|
|
|
|
|
|
|
2023-08-22 20:13:13 +02:00
|
|
|
|
detector.incrementSeq();
|
Scheduler-test: rework handling of notifications in the Activity-Language
While the recent refactoring...
206c67cc
...was a step into the right direction, it pushed too hard,
overlooking the requirement to protect the scheduler contents
and thus all of the Activity-chains against concurrent modification.
Moreover, the recent solution still seems not quite orthogonal.
Thus the handling of notifications was thoroughly reworked:
- the explicit "double-dispatch" was removed, since actual usage
of the language indicates that we only need notifications to
Gate (and Hook), but not to any other conceivable Activity.
- thus it seems unnecessary to turn "notification" into some kind
of secondary work mode. Rather, it is folded as special case
into the regular dispatch.
This leads to new processing rules:
- a POST goes into λ-post (obviously... that's its meaning)
- a NOTIFY now passes its *target* into λ-post
- λ-post invokes ''dispatch''
- and **dispatching a Gate now implies to notify the Gate**
This greatly simplifies the »state machine« in the Activity-Language,
but also incurs some limitations (which seems adequate, since it is
now clear that we do not ''schedule'' or ''dispatch'' arbitrary
Activities — rather we'll do this only with POST and NOTIFY,
and all further processing happens by passing activation
along the chain, without involving the Scheduler)
2023-12-16 23:47:50 +01:00
|
|
|
|
ctx.tick.returning(activity::KICK);
|
|
|
|
|
|
CHECK (activity::KICK == ctx.tick(t));
|
2023-08-19 17:48:38 +02:00
|
|
|
|
CHECK (detector.verifyInvocation(CTX_TICK).timeArg(t));
|
2023-08-18 04:00:21 +02:00
|
|
|
|
|
2023-08-19 17:48:38 +02:00
|
|
|
|
CHECK (detector.verifyInvocation(CTX_WORK).timeArg(t)
|
|
|
|
|
|
.beforeInvocation(CTX_DONE).timeArg(t)
|
|
|
|
|
|
.beforeInvocation(CTX_POST).timeArg(t)
|
|
|
|
|
|
.beforeInvocation(CTX_TICK).timeArg(t).seq(0)
|
|
|
|
|
|
.beforeInvocation(CTX_TICK).timeArg(t).seq(1));
|
2023-08-17 19:37:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-18 04:00:21 +02:00
|
|
|
|
|
2023-10-18 15:50:11 +02:00
|
|
|
|
/** @test a rigged diagnostic probe to detect Activity activation
|
2023-07-13 01:51:21 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
2023-10-18 15:50:11 +02:00
|
|
|
|
watch_ActivationProbe()
|
2023-07-13 01:51:21 +02:00
|
|
|
|
{
|
2023-08-18 19:37:44 +02:00
|
|
|
|
ActivityDetector detector;
|
2023-08-19 17:48:38 +02:00
|
|
|
|
auto someID = "trap-" + randStr(4);
|
2023-08-18 19:37:44 +02:00
|
|
|
|
Activity& probe = detector.buildActivationProbe (someID);
|
2023-09-01 17:39:55 +02:00
|
|
|
|
CHECK (probe.is (Activity::HOOK));
|
2023-08-19 17:48:38 +02:00
|
|
|
|
|
2023-10-25 22:40:13 +02:00
|
|
|
|
CHECK (not detector.wasInvoked (probe));
|
|
|
|
|
|
|
2023-08-19 17:48:38 +02:00
|
|
|
|
Time realTime = RealClock::now();
|
|
|
|
|
|
probe.activate (realTime, detector.executionCtx);
|
|
|
|
|
|
|
|
|
|
|
|
CHECK (detector.verifyInvocation(someID).timeArg(realTime));
|
2023-10-25 22:40:13 +02:00
|
|
|
|
|
|
|
|
|
|
// Probe instance recalls last invocation "now" argument
|
|
|
|
|
|
CHECK (realTime == detector.invokeTime (probe));
|
|
|
|
|
|
CHECK (detector.wasInvoked (probe));
|
2023-08-19 17:48:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-19 23:59:18 +02:00
|
|
|
|
/** @test diagnostic adaptor to detect and pass-through Activity activation
|
2023-08-19 17:48:38 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
2023-08-19 23:59:18 +02:00
|
|
|
|
watch_ActivationTap()
|
2023-08-19 17:48:38 +02:00
|
|
|
|
{
|
|
|
|
|
|
ActivityDetector detector;
|
2023-08-19 19:06:44 +02:00
|
|
|
|
|
|
|
|
|
|
Time nomTime{99,11};
|
|
|
|
|
|
Activity feed{size_t{12},size_t{34}};
|
|
|
|
|
|
Activity feed2{size_t{56},size_t{78}};
|
|
|
|
|
|
feed.next = &feed2;
|
|
|
|
|
|
string jobID = "job-" + randStr(4);
|
|
|
|
|
|
Activity invoke{detector.buildMockJobFunctor(jobID), nomTime, feed};
|
|
|
|
|
|
|
|
|
|
|
|
Time t1{0,1,1};
|
|
|
|
|
|
CHECK (activity::PASS == invoke.activate (t1, detector.executionCtx));
|
|
|
|
|
|
CHECK (detector.verifyInvocation (jobID).arg(nomTime, 12));
|
|
|
|
|
|
|
|
|
|
|
|
// decorate the INVOKE-Activity with an ActivationTap
|
|
|
|
|
|
Activity& tap = detector.buildActivationTap (invoke);
|
|
|
|
|
|
CHECK (tap.next == invoke.next);
|
|
|
|
|
|
|
2023-08-22 20:13:13 +02:00
|
|
|
|
detector.incrementSeq();
|
2023-08-19 19:06:44 +02:00
|
|
|
|
Time t2{0,2,2};
|
|
|
|
|
|
// now activate through the Tap....
|
|
|
|
|
|
tap.activate(t2, detector.executionCtx);
|
|
|
|
|
|
CHECK (detector.verifySeqIncrement(1) // ==> the ActivationTap "tap-INVOKE" reports and passes activation
|
|
|
|
|
|
.beforeInvocation("tap-INVOKE").seq(1).arg("JobFun-ActivityDetector."+jobID)
|
|
|
|
|
|
.beforeInvocation(jobID).seq(1).arg(nomTime,12));
|
|
|
|
|
|
|
|
|
|
|
|
// WARNING: can still activate the watched subject directly...
|
2023-08-22 20:13:13 +02:00
|
|
|
|
detector.incrementSeq();
|
2023-08-19 19:06:44 +02:00
|
|
|
|
Time t3{0,3,3};
|
|
|
|
|
|
invoke.activate (t3, detector.executionCtx);
|
|
|
|
|
|
CHECK (detector.verifyInvocation(jobID).seq(2)); // subject invoked
|
|
|
|
|
|
CHECK (detector.ensureNoInvocation("tap-INVOKE").seq(2) // but invocation not detected by ActivationTap
|
|
|
|
|
|
.beforeInvocation(jobID).seq(2));
|
2023-08-19 17:48:38 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-19 23:59:18 +02:00
|
|
|
|
/** @test inject (prepend) an ActivationTap into existing wiring
|
|
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
insert_ActivationTap()
|
|
|
|
|
|
{
|
|
|
|
|
|
ActivityDetector detector;
|
|
|
|
|
|
|
|
|
|
|
|
Activity subject;
|
|
|
|
|
|
Activity followUp{size_t(1), size_t(2)};
|
|
|
|
|
|
subject.next = &followUp;
|
|
|
|
|
|
Activity* wiring = &subject;
|
|
|
|
|
|
CHECK (isSameObject (*wiring, subject));
|
|
|
|
|
|
CHECK (wiring->verb_ == Activity::TICK);
|
|
|
|
|
|
|
|
|
|
|
|
detector.insertActivationTap (wiring);
|
|
|
|
|
|
CHECK (not isSameObject (*wiring, subject));
|
|
|
|
|
|
CHECK (wiring->verb_ == Activity::HOOK);
|
|
|
|
|
|
CHECK (wiring->data_.callback.arg == size_t(&subject));
|
|
|
|
|
|
CHECK (wiring->next == subject.next);
|
|
|
|
|
|
|
|
|
|
|
|
Time tt{1,1,1};
|
|
|
|
|
|
// now activate through the wiring....
|
|
|
|
|
|
wiring->activate(tt, detector.executionCtx);
|
|
|
|
|
|
CHECK (detector.verifyInvocation("tap-TICK").arg("⧐ Act(TICK")
|
|
|
|
|
|
.beforeInvocation("CTX-tick").timeArg(tt));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-10-18 15:50:11 +02:00
|
|
|
|
/** @test diagnostic setup to detect and watch passing a notification
|
2023-08-21 18:23:57 +02:00
|
|
|
|
* - setup a chain-Activity (here: a `TICK`) protected by a `GATE`
|
|
|
|
|
|
* - configure the `GATE` to require one notification
|
|
|
|
|
|
* - connect a `NOTIFY`-Activity to trigger the `GATE`
|
|
|
|
|
|
* - inject a diagnostics Tap into the notification-connection
|
|
|
|
|
|
* - dispatch of the notification can be verified
|
|
|
|
|
|
* - notification has been passed through the Tap to the `GATE`
|
|
|
|
|
|
* - `GATE` has been decremented to zero and triggers chain
|
2023-10-18 15:50:11 +02:00
|
|
|
|
* - finally the chained `TICK`-Activity calls into the `executionCtx`
|
2023-08-19 17:48:38 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
|
|
|
|
|
watch_notification()
|
|
|
|
|
|
{
|
Scheduler-test: rework handling of notifications in the Activity-Language
While the recent refactoring...
206c67cc
...was a step into the right direction, it pushed too hard,
overlooking the requirement to protect the scheduler contents
and thus all of the Activity-chains against concurrent modification.
Moreover, the recent solution still seems not quite orthogonal.
Thus the handling of notifications was thoroughly reworked:
- the explicit "double-dispatch" was removed, since actual usage
of the language indicates that we only need notifications to
Gate (and Hook), but not to any other conceivable Activity.
- thus it seems unnecessary to turn "notification" into some kind
of secondary work mode. Rather, it is folded as special case
into the regular dispatch.
This leads to new processing rules:
- a POST goes into λ-post (obviously... that's its meaning)
- a NOTIFY now passes its *target* into λ-post
- λ-post invokes ''dispatch''
- and **dispatching a Gate now implies to notify the Gate**
This greatly simplifies the »state machine« in the Activity-Language,
but also incurs some limitations (which seems adequate, since it is
now clear that we do not ''schedule'' or ''dispatch'' arbitrary
Activities — rather we'll do this only with POST and NOTIFY,
and all further processing happens by passing activation
along the chain, without involving the Scheduler)
2023-12-16 23:47:50 +01:00
|
|
|
|
Time tt{11,11}; // start time of the NOTIFY
|
|
|
|
|
|
Time ts{22,22}; // start time of the target-chain
|
|
|
|
|
|
Time td{33,33}; // deadline for the target-chain
|
2023-08-19 17:48:38 +02:00
|
|
|
|
ActivityDetector detector;
|
2023-08-21 18:23:57 +02:00
|
|
|
|
|
|
|
|
|
|
Activity chain;
|
Scheduler-test: rework handling of notifications in the Activity-Language
While the recent refactoring...
206c67cc
...was a step into the right direction, it pushed too hard,
overlooking the requirement to protect the scheduler contents
and thus all of the Activity-chains against concurrent modification.
Moreover, the recent solution still seems not quite orthogonal.
Thus the handling of notifications was thoroughly reworked:
- the explicit "double-dispatch" was removed, since actual usage
of the language indicates that we only need notifications to
Gate (and Hook), but not to any other conceivable Activity.
- thus it seems unnecessary to turn "notification" into some kind
of secondary work mode. Rather, it is folded as special case
into the regular dispatch.
This leads to new processing rules:
- a POST goes into λ-post (obviously... that's its meaning)
- a NOTIFY now passes its *target* into λ-post
- λ-post invokes ''dispatch''
- and **dispatching a Gate now implies to notify the Gate**
This greatly simplifies the »state machine« in the Activity-Language,
but also incurs some limitations (which seems adequate, since it is
now clear that we do not ''schedule'' or ''dispatch'' arbitrary
Activities — rather we'll do this only with POST and NOTIFY,
and all further processing happens by passing activation
along the chain, without involving the Scheduler)
2023-12-16 23:47:50 +01:00
|
|
|
|
Activity gate{1, td};
|
2023-08-21 18:23:57 +02:00
|
|
|
|
gate.next = &chain;
|
Scheduler-test: rework handling of notifications in the Activity-Language
While the recent refactoring...
206c67cc
...was a step into the right direction, it pushed too hard,
overlooking the requirement to protect the scheduler contents
and thus all of the Activity-chains against concurrent modification.
Moreover, the recent solution still seems not quite orthogonal.
Thus the handling of notifications was thoroughly reworked:
- the explicit "double-dispatch" was removed, since actual usage
of the language indicates that we only need notifications to
Gate (and Hook), but not to any other conceivable Activity.
- thus it seems unnecessary to turn "notification" into some kind
of secondary work mode. Rather, it is folded as special case
into the regular dispatch.
This leads to new processing rules:
- a POST goes into λ-post (obviously... that's its meaning)
- a NOTIFY now passes its *target* into λ-post
- λ-post invokes ''dispatch''
- and **dispatching a Gate now implies to notify the Gate**
This greatly simplifies the »state machine« in the Activity-Language,
but also incurs some limitations (which seems adequate, since it is
now clear that we do not ''schedule'' or ''dispatch'' arbitrary
Activities — rather we'll do this only with POST and NOTIFY,
and all further processing happens by passing activation
along the chain, without involving the Scheduler)
2023-12-16 23:47:50 +01:00
|
|
|
|
Activity notification{&gate, ts}; // note: follow-up start time `ts` injected here
|
2023-08-21 18:23:57 +02:00
|
|
|
|
CHECK (gate.data_.condition.rest == 1);
|
|
|
|
|
|
|
|
|
|
|
|
detector.insertActivationTap (notification.data_.notification.target);
|
|
|
|
|
|
|
Scheduler-test: rework handling of notifications in the Activity-Language
While the recent refactoring...
206c67cc
...was a step into the right direction, it pushed too hard,
overlooking the requirement to protect the scheduler contents
and thus all of the Activity-chains against concurrent modification.
Moreover, the recent solution still seems not quite orthogonal.
Thus the handling of notifications was thoroughly reworked:
- the explicit "double-dispatch" was removed, since actual usage
of the language indicates that we only need notifications to
Gate (and Hook), but not to any other conceivable Activity.
- thus it seems unnecessary to turn "notification" into some kind
of secondary work mode. Rather, it is folded as special case
into the regular dispatch.
This leads to new processing rules:
- a POST goes into λ-post (obviously... that's its meaning)
- a NOTIFY now passes its *target* into λ-post
- λ-post invokes ''dispatch''
- and **dispatching a Gate now implies to notify the Gate**
This greatly simplifies the »state machine« in the Activity-Language,
but also incurs some limitations (which seems adequate, since it is
now clear that we do not ''schedule'' or ''dispatch'' arbitrary
Activities — rather we'll do this only with POST and NOTIFY,
and all further processing happens by passing activation
along the chain, without involving the Scheduler)
2023-12-16 23:47:50 +01:00
|
|
|
|
notification.activate (tt, detector.executionCtx); // dispatch time `tt` (is actually irrelevant here)
|
|
|
|
|
|
// activating the NOTIFY causes it to POST its target, thereby setting the deadline from the GATE
|
|
|
|
|
|
CHECK (detector.verifyInvocation("CTX-post").arg("22.022","33.033", "tap-GATE", "≺test::CTX≻"));
|
2023-08-21 18:23:57 +02:00
|
|
|
|
|
Scheduler-test: rework handling of notifications in the Activity-Language
While the recent refactoring...
206c67cc
...was a step into the right direction, it pushed too hard,
overlooking the requirement to protect the scheduler contents
and thus all of the Activity-chains against concurrent modification.
Moreover, the recent solution still seems not quite orthogonal.
Thus the handling of notifications was thoroughly reworked:
- the explicit "double-dispatch" was removed, since actual usage
of the language indicates that we only need notifications to
Gate (and Hook), but not to any other conceivable Activity.
- thus it seems unnecessary to turn "notification" into some kind
of secondary work mode. Rather, it is folded as special case
into the regular dispatch.
This leads to new processing rules:
- a POST goes into λ-post (obviously... that's its meaning)
- a NOTIFY now passes its *target* into λ-post
- λ-post invokes ''dispatch''
- and **dispatching a Gate now implies to notify the Gate**
This greatly simplifies the »state machine« in the Activity-Language,
but also incurs some limitations (which seems adequate, since it is
now clear that we do not ''schedule'' or ''dispatch'' arbitrary
Activities — rather we'll do this only with POST and NOTIFY,
and all further processing happens by passing activation
along the chain, without involving the Scheduler)
2023-12-16 23:47:50 +01:00
|
|
|
|
detector.incrementSeq();
|
|
|
|
|
|
// to see the effect of the instrumentation, we need to mimic the behaviour of λ-post,
|
|
|
|
|
|
// which is to call Activity::dispatch() on the given target
|
|
|
|
|
|
notification.data_.notification.target->dispatch (ts, detector.executionCtx); // note: using `ts` for the follow-up chain
|
|
|
|
|
|
CHECK (detector.verifyInvocation("tap-GATE").seq(1).arg("22.022 --notify-↯> Act(GATE"));
|
2023-08-21 18:23:57 +02:00
|
|
|
|
CHECK (gate.data_.condition.rest == 0);
|
2023-07-13 01:51:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2023-08-20 02:39:57 +02:00
|
|
|
|
/** @test diagnostic setup to watch Activity::GATE activation
|
|
|
|
|
|
* - when applied, Tap will be inserted before and after the
|
|
|
|
|
|
* instrumented GATE-Activity
|
|
|
|
|
|
* - it can thus be traced when the Gate is activated,
|
|
|
|
|
|
* but also when the Gate condition is met and the `next`
|
|
|
|
|
|
* Activity after the Gate is activated
|
|
|
|
|
|
* - for this unit-test, a Gate and a follow-up Activity
|
|
|
|
|
|
* is invoked directly, to verify the generated log entries
|
2023-07-13 01:51:21 +02:00
|
|
|
|
*/
|
|
|
|
|
|
void
|
2023-08-19 17:48:38 +02:00
|
|
|
|
watch_gate()
|
2023-07-13 01:51:21 +02:00
|
|
|
|
{
|
2023-08-20 01:35:14 +02:00
|
|
|
|
ActivityDetector detector;
|
|
|
|
|
|
|
|
|
|
|
|
Activity gate{0};
|
|
|
|
|
|
Activity followUp;
|
|
|
|
|
|
gate.next = &followUp;
|
|
|
|
|
|
|
|
|
|
|
|
Activity* wiring = &gate;
|
|
|
|
|
|
detector.watchGate (wiring);
|
|
|
|
|
|
|
|
|
|
|
|
Time tt{5,5};
|
|
|
|
|
|
wiring->activate(tt, detector.executionCtx);
|
2023-08-22 20:13:13 +02:00
|
|
|
|
detector.incrementSeq();
|
2023-08-20 02:39:57 +02:00
|
|
|
|
wiring->next->activate(tt, detector.executionCtx);
|
2023-08-20 01:35:14 +02:00
|
|
|
|
|
2023-08-20 02:39:57 +02:00
|
|
|
|
CHECK (detector.verifyInvocation("tap-GATE").seq(0).timeArg(tt)
|
|
|
|
|
|
.beforeSeqIncrement(1)
|
2023-09-01 21:59:25 +02:00
|
|
|
|
.beforeInvocation("after-GATE").seq(1).timeArg(tt)
|
2023-08-20 02:39:57 +02:00
|
|
|
|
.beforeInvocation("CTX-tick").seq(1).timeArg(tt));
|
2023-07-13 01:51:21 +02:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Register this test class... */
|
|
|
|
|
|
LAUNCHER (ActivityDetector_test, "unit engine");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}}} // namespace vault::gear::test
|