2023-07-03 18:40:37 +02:00
/*
SchedulerActivity ( Test ) - verify activities processed in the scheduler
Copyright ( C ) Lumiera . org
2023 , Hermann Vosseler < Ichthyostega @ web . de >
This program 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 .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/** @file scheduler-activity-test.cpp
* * unit test \ ref SchedulerActivity_test
*/
# include "lib/test/run.hpp"
2023-08-18 16:54:35 +02:00
# include "lib/test/test-helper.hpp"
2023-08-17 19:37:38 +02:00
# include "activity-detector.hpp"
2023-07-03 18:40:37 +02:00
# include "vault/gear/activity-lang.hpp"
2023-08-18 16:54:35 +02:00
# include "vault/real-clock.hpp"
2023-07-07 03:41:30 +02:00
# include "lib/time/timevalue.hpp"
2023-09-02 23:57:30 +02:00
# include "lib/format-cout.hpp"
2023-07-03 18:40:37 +02:00
using test : : Test ;
2023-07-07 03:41:30 +02:00
using lib : : time : : Time ;
using lib : : time : : FSecs ;
2023-07-03 18:40:37 +02:00
namespace vault {
2023-07-13 01:51:21 +02:00
namespace gear {
2023-07-03 18:40:37 +02:00
namespace test {
/*****************************************************************/ /**
* @ test verify behaviour of the Scheduler _Activity Language . _
* @ see SchedulerCommutator_test
* @ see SchedulerUsage_test
*/
class SchedulerActivity_test : public Test
{
virtual void
run ( Arg )
{
2023-07-13 01:51:21 +02:00
simpleUsage ( ) ;
2023-08-18 16:54:35 +02:00
verifyActivity_Post ( ) ;
2023-08-22 17:37:58 +02:00
verifyActivity_Invoke ( ) ;
verifyActivity_Notify_activate ( ) ;
verifyActivity_Notify_dispatch ( ) ;
2023-08-22 18:57:15 +02:00
verifyActivity_Gate_pass ( ) ;
verifyActivity_Gate_dead ( ) ;
verifyActivity_Gate_block ( ) ;
verifyActivity_Gate_opened ( ) ;
2023-07-13 01:51:21 +02:00
termBuilder ( ) ;
2023-08-30 22:19:57 +02:00
dispatchChain ( ) ;
2023-07-13 01:51:21 +02:00
scenario_RenderJob ( ) ;
2023-08-31 20:18:35 +02:00
scenario_Notification ( ) ;
2023-07-13 01:51:21 +02:00
scenario_IOJob ( ) ;
scenario_MetaJob ( ) ;
2023-07-03 18:40:37 +02:00
}
2023-08-22 17:37:58 +02:00
/** @test demonstrate simple Activity usage */
2023-07-03 18:40:37 +02:00
void
simpleUsage ( )
{
2023-07-06 16:35:42 +02:00
// Activities are »POD with constructor«
2023-08-22 17:37:58 +02:00
Activity start { Activity : : WORKSTART } ;
CHECK ( start . verb_ = = Activity : : WORKSTART ) ;
CHECK ( start . next = = nullptr ) ;
CHECK ( start . data_ . timing . instant = = Time : : NEVER ) ; //////////////////////////////////////////TICKET #1317 : the purpose of this time data is not clear yet
CHECK ( start . data_ . timing . quality = = 0 ) ;
// use the ActivityDetector for test instrumentation...
ActivityDetector detector ;
// Activities can be invoked within an ExecutionContext
Time now = RealClock : : now ( ) ;
start . activate ( now , detector . executionCtx ) ;
// In this case, activation causes invocation of λ-work on the context
CHECK ( detector . verifyInvocation ( " CTX-work " ) . arg ( now , 0 ) ) ;
// cout << detector.showLog()<<endl; // HINT: use this for investigation...
}
/** @test behaviour of Activity::POST
2023-08-22 18:38:40 +02:00
* - invoke the λ - post to dispatch the chain through the queue
* - the chain to be executed is given as ` next `
* - time window for scheduling as data field
2023-10-17 01:57:17 +02:00
* - the start time from this window becomes the ` when ` parameter
* - the ` now ` parameter from the activation is thus ignored
2023-08-22 17:37:58 +02:00
*/
void
verifyActivity_Post ( )
{
Activity chain ;
Activity post { Time { 0 , 11 } , Time { 0 , 22 } , & chain } ;
2023-09-01 17:39:55 +02:00
CHECK ( chain . is ( Activity : : TICK ) ) ;
CHECK ( post . is ( Activity : : POST ) ) ;
2023-08-22 17:37:58 +02:00
CHECK ( Time ( 0 , 11 ) = = post . data_ . timeWindow . life ) ;
CHECK ( Time ( 0 , 22 ) = = post . data_ . timeWindow . dead ) ;
CHECK ( & chain = = post . next ) ;
ActivityDetector detector ;
2023-10-17 01:57:17 +02:00
Time tt { 5 , 5 } ;
2023-08-22 17:37:58 +02:00
post . activate ( tt , detector . executionCtx ) ;
2023-10-17 01:57:17 +02:00
CHECK ( detector . verifyInvocation ( " CTX-post " ) . arg ( " 11.000 " , " Act(POST " , " ≺test::CTX≻ " ) ) ;
2023-07-03 18:40:37 +02:00
}
2023-08-18 16:54:35 +02:00
/** @test behaviour of Activity::INVOKE
* - setup requires two FEED - Activities to be chained up as arguments
* - use the rigged execution context provided by ActivityDetector
* - can verify this way that the activation leads to JobFunctor invocation
2023-07-03 18:40:37 +02:00
*/
void
2023-07-13 01:51:21 +02:00
verifyActivity_Invoke ( )
2023-07-03 18:40:37 +02:00
{
2023-08-17 19:37:38 +02:00
ActivityDetector detector ;
2023-08-18 16:54:35 +02:00
2023-08-29 04:19:19 +02:00
uint64_t x1 = rand ( ) , x2 = rand ( ) ;
2023-08-18 16:54:35 +02:00
Time nomTime = lib : : test : : randTime ( ) ;
Activity feed { x1 , x2 } ;
Activity feed2 { x1 + 1 , x1 + 2 } ;
feed . next = & feed2 ;
Activity invoke { detector . buildMockJobFunctor ( " job " ) , nomTime , feed } ;
Time realTime = RealClock : : now ( ) ;
CHECK ( activity : : PASS = = invoke . activate ( realTime , detector . executionCtx ) ) ;
CHECK ( detector . verifyInvocation ( " job " ) . arg ( nomTime , x1 ) ) ;
2023-07-03 18:40:37 +02:00
}
2023-08-22 20:13:13 +02:00
/** @test behaviour of Activity::NOTIFY when _activated_
2023-08-22 17:37:58 +02:00
* - notification is dispatched as special message to an indicated target Activity
* - when activated , a ` NOTIFY ` - Activity _posts itself_ through the Execution Context hook
* - this way , further processing will happen in management mode ( single threaded )
2023-07-03 18:40:37 +02:00
*/
void
2023-08-22 17:37:58 +02:00
verifyActivity_Notify_activate ( )
2023-07-13 01:51:21 +02:00
{
2023-08-22 17:37:58 +02:00
Activity chain ;
Activity notify { & chain } ;
ActivityDetector detector ;
Time tt { 111 , 11 } ;
notify . activate ( tt , detector . executionCtx ) ;
2023-08-22 20:13:13 +02:00
2023-08-22 18:57:15 +02:00
CHECK ( detector . verifyInvocation ( " CTX-post " ) . arg ( " 11.111 " , " Act(NOTIFY " , " ≺test::CTX≻ " ) ) ;
2023-07-13 01:51:21 +02:00
}
2023-08-22 20:13:13 +02:00
/** @test behaviour of Activity::NOTIFY when activation leads to a _dispatch_
2023-08-22 17:37:58 +02:00
* - when _posting_ a ` NOTIFY ` , a dedicated _notification_ function is invoked on the chain
* - what actually happens then depends on the receiver ; here we just activate a test - Tap
2023-08-18 16:54:35 +02:00
*/
void
2023-08-22 17:37:58 +02:00
verifyActivity_Notify_dispatch ( )
2023-08-18 16:54:35 +02:00
{
2023-08-22 17:37:58 +02:00
ActivityDetector detector ;
2023-08-22 18:57:15 +02:00
// use a diagnostic Tap as chain to detect passing of notification
Activity notify { detector . buildActivationProbe ( " notifyTargetActivity " ) } ;
2023-08-22 17:37:58 +02:00
Time tt { 111 , 11 } ;
notify . dispatch ( tt , detector . executionCtx ) ;
2023-08-22 20:13:13 +02:00
2023-08-22 18:57:15 +02:00
CHECK ( detector . verifyInvocation ( " notifyTargetActivity " ) . arg ( " 11.111 " ) ) ;
2023-08-18 16:54:35 +02:00
}
2023-08-22 20:13:13 +02:00
/** @test behaviour of Activity::GATE:
* if conditions are met , the activation is just passed ,
* so the executor ( in the Scheduler ) will just invoke the chain
2023-08-22 18:57:15 +02:00
*/
void
verifyActivity_Gate_pass ( )
{
Activity chain ;
Activity gate { 0 } ;
gate . next = & chain ;
ActivityDetector detector ;
2023-08-22 20:13:13 +02:00
Activity & wiring = detector . buildGateWatcher ( gate ) ;
Time tt { 333 , 33 } ;
CHECK ( activity : : PASS = = wiring . activate ( tt , detector . executionCtx ) ) ;
CHECK ( detector . verifyInvocation ( " tap-GATE " ) . arg ( " 33.333 ⧐ Act(GATE " ) ) ;
2023-08-22 18:57:15 +02:00
}
2023-08-22 23:52:28 +02:00
/** @test behaviour of Activity::GATE:
* the rest of the chain is just skipped in case of deadline violation
2023-07-13 01:51:21 +02:00
*/
void
2023-08-22 18:57:15 +02:00
verifyActivity_Gate_dead ( )
2023-07-13 01:51:21 +02:00
{
2023-08-22 18:57:15 +02:00
Activity chain ;
2023-08-22 20:13:13 +02:00
Activity gate { 0 , Time { 333 , 33 } } ;
2023-08-22 18:57:15 +02:00
gate . next = & chain ;
ActivityDetector detector ;
2023-08-22 20:13:13 +02:00
Activity & wiring = detector . buildGateWatcher ( gate ) ;
Time t1 { 330 , 33 } ; // still before the deadline
Time t2 { 333 , 33 } ; // exactly at deadline => rejected
Time t3 { 335 , 33 } ; // after the deadline => rejected
CHECK ( activity : : PASS = = wiring . activate ( t1 , detector . executionCtx ) ) ;
CHECK ( detector . verifyInvocation ( " tap-GATE " ) . arg ( " 33.330 ⧐ Act(GATE " ) . seq ( 0 ) ) ;
detector . incrementSeq ( ) ;
CHECK ( activity : : SKIP = = wiring . activate ( t2 , detector . executionCtx ) ) ;
CHECK ( detector . verifyInvocation ( " tap-GATE " ) . arg ( " 33.333 ⧐ Act(GATE " ) . seq ( 1 ) ) ;
detector . incrementSeq ( ) ;
CHECK ( activity : : SKIP = = wiring . activate ( t3 , detector . executionCtx ) ) ;
CHECK ( detector . verifyInvocation ( " tap-GATE " ) . arg ( " 33.335 ⧐ Act(GATE " ) . seq ( 2 ) ) ;
2023-08-22 18:57:15 +02:00
}
2023-08-22 23:52:28 +02:00
/** @test behaviour of Activity::GATE:
2023-08-22 20:13:13 +02:00
* the count - down condition determines if activation _passes_
* or will _spin around_ for later re - try
2023-08-22 18:57:15 +02:00
*/
void
verifyActivity_Gate_block ( )
{
Activity chain ;
2023-08-22 20:13:13 +02:00
Activity gate { 23 } ;
2023-08-22 18:57:15 +02:00
gate . next = & chain ;
ActivityDetector detector ;
2023-08-22 20:13:13 +02:00
Activity & wiring = detector . buildGateWatcher ( gate ) ;
Time tt { 333 , 33 } ;
CHECK ( activity : : SKIP = = wiring . activate ( tt , detector . executionCtx ) ) ;
CHECK ( 23 = = gate . data_ . condition . rest ) ; // prerequisite-count not altered
2023-08-31 23:55:42 +02:00
Time reScheduled = tt + detector . executionCtx . getWaitDelay ( ) ;
2023-08-22 20:13:13 +02:00
CHECK ( tt < reScheduled ) ;
2023-08-22 23:52:28 +02:00
2023-08-22 20:13:13 +02:00
CHECK ( detector . verifyInvocation ( " tap-GATE " ) . arg ( " 33.333 ⧐ Act(GATE " )
. beforeInvocation ( " CTX-post " ) . arg ( reScheduled , " Act(GATE " , " ≺test::CTX≻ " ) ) ;
2023-08-22 18:57:15 +02:00
}
2023-08-22 23:52:28 +02:00
/** @test behaviour of Activity::GATE on notification
* - Gate configured initially such that it blocks
* ( without violating deadline )
* - thus a regular activation signals to skip the chain ,
* but also re - schedules a further check into the future
* - when receiving a notification , the latch is decremented
* - if this causes the Gate to open , the chain is immediately
* scheduled for activation , but the Gate also locked forever
2023-10-16 22:46:31 +02:00
* - neither a further activation , nor a further notification
2023-08-22 23:52:28 +02:00
* has any effect after this point . . .
2023-08-22 18:57:15 +02:00
*/
void
verifyActivity_Gate_opened ( )
{
Activity chain ;
2023-08-22 20:13:13 +02:00
Activity gate { 1 } ;
2023-08-22 18:57:15 +02:00
gate . next = & chain ;
2023-08-22 23:52:28 +02:00
// Conditionals in the gate block invocations
CHECK ( gate . data_ . condition . isHold ( ) ) ;
CHECK ( gate . data_ . condition . rest = = 1 ) ;
CHECK ( gate . data_ . condition . dead = = Time : : NEVER ) ;
2023-08-22 18:57:15 +02:00
ActivityDetector detector ;
2023-08-22 20:13:13 +02:00
Activity & wiring = detector . buildGateWatcher ( gate ) ;
Time tt { 333 , 33 } ;
2023-08-31 23:55:42 +02:00
Time reScheduled = tt + detector . executionCtx . getWaitDelay ( ) ; // retrieve the next time to retry
2023-08-22 23:52:28 +02:00
CHECK ( tt < reScheduled ) ;
// an attempt to activate blocks (and re-schedules for later retry)
2023-08-22 20:13:13 +02:00
CHECK ( activity : : SKIP = = wiring . activate ( tt , detector . executionCtx ) ) ;
2023-08-22 23:52:28 +02:00
CHECK ( 1 = = gate . data_ . condition . rest ) ; // unchanged (and locked)...
CHECK ( detector . verifyInvocation ( " tap-GATE " ) . arg ( " 33.333 ⧐ Act(GATE " )
. beforeInvocation ( " CTX-post " ) . arg ( reScheduled , " Act(GATE " , " ≺test::CTX≻ " ) ) ;
2023-08-22 20:13:13 +02:00
detector . incrementSeq ( ) ;
// Gate receives a notification from some prerequisite Activity
CHECK ( activity : : PASS = = wiring . notify ( tt , detector . executionCtx ) ) ;
CHECK ( 0 = = gate . data_ . condition . rest ) ; // condition has been decremented...
CHECK ( detector . verifyInvocation ( " tap-GATE " ) . seq ( 0 ) . arg ( " 33.333 ⧐ Act(GATE " )
. beforeInvocation ( " CTX-post " ) . seq ( 0 ) . arg ( reScheduled , " Act(GATE " , " ≺test::CTX≻ " )
. beforeInvocation ( " tap-GATE " ) . seq ( 1 ) . arg ( " 33.333 --notify-↯> Act(GATE " )
2023-08-30 22:19:57 +02:00
. beforeInvocation ( " CTX-post " ) . seq ( 1 ) . arg ( tt , " after-GATE " , " ≺test::CTX≻ " ) ) ;
2023-08-22 23:52:28 +02:00
CHECK ( gate . data_ . condition . dead = = Time : : MIN ) ;
detector . incrementSeq ( ) ;
Time ttt { 444 , 44 } ;
// when the re-scheduled check happens later, it is blocked to prevent double activation
CHECK ( activity : : SKIP = = wiring . activate ( ttt , detector . executionCtx ) ) ;
CHECK ( detector . verifyInvocation ( " tap-GATE " ) . seq ( 2 ) . arg ( " 44.444 ⧐ Act(GATE " ) ) ;
CHECK ( detector . ensureNoInvocation ( " CTX-post " ) . seq ( 2 ) ) ;
CHECK ( gate . data_ . condition . dead = = Time : : MIN ) ;
detector . incrementSeq ( ) ;
// even a further notification has no effect now....
wiring . notify ( ttt , detector . executionCtx ) ;
// conditionals were not touched:
CHECK ( gate . data_ . condition . dead = = Time : : MIN ) ;
CHECK ( gate . data_ . condition . rest = = 0 ) ;
// the log shows the further notification (at Seq=3) but no dispatch happens anymore
CHECK ( detector . verifySeqIncrement ( 3 )
. beforeInvocation ( " tap-GATE " ) . seq ( 3 ) . arg ( " 44.444 --notify-↯> Act(GATE " ) ) ;
2023-08-30 22:19:57 +02:00
CHECK ( detector . ensureNoInvocation ( " CTX-post " ) . seq ( 3 ) . arg ( tt , " after-GATE " , " ≺test::CTX≻ " ) ) ;
2023-08-22 23:52:28 +02:00
// cout << detector.showLog()<<endl; // HINT: use this for investigation...
2023-07-13 01:51:21 +02:00
}
2023-08-29 19:10:24 +02:00
/** @test verify the Activity term builder
* - use the builder syntax to define a simple Activity chain
* - verify the basic outfitting and sane connectivity
* - verify values reported by the BlockFlow allocator
* - ensure the defined Job can be properly invoked
2023-07-13 01:51:21 +02:00
*/
void
termBuilder ( )
{
2023-08-29 02:18:07 +02:00
ActivityDetector detector ;
BlockFlowAlloc bFlow ;
ActivityLang activityLang { bFlow } ;
2023-08-29 18:46:37 +02:00
2023-08-29 02:18:07 +02:00
Time start { 0 , 1 } ;
Time dead { 0 , 10 } ;
2023-08-29 18:46:37 +02:00
Activity * act { nullptr } ;
{
auto term = activityLang . buildCalculationJob ( detector . buildMockJob ( ) , start , dead ) ;
act = & term . post ( ) ;
} // NOTE: generated Activity chain remains valid after term goes out of scope
// Values reported for the BlockFlow allocator look sane...
CHECK ( watch ( bFlow ) . cntElm ( ) = = 7 ) ; // POST, GATE, WORKSTART, INVOKE, FEED, FEED, WORKSTOP
CHECK ( watch ( bFlow ) . cntEpochs ( ) = = 1 ) ; // all placed into a single epoch...
CHECK ( watch ( bFlow ) . find ( * act ) > dead ) ; // which terminates shortly after the given deadline
CHECK ( watch ( bFlow ) . find ( * act ) < dead + Time ( 500 , 0 ) ) ;
2023-08-29 02:18:07 +02:00
2023-08-29 16:40:52 +02:00
// Time window parameters have been included
2023-09-01 17:39:55 +02:00
CHECK ( act - > is ( Activity : : POST ) ) ;
2023-08-29 18:46:37 +02:00
CHECK ( start = = act - > data_ . timeWindow . life ) ;
CHECK ( dead = = act - > data_ . timeWindow . dead ) ;
// sane wiring, leading to an INVOCATE eventually
2023-09-01 17:39:55 +02:00
while ( not act - > is ( Activity : : INVOKE ) )
2023-08-29 18:46:37 +02:00
act = act - > next ;
2023-08-29 19:10:24 +02:00
2023-09-01 17:39:55 +02:00
CHECK ( act - > is ( Activity : : INVOKE ) ) ;
2023-08-29 18:46:37 +02:00
CHECK ( watch ( bFlow ) . find ( * act ) ! = Time : : NEVER ) ; // can also be found within the BlockFlow allocator
2023-08-29 19:10:24 +02:00
// this invocation is properly defined and executable
Time now { 55 , 5 } ;
CHECK ( activity : : PASS = = act - > activate ( now , detector . executionCtx ) ) ;
CHECK ( detector . verifyInvocation ( " mockJob " ) ) ;
2023-07-13 01:51:21 +02:00
}
2023-08-30 22:19:57 +02:00
/** @test verify the ability to _dispatch and perform_ a chain of activities.
* - use a directly wired , arbitrary chain
* - dispatch will activate all Activities
2023-08-30 23:13:41 +02:00
* - however , when the Gate is configured to be blocked
* ( waiting on prerequisites ) , then the rest of the chain is not activated ,
* only a re - check of the Gate is scheduled for later ( 1.011 - > 2.011 )
* - the dispatch function also handles the notifications ;
* when a notification towards the Gate is dispatched , the Gate is
* decremented and thereby opened ; activation of the rest of the chain
* is then planned ( but not executed synchronously in the same call )
2023-08-30 22:19:57 +02:00
*/
void
dispatchChain ( )
{
Time tt { 11 , 1 } ;
Activity tick ;
Activity gate { 0 } ;
gate . next = & tick ;
Activity post { tt , & gate } ;
// so now we have POST ⟶ GATE ⟶ TICK;
ActivityDetector detector ;
2023-09-01 17:18:32 +02:00
detector . executionCtx . getSchedTime = [ & ] { return tt ; } ;
2023-08-30 22:19:57 +02:00
// insert instrumentation to trace activation
detector . watchGate ( post . next , " Gate " ) ;
2023-10-23 01:48:46 +02:00
CHECK ( activity : : PASS = = ActivityLang : : dispatchChain ( & post , detector . executionCtx ) ) ; // start execution (case/seq == 0)
2023-08-30 23:13:41 +02:00
CHECK ( detector . verifyInvocation ( " Gate " ) . arg ( " 1.011 ⧐ Act(GATE " ) // ...first the Gate was activated
. beforeInvocation ( " after-Gate " ) . arg ( " 1.011 ⧐ Act(TICK " ) // ...then activation passed out of Gate...
. beforeInvocation ( " CTX-tick " ) . arg ( " 1.011 " ) ) ; // ...and finally the TICK invoked the λ-tick
2023-08-30 22:19:57 +02:00
2023-08-30 23:13:41 +02:00
detector . incrementSeq ( ) ;
gate . data_ . condition . incDependencies ( ) ; // Gate is blocked
2023-10-23 01:48:46 +02:00
CHECK ( activity : : PASS = = ActivityLang : : dispatchChain ( & post , detector . executionCtx ) ) ; // start execution (case/seq == 1)
2023-08-30 23:13:41 +02:00
CHECK ( detector . verifyInvocation ( " Gate " ) . seq ( 1 ) . arg ( " 1.011 ⧐ Act(GATE " ) // ...the Gate was activated...
. beforeInvocation ( " CTX-post " ) . seq ( 1 ) . arg ( " 2.011 " , " Act(GATE " , " ≺test::CTX≻ " ) ) ; // ...but was found blocked and re-scheduled itself to 2.011
CHECK ( detector . ensureNoInvocation ( " after-Gate " ) . seq ( 1 ) // verify activation was not passed out behind Gate
. afterInvocation ( " Gate " ) . seq ( 1 ) ) ;
CHECK ( detector . ensureNoInvocation ( " CTX-tick " ) . seq ( 1 ) // verify also the λ-tick was not invoked this time
. afterInvocation ( " Gate " ) . seq ( 1 ) ) ;
detector . incrementSeq ( ) ;
Activity notify { post . next } ; // Notification via instrumented connection to the Gate
2023-10-23 01:48:46 +02:00
CHECK ( activity : : PASS = = ActivityLang : : dispatchChain ( & notify , detector . executionCtx ) ) ; // dispatch a notification (case/seq == 2)
2023-08-30 23:13:41 +02:00
CHECK ( detector . verifyInvocation ( " Gate " ) . seq ( 2 ) . arg ( " 1.011 --notify-↯> Act(GATE " ) // ...notification dispatched towards the Gate
. beforeInvocation ( " CTX-post " ) . seq ( 2 ) . arg ( " 1.011 " , " after-Gate " , " ≺test::CTX≻ " ) ) ; // ...this opened the Gate and posted/requested activation of the rest of the chain
CHECK ( detector . ensureNoInvocation ( " after-Gate " ) . seq ( 2 ) // verify that activation was not passed out directly
. afterInvocation ( " CTX-post " ) . seq ( 2 ) ) ;
CHECK ( detector . ensureNoInvocation ( " CTX-tick " ) . seq ( 2 ) // verify also the λ-tick was not invoked directly
. afterInvocation ( " CTX-post " ) . seq ( 2 ) ) ;
2023-08-30 22:19:57 +02:00
}
2023-09-01 17:18:32 +02:00
/** @test usage scenario: Activity graph for a simple render job
2023-09-02 22:36:02 +02:00
* - build an activity term based on the » CalculationJob « wiring template
2023-08-30 23:57:07 +02:00
* - dispatch the generated Activity chain and verify sequence of invocations
2023-07-13 01:51:21 +02:00
*/
void
scenario_RenderJob ( )
{
2023-08-30 23:57:07 +02:00
Time nominal { 7 , 7 } ;
Time start { 0 , 1 } ;
Time dead { 0 , 10 } ;
ActivityDetector detector ;
Job testJob { detector . buildMockJob ( " testJob " , nominal , 12345 ) } ;
2023-09-01 17:18:32 +02:00
TimeVar now = Time { 5 , 5 } ;
2023-09-01 23:51:15 +02:00
detector . executionCtx . getSchedTime = [ & ] { // increase "current" time on each access
2023-09-01 17:18:32 +02:00
now + = FSecs ( 1 , 20 ) ;
return now ;
} ;
2023-08-30 23:57:07 +02:00
BlockFlowAlloc bFlow ;
ActivityLang activityLang { bFlow } ;
2023-09-02 03:35:02 +02:00
// Build the Activity-Term for a simple calculation job...
Activity & anchor = activityLang . buildCalculationJob ( testJob , start , dead )
. post ( ) ; // retrieve the entrance point to the chain
2023-08-30 23:57:07 +02:00
// insert instrumentation to trace activation
detector . watchGate ( anchor . next , " theGate " ) ;
2023-10-23 01:48:46 +02:00
CHECK ( activity : : PASS = = ActivityLang : : dispatchChain ( & anchor , detector . executionCtx ) ) ;
2023-08-31 20:18:35 +02:00
2023-09-01 17:18:32 +02:00
CHECK ( detector . verifyInvocation ( " theGate " ) . arg ( " 5.105 ⧐ Act(GATE " )
2023-08-31 20:18:35 +02:00
. beforeInvocation ( " after-theGate " ) . arg ( " ⧐ Act(WORKSTART " )
2023-09-01 17:18:32 +02:00
. beforeInvocation ( " CTX-work " ) . arg ( " 5.155 " , " " )
2023-08-31 20:18:35 +02:00
. beforeInvocation ( " testJob " ) . arg ( " 7.007 " , 12345 )
2023-09-01 17:18:32 +02:00
. beforeInvocation ( " CTX-done " ) . arg ( " 5.355 " , " " ) ) ;
2023-08-31 20:18:35 +02:00
}
2023-09-01 23:51:15 +02:00
/** @test usage scenario: Notification from prerequisite Jobs within time window
2023-08-31 20:18:35 +02:00
* - build [ similar ] ( \ ref # scenario_RenderJob ) » CalculationJob « wiring
* - configure extended dependency notification capabilities
* - Case - 1 : a Notification decreases the latch , but blocks otherwise
* - Case - 2 : when the primary chain is activated after the Notification ,
* then the tail chain behind the Gate is dispatched
*/
void
scenario_Notification ( )
{
Time nominal { 7 , 7 } ;
Time start { 0 , 1 } ;
Time dead { 0 , 10 } ;
ActivityDetector detector ;
Job testJob { detector . buildMockJob ( " testJob " , nominal , 12345 ) } ;
BlockFlowAlloc bFlow ;
ActivityLang activityLang { bFlow } ;
2023-09-01 23:51:15 +02:00
// emulate a blocking prerequisite dependency
2023-08-31 20:18:35 +02:00
Activity trigger { Activity : : NOTIFY } ;
2023-09-01 23:51:15 +02:00
// Build the Activity-Term...
auto term = activityLang . buildCalculationJob ( testJob , start , dead )
. expectNotification ( trigger ) // ...require notification from prerequisite
. requireDirectActivation ( ) ; // ...additionally insert inhibition to avoid activation
// before the primary-chain has been scheduled
Activity & anchor = term . post ( ) ;
CHECK ( anchor . is ( Activity : : POST ) ) ;
2023-09-01 22:47:37 +02:00
CHECK ( anchor . next - > is ( Activity : : NOTIFY ) ) ;
2023-09-01 23:51:15 +02:00
CHECK ( anchor . next - > next - > is ( Activity : : GATE ) ) ;
CHECK ( anchor . next - > next - > next - > is ( Activity : : WORKSTART ) ) ;
CHECK ( anchor . next - > next - > next - > next - > is ( Activity : : INVOKE ) ) ;
CHECK ( anchor . next - > next - > next - > next - > next - > is ( Activity : : FEED ) ) ;
CHECK ( anchor . next - > next - > next - > next - > next - > next - > is ( Activity : : FEED ) ) ;
CHECK ( anchor . next - > next - > next - > next - > next - > next - > next - > is ( Activity : : WORKSTOP ) ) ;
CHECK ( anchor . next - > next - > next - > next - > next - > next - > next - > next = = nullptr ) ;
// insert test-instrumentation to trace activation
detector . watchGate ( anchor . next - > next , " theGate " ) ;
2023-09-01 19:23:27 +02:00
detector . insertActivationTap ( trigger . data_ . notification . target , " trigger " ) ;
2023-09-01 23:51:15 +02:00
detector . insertActivationTap ( anchor . next - > data_ . notification . target , " deBlock " ) ;
// rig the λ-post to forward dispatch as expected in real usage
2023-09-01 22:47:37 +02:00
detector . executionCtx . post . implementedAs (
2023-10-23 01:48:46 +02:00
[ & ] ( Time when , Activity * postedAct , auto & ctx )
2023-09-01 22:47:37 +02:00
{
2023-09-01 23:51:15 +02:00
if ( when = = ctx . getSchedTime ( ) ) // only for POST to run „right now“
2023-10-18 23:02:29 +02:00
return ActivityLang : : dispatchChain ( postedAct , ctx ) ;
2023-09-01 22:47:37 +02:00
else
return activity : : PASS ;
} ) ;
2023-09-01 19:23:27 +02:00
2023-09-01 23:51:15 +02:00
///// Case-1 : send a notification from prerequisite, but prior to activating primary-chain
2023-10-23 01:48:46 +02:00
CHECK ( activity : : PASS = = ActivityLang : : dispatchChain ( & trigger , detector . executionCtx ) ) ;
2023-09-01 23:51:15 +02:00
CHECK ( detector . verifyInvocation ( " trigger " ) . seq ( 0 ) . arg ( " 5.555 --notify-↯> Act(GATE " ) // notification dispatched to the Gate
. arg ( " <2, until 0:00:10.000 " ) ) ; // Note: the Gate-latch expects 2 notifications
CHECK ( detector . ensureNoInvocation ( " testJob " ) // ==> the latch was decremented but no invocation yet
. afterInvocation ( " trigger " ) ) ;
2023-09-01 19:23:27 +02:00
2023-09-01 23:51:15 +02:00
///// Case-2 : now activate the primary-chain
2023-09-01 19:23:27 +02:00
detector . incrementSeq ( ) ;
2023-10-23 01:48:46 +02:00
CHECK ( activity : : PASS = = ActivityLang : : dispatchChain ( & anchor , detector . executionCtx ) ) ;
2023-09-01 23:51:15 +02:00
CHECK ( detector . verifyInvocation ( " CTX-post " ) . seq ( 1 ) . arg ( " 5.555 " , " Act(NOTIFY " , " ≺test::CTX≻ " ) // immediately at begin, the internal self-notification is posted
. beforeInvocation ( " deBlock " ) . seq ( 1 ) . arg ( " 5.555 --notify-↯> Act(GATE " ) // ...and then dispatched recursively via ActivityLang::dispatchChain() towards the Gate
. arg ( " <1, until 0:00:10.000 " ) // Note: at this point, the Gate-latch expects 1 notifications
. beforeInvocation ( " CTX-post " ) . seq ( 1 ) . arg ( " 5.555 " , " after-theGate " , " ≺test::CTX≻ " ) // ==> the latch was decremented and the Gate OPENS, and thus post() the tail-chain
. beforeInvocation ( " after-theGate " ) . arg ( " 5.555 ⧐ Act(WORKSTART " ) // ...causing the activation to pass behind the Gate
. beforeInvocation ( " CTX-work " ) . seq ( 1 ) . arg ( " 5.555 " , " " ) // ...through WORKSTART
. beforeInvocation ( " testJob " ) . seq ( 1 ) . arg ( " 7.007 " , 12345 ) // ...then invoke the JobFunctor itself (with the nominal Time{7,7})
. beforeInvocation ( " CTX-done " ) . seq ( 1 ) . arg ( " 5.555 " , " " ) // ...and finally the WORKSTOP
. beforeInvocation ( " theGate " ) . seq ( 1 ) . arg ( " 5.555 ⧐ Act(GATE " ) // RETURN to the primary-chain activation (after the internal self-notification)
. arg ( " <0, until -85401592:56:01.825 " ) ) ; // -- however, after opening the Gate, the notification has blocked it permanently
CHECK ( detector . ensureNoInvocation ( " testJob " ) // ==> thus no further (redundant) activation of the JobFunctor
. afterInvocation ( " CTX-done " ) . seq ( 1 ) ) ;
2023-08-30 23:57:07 +02:00
2023-09-01 19:23:27 +02:00
detector . incrementSeq ( ) ;
2023-10-23 01:48:46 +02:00
CHECK ( activity : : PASS = = ActivityLang : : dispatchChain ( & trigger , detector . executionCtx ) ) ; // and any further external trigger is likewise blocked:
2023-09-01 23:51:15 +02:00
CHECK ( detector . verifyInvocation ( " trigger " ) . seq ( 2 ) . arg ( " 5.555 --notify-↯> Act(GATE " ) // ... it reaches the Gate
. arg ( " <0, until -85401592:56:01.825 " ) ) ; // ... but the Gate has been closed permanently (by setting the deadline to Time::MIN)
CHECK ( detector . ensureNoInvocation ( " testJob " ) // ==> no further invocation
. afterInvocation ( " trigger " ) . seq ( 2 ) ) ;
2023-09-02 22:36:02 +02:00
// cout << detector.showLog()<<endl; // HINT: use this for investigation...
2023-07-13 01:51:21 +02:00
}
2023-09-02 23:57:30 +02:00
/** @test usage scenario: Activity graph for an async Job
2023-09-02 22:36:02 +02:00
* - use a simple [ calculation job term ] ( \ ref # scenario_RenderJob ) as follow - up receiver
* - build an activity Term based on the » Async Load Job « wiring and link it to the receiver
* - also retrieve the Activity record used as re - entrance point after completing async IO
2023-07-13 01:51:21 +02:00
*/
void
scenario_IOJob ( )
{
2023-09-02 22:36:02 +02:00
Time nominal { 7 , 7 } ;
Time start { 0 , 1 } ;
Time dead { 0 , 10 } ;
ActivityDetector detector ;
Job loadJob { detector . buildMockJob ( " loadJob " , nominal , 12345 ) } ;
2023-09-02 23:57:30 +02:00
Job calcJob { detector . buildMockJob ( " calcJob " ) } ;
2023-09-02 22:36:02 +02:00
BlockFlowAlloc bFlow ;
ActivityLang activityLang { bFlow } ;
auto followup = activityLang . buildCalculationJob ( calcJob , start , dead ) ;
auto loadTerm = activityLang . buildAsyncLoadJob ( loadJob , start , dead )
. appendNotificationTo ( followup ) ;
Activity & anchor = loadTerm . post ( ) ;
Activity & notify = loadTerm . callback ( ) ;
2023-09-02 23:57:30 +02:00
CHECK ( anchor . is ( Activity : : POST ) ) ;
CHECK ( anchor . next - > is ( Activity : : WORKSTART ) ) ;
CHECK ( anchor . next - > next - > is ( Activity : : INVOKE ) ) ;
CHECK ( anchor . next - > next - > next - > is ( Activity : : FEED ) ) ;
CHECK ( anchor . next - > next - > next - > next - > is ( Activity : : FEED ) ) ;
CHECK ( anchor . next - > next - > next - > next - > next = = nullptr ) ; // Note: chain is severed here
2023-09-02 22:36:02 +02:00
2023-09-02 23:57:30 +02:00
CHECK ( notify . is ( Activity : : WORKSTOP ) ) ; // ...WORKSTOP will be emitted from callback
CHECK ( notify . next - > is ( Activity : : NOTIFY ) ) ; // ...followed by notification of dependent job(s)
CHECK ( notify . next - > next = = nullptr ) ;
2023-09-02 22:36:02 +02:00
2023-09-02 23:57:30 +02:00
CHECK ( notify . next - > data_ . notification . target = = followup . post ( ) . next ) ; // was wired to the GATE of the follow-up activity Term
CHECK ( followup . post ( ) . next - > is ( Activity : : GATE ) ) ;
///// Case-1 : trigger off the async IO job
2023-10-23 01:48:46 +02:00
CHECK ( activity : : PASS = = ActivityLang : : dispatchChain ( & anchor , detector . executionCtx ) ) ;
2023-09-02 23:57:30 +02:00
CHECK ( detector . verifyInvocation ( " CTX-work " ) . seq ( 0 ) . arg ( " 5.555 " , " " ) // activation of WORKSTART
. beforeInvocation ( " loadJob " ) . seq ( 0 ) . arg ( " 7.007 " , 12345 ) ) ; // activation of JobFunctor
CHECK ( detector . ensureNoInvocation ( " CTX-done " ) . seq ( 0 ) // IO operation just runs, no further activity yet
. afterInvocation ( " loadJob " ) . seq ( 0 ) ) ;
///// Case-2 : activate the rest of the chain after IO is done
detector . incrementSeq ( ) ;
2023-10-23 01:48:46 +02:00
CHECK ( activity : : PASS = = ActivityLang : : dispatchChain ( & notify , detector . executionCtx ) ) ;
2023-09-02 23:57:30 +02:00
CHECK ( detector . verifyInvocation ( " CTX-done " ) . seq ( 1 ) . arg ( " 5.555 " , " " ) // activation of WORKSTOP via callback
. beforeInvocation ( " CTX-post " ) . seq ( 1 ) . arg ( " 5.555 " , " Act(NOTIFY " , " ≺test::CTX≻ " ) ) ; // the following NOTIFY is posted...
2023-07-13 01:51:21 +02:00
}
2023-09-02 03:35:02 +02:00
/** @test usage scenario: Activity graph for administrative job
* - by default , neither Gate , nor start / stop notification used
* - rather , the ` INVOKE ` and the argument - ` FEED ` is posted directly
* @ remark the job itself is thus performed in » management mode «
* ( holding the ` GroomingToken ` ) , and may modify the queue
* to generate new jobs .
2023-07-13 01:51:21 +02:00
*/
void
scenario_MetaJob ( )
2023-07-03 18:40:37 +02:00
{
2023-09-02 03:35:02 +02:00
Time nominal { 7 , 7 } ;
Time start { 0 , 1 } ;
Time dead { 0 , 10 } ;
ActivityDetector detector ;
Job testJob { detector . buildMockJob ( " metaJob " , nominal , 12345 ) } ;
BlockFlowAlloc bFlow ;
ActivityLang activityLang { bFlow } ;
// Build Activity-Term with a chain defining a meta-job...
Activity & anchor = activityLang . buildMetaJob ( testJob , start , dead )
. post ( ) ;
CHECK ( anchor . is ( Activity : : POST ) ) ;
CHECK ( anchor . next - > is ( Activity : : INVOKE ) ) ;
CHECK ( anchor . next - > next - > is ( Activity : : FEED ) ) ;
CHECK ( anchor . next - > next - > next - > is ( Activity : : FEED ) ) ;
CHECK ( anchor . next - > next - > next - > next = = nullptr ) ;
// insert test-instrumentation
detector . insertActivationTap ( anchor . next ) ;
2023-10-23 01:48:46 +02:00
CHECK ( activity : : PASS = = ActivityLang : : dispatchChain ( & anchor , detector . executionCtx ) ) ;
2023-09-02 03:35:02 +02:00
CHECK ( detector . verifyInvocation ( " tap-INVOKE " ) . arg ( " 5.555 ⧐ Act(INVOKE " )
. beforeInvocation ( " metaJob " ) . arg ( " 7.007 " , 12345 ) ) ;
2023-07-03 18:40:37 +02:00
}
} ;
/** Register this test class... */
LAUNCHER ( SchedulerActivity_test , " unit engine " ) ;
2023-07-06 16:35:42 +02:00
} } } // namespace vault::gear::test