Lumiera 0.pre.04
»edit your freedom«
Loading...
Searching...
No Matches
activity-detector.hpp
Go to the documentation of this file.
1/*
2 ACTIVITY-DETECTOR.hpp - test scaffolding to observe activities within the scheduler
3
4 Copyright (C)
5 2023, Hermann Vosseler <Ichthyostega@web.de>
6
7  **Lumiera** is free software; you can redistribute it and/or modify it
8  under the terms of the GNU General Public License as published by the
9  Free Software Foundation; either version 2 of the License, or (at your
10  option) any later version. See the file COPYING for further details.
11
12*/
13
61#ifndef VAULT_GEAR_TEST_ACTIVITY_DETECTOR_H
62#define VAULT_GEAR_TEST_ACTIVITY_DETECTOR_H
63
64
65#include "vault/common.hpp"
68
69#include "vault/gear/job.h"
75#include "lib/meta/function.hpp"
76#include "lib/item-wrapper.hpp"
77#include "lib/format-util.hpp"
78#include "lib/random.hpp"
79#include "lib/util.hpp"
80
81#include <functional>
82#include <utility>
83#include <string>
84#include <deque>
85
86
87namespace vault{
88namespace gear {
89namespace test {
90
91 using std::string;
92 using std::function;
94 using lib::time::Time;
95 using lib::time::FSecs;
98 using util::unConst;
99 using util::isnil;
100 using lib::rani;
101 using std::forward;
102 using std::move;
103
104
105 namespace {// Diagnostic markers
106 const string MARK_INC{"IncSeq"};
107 const string MARK_SEQ{"Seq"};
108
109 using SIG_JobDiagnostic = void(Time, int32_t);
110 const size_t JOB_ARG_POS_TIME = 0;
111
112 const string CTX_POST{"CTX-post"};
113 const string CTX_WORK{"CTX-work"};
114 const string CTX_DONE{"CTX-done"};
115 const string CTX_TICK{"CTX-tick"};
116
118 }
119
120 class ActivityDetector;
121
122
130 : private lib::test::EventMatch
131 {
133
135 : _Parent{move (matcher)}
136 { }
137
138 friend class ActivityDetector;
139
140 public:
141 // standard copy acceptable
142
147 operator bool() const { return _Parent::operator bool(); }
148
149
150 /* query builder(s) to find a match stepping forwards */
151 ActivityMatch& beforeInvocation (string match) { return delegate (&EventMatch::beforeCall, move(match)); }
152 // more here...
153
154 /* query builders to find a match stepping backwards */
155 ActivityMatch& afterInvocation (string match) { return delegate (&EventMatch::afterCall, move(match)); }
156 // more here...
157
158
160 template<typename...ARGS>
162 arg (ARGS const& ...args)
163 {
164 return delegate (&EventMatch::arg<ARGS...>, args...);
165 }
166
169 seq (uint seqNr)
170 {
171 _Parent::attrib (MARK_SEQ, util::toString (seqNr));
172 return *this;
173 }
174
178 {
179 _Parent::beforeEvent(MARK_INC, util::toString(seqNr));
180 return *this;
181 }
184 {
185 _Parent::afterEvent(MARK_INC, util::toString(seqNr));
186 return *this;
187 }
188
192 {
193 return delegate (&EventMatch::argPos<Time const&>, size_t(JOB_ARG_POS_TIME), time);
194 }
195
196
197 private:
203 template<typename...ARGS>
205 delegate (_Parent& (_Parent::*fun) (ARGS...), ARGS&& ...args)
206 {
207 return static_cast<ActivityMatch&> (
208 (this->*fun) (forward<ARGS> (args)...));
209 }
210 };
211
212
213
223 {
225
228
232 template<typename RET, typename...ARGS>
234 {
236 using ImplFun = std::function<RET(ARGS...)>;
237
238 string id_;
240 uint const* seqNr_;
243
244 public:
245 DiagnosticFun (string id, EventLog& masterLog, uint const& invocationSeqNr)
246 : id_{id}
247 , log_{&masterLog}
248 , seqNr_{&invocationSeqNr}
249 , implFun_{}
250 , retVal_{}
251 {
253 }
254
256 template<typename VAL>
258 returning (VAL&& riggedResponse)
259 {
260 retVal_ = std::forward<VAL> (riggedResponse);
261 return std::move (*this);
262 }
263
265 template<class FUN>
267 implementedAs (FUN&& customImpl)
268 {
269 implFun_ = std::forward<FUN> (customImpl);
270 return std::move (*this);
271 }
272
273 // default copyable
274
276 RET
277 operator() (ARGS ...args) const
278 {
279 log_->call (log_->getID(), id_, args...)
280 .addAttrib (MARK_SEQ, util::toString(*seqNr_));
281 return implFun_? implFun_(std::forward<ARGS>(args)...)
282 : *retVal_;
283 }
284
285 operator string() const
286 {
287 return log_->getID()+"."+id_;
288 }
289 };
290
292 template<typename SIG>
301
302 using Logger = _DiagnosticFun<void(string)>::Type;
303
304
309 : public NopJobFunctor
310 {
312
314
318 void
320 {
321 mockOperation_(Time{TimeValue{param.nominalTime}}, param.invoKey.part.a);
322 }
323
324 string diagnostic() const override
325 {
326 return "JobFun-"+string{mockOperation_};
327 }
328
329 JobKind
331 {
332 return TEST_JOB;
333 }
334
335 public:
336 MockJobFunctor (MockOp mockedJobOperation)
337 : mockOperation_{move (mockedJobOperation)}
338 { }
339 };
340
341
346 : public Activity
347 , public activity::Hook
348 {
351
352 Activity*
354 {
355 return reinterpret_cast<Activity*> (data_.callback.arg);
356 }
357
358 Activity const*
359 target() const
360 {
361 return unConst(this)->target();
362 }
363
366 , Time now
367 , void* executionCtx) override
368 {
369 REQUIRE (thisHook.is (Activity::HOOK));
370 invoked_ = now;
371 if (not target())
372 {// no adapted target; just record this activation
373 log_(util::toString(now) + " ⧐ ");
374 return activity::PASS;
375 }
376 else
377 {// forward activation to the adapted target Activity
378 auto ctx = *static_cast<FakeExecutionCtx*> (executionCtx);
379 log_(util::toString(now) + " ⧐ " + util::toString (*target()));
380 return target()->activate (now, ctx);
381 }
382 }
383
385 notify ( Activity& thisHook
386 , Time now
387 , void* executionCtx) override
388 {
389 REQUIRE (thisHook.is (Activity::HOOK));
390 invoked_ = now;
391 if (not target())
392 {// no adapted target; just record this notification
393 log_(util::toString(now) + " --notify-↯• ");
394 return activity::PASS;
395 }
396 else
397 {// forward notification-dispatch to the adapted target Activity
398 auto ctx = *static_cast<FakeExecutionCtx*> (executionCtx);
399 log_(util::toString(now) + " --notify-↯> " + util::toString (*target()));
400 return target()->dispatch (now, ctx);
401 }
402 }
403
404 Time
405 getDeadline() const override
406 {
407 if (target() and target()->is(Activity::GATE))
408 return target()->data_.condition.getDeadline();
409 else
410 return Time::NEVER;
411 }
412
413 std::string
414 diagnostic() const override
415 {
416 return "Probe("+string{log_}+")";
417 }
418
419 public:
420 ActivityProbe (string id, EventLog& masterLog, uint const& invocationSeqNr)
421 : Activity{*this, 0}
422 , log_{id, masterLog, invocationSeqNr}
423 { }
424
425 ActivityProbe (Activity const& subject, string id, EventLog& masterLog, uint const& invocationSeqNr)
426 : Activity{*this, reinterpret_cast<size_t> (&subject)}
427 , log_{id, masterLog, invocationSeqNr}
428 {
429 next = subject.next;
430 }
431
432 operator string() const
433 {
434 return diagnostic();
435 }
436
437
438 static Time
440 {
441 if (act and act->verb_ == HOOK)
442 {
443 ActivityProbe* probe = dynamic_cast<ActivityProbe*> (act->data_.callback.hook);
444 if (probe)
445 return probe->invoked_;
446 }
447 return Time::NEVER;
448 }
449 };
450
451
452 /* ===== Maintain throw-away mock instances ===== */
453
454 std::deque<MockJobFunctor> mockOps_{};
455 std::deque<ActivityProbe> mockActs_{};
456
457
458 public:
459 ActivityDetector(string id ="")
460 : eventLog_{"ActivityDetector" + (isnil (id)? string{}: "("+id+")")}
461 , invocationSeq_{0}
462 { }
463
464 operator string() const
465 {
466 return util::join (eventLog_);
467 }
468
469 string
470 showLog() const
471 {
472 return "\n____Event-Log___________________________\n"
473 + util::join (eventLog_, "\n")
474 + "\n────╼━━━━━━━━╾──────────────────────────"
475 ;
476 }
477
478 void
479 clear(string newID)
480 {
481 if (isnil (newID))
483 else
484 eventLog_.clear (newID);
485 }
486
488 uint
490 {
493 return invocationSeq_;
494 }
495
496 uint
497 currSeq() const
498 {
499 return invocationSeq_;
500 }
501
502
509 template<typename SIG>
510 auto
512 {
513 using Functor = _DiagnosticFun<SIG>::Type;
514 return Functor{id, eventLog_, invocationSeq_};
515 }
516
517 JobClosure&
519 {
520 return mockOps_.emplace_back (
521 buildDiagnosticFun<SIG_JobDiagnostic> (id));
522 }
523
524 Job
525 buildMockJob (string id =""
526 ,Time nominal = lib::test::randTime()
527 ,size_t extra = rani())
528 {
529 InvocationInstanceID invoKey;
530 invoKey.part.a = extra;
531 invoKey.part.t = _raw(nominal);
532 return Job{buildMockJobFunctor (isnil(id)? "mockJob-"+util::toString(nominal) : id)
533 ,invoKey
534 ,nominal};
535 }
536
538 Activity&
540 {
541 return mockActs_.emplace_back (id, eventLog_, invocationSeq_);
542 }
543
545 Activity&
546 buildActivationTap (Activity const& subject, string id ="")
547 {
548 return mockActs_.emplace_back (subject
549 ,isnil(id)? "tap-"+subject.showVerb()+util::showAdr(subject)
550 : id
551 ,eventLog_
553 }
554
556 Activity&
557 insertActivationTap (Activity*& wiring, string id ="")
558 {
559 wiring = wiring? & buildActivationTap (*wiring, id)
560 : & buildActivationProbe (isnil(id)? "tail-"+util::showAdr(&wiring) : id);
561 return *wiring;
562 }
563
564 Activity&
565 buildGateWatcher (Activity& gate, string id ="")
566 {
567 insertActivationTap (gate.next, "after-" + (isnil(id)? gate.showVerb()+util::showAdr(gate) : id));
568 return buildActivationTap (gate, id);
569 }
570
571 Activity&
572 watchGate (Activity*& wiring, string id ="")
573 {
574 wiring = wiring? & buildGateWatcher (*wiring, id)
575 : & buildActivationProbe (isnil(id)? "tail-"+util::showAdr(&wiring) : id);
576 return *wiring;
577 }
578
579
580 Time invokeTime (Activity const* hook) { return ActivityProbe::lastInvoked (hook); }
581 bool wasInvoked (Activity const* hook) { return invokeTime(hook).isRegular(); }
582 Time invokeTime (Activity const& hook) { return invokeTime (&hook); }
583 bool wasInvoked (Activity const& hook) { return wasInvoked (&hook); }
584
585
586 struct FakeExecutionCtx;
588 using SIG_work = void(Time, size_t);
589 using SIG_done = void(Time, size_t);
591
600 {
605
606 function<Time()> getSchedTime = [this]{ return SCHED_TIME_MARKER;};
607
609 : post{detector.buildDiagnosticFun<SIG_post>(CTX_POST).returning(activity::PASS)}
610 , work{detector.buildDiagnosticFun<SIG_work>(CTX_WORK)}
611 , done{detector.buildDiagnosticFun<SIG_done>(CTX_DONE)}
612 , tick{detector.buildDiagnosticFun<SIG_tick>(CTX_TICK).returning(activity::PASS)}
613 { }
614
615 operator string() const { return "≺test::CTX≻"; }
616 };
617
619
620
621
623 verifyInvocation (string fun)
624 {
625 return ActivityMatch{move (eventLog_.verifyCall(fun))};
626 }
627
630 {
631 return ActivityMatch{move (eventLog_.ensureNot(fun).locateCall(fun))};
632 }
633
636 {
637 return ActivityMatch{move (eventLog_.verifyEvent(MARK_INC, util::toString(seqNr)))};
638 }
639
640
641 private:
642 };
643
644
645}}} // namespace vault::gear::test
646#endif /*VAULT_GEAR_TEST_ACTIVITY_DETECTOR_H*/
Descriptor for a piece of operational logic performed by the scheduler.
Helper to log and verify the occurrence of events.
EventLog & addAttrib(string const &key, X &&initialiser, ARGS &&...args)
Qualify the latest entry: set further attribute(s)
string getID() const
EventLog & event(string text)
log some text as event
EventLog & clear()
purge log contents while retaining just the original Header-ID
EventMatch verifyCall(string match) const
start a query to match especially a function call
EventMatch verifyEvent(string match) const
start a query to match for some event.
EventMatch ensureNot(string match) const
start a query to ensure the given expression does not match.
EventLog & call(string target, string function)
Log occurrence of a function call with no arguments.
EventMatch & beforeEvent(string match)
find a match for an "event" after the current point of reference
EventMatch & locateCall(string match)
basic search for some specific function invocation
EventMatch & afterEvent(string match)
EventMatch & attrib(string key, string valueMatch)
refine filter to additionally match on a specific attribute
Offset measures a distance in time.
basic constant internal time value.
bool isRegular() const
a mutable time value, behaving like a plain number, allowing copy and re-accessing
Lumiera's internal time value datatype.
static const Time NEVER
border condition marker value. NEVER >= any time value
static const Time ANYTIME
border condition marker value. ANYTIME <= any time value
ItemWrapper & defaultInit()
implant a default-initialised instance of the payload type
Any copy and copy construction prohibited.
Definition nocopy.hpp:38
Record to describe an Activity, to happen within the Scheduler's control flow.
Definition activity.hpp:227
bool is(Activity::Verb expectedVerb) const
Definition activity.hpp:422
activity::Proc activate(Time now, EXE &executionCtx)
Core Operation: Activate and perform this Activity.
Definition activity.hpp:626
Activity * next
Activities are organised into chains to represent relations based on verbs.
Definition activity.hpp:249
activity::Proc dispatch(Time now, EXE &executionCtx)
Entrance point for an activation, which has been dispatched indirectly through the dispatch and/or pr...
Definition activity.hpp:676
std::string showVerb() const
@ HOOK
invoke an extension point through the activity::Hook interface
Definition activity.hpp:239
@ GATE
probe window + count-down; activate next Activity, else re-schedule
Definition activity.hpp:236
Interface of the closure for frame rendering jobs.
Definition job.h:244
Individual frame rendering task, forwarding to a closure.
Definition job.h:276
Stub/Test implementation of the JobFunctor interface for a render job to do nothing at all
Extension point to invoke a callback from Activity activation.
Definition activity.hpp:154
operator std::string() const
Definition activity.hpp:188
A rigged CALLBACK-Activity to watch passing of activations.
ActivityProbe(Activity const &subject, string id, EventLog &masterLog, uint const &invocationSeqNr)
activity::Proc activation(Activity &thisHook, Time now, void *executionCtx) override
Callback on activation of the corresponding HOOK-Activity.
activity::Proc notify(Activity &thisHook, Time now, void *executionCtx) override
Callback when dispatching a NOTIFY-Activity to thisHook.
ActivityProbe(string id, EventLog &masterLog, uint const &invocationSeqNr)
A Mock functor, logging all invocations into the EventLog.
DiagnosticFun && implementedAs(FUN &&customImpl)
use the given λ to provide (optional) implementation logic
DiagnosticFun(string id, EventLog &masterLog, uint const &invocationSeqNr)
DiagnosticFun && returning(VAL &&riggedResponse)
prepare a response value to return from the mock invocation
RET operator()(ARGS ...args) const
mock function call operator: logs all invocations
A Mocked job operation to detect any actual invocation.
void invokeJobOperation(JobParameter param) override
rigged diagnostic implementation of job invocation
_DiagnosticFun< SIG_JobDiagnostic >::Type MockOp
Diagnostic context to record and evaluate activations within the Scheduler.
bool wasInvoked(Activity const &hook)
ActivityMatch verifySeqIncrement(uint seqNr)
bool wasInvoked(Activity const *hook)
RebindVariadic< DiagnosticFun, SigTypes >::Type Type
auto buildDiagnosticFun(string id)
Generic testing helper: build a λ-mock, logging all invocations.
std::deque< MockJobFunctor > mockOps_
_DiagnosticFun< void(string)>::Type Logger
JobClosure & buildMockJobFunctor(string id)
ActivityMatch ensureNoInvocation(string fun)
Activity & buildActivationProbe(string id)
build a rigged HOOK-Activity to record each invocation
Time invokeTime(Activity const &hook)
Activity & insertActivationTap(Activity *&wiring, string id="")
build ActivationProbe to record each activation before passing it to the subject
activity::Proc(Time, Time, Activity *, FakeExecutionCtx &) SIG_post
Time invokeTime(Activity const *hook)
Job buildMockJob(string id="", Time nominal=lib::test::randTime(), size_t extra=rani())
Activity & watchGate(Activity *&wiring, string id="")
Activity & buildGateWatcher(Activity &gate, string id="")
Activity & buildActivationTap(Activity const &subject, string id="")
build ActivationProbe to record each activation before passing it to the subject
ActivityMatch verifyInvocation(string fun)
std::deque< ActivityProbe > mockActs_
uint incrementSeq()
increment the internal invocation sequence number
ActivityMatch & arg(ARGS const &...args)
qualifier: additionally match the function arguments
ActivityMatch & afterSeqIncrement(uint seqNr)
ActivityMatch(lib::test::EventMatch &&matcher)
ActivityMatch & afterInvocation(string match)
ActivityMatch & beforeInvocation(string match)
ActivityMatch & delegate(_Parent &(_Parent::*fun)(ARGS...), ARGS &&...args)
ActivityMatch & beforeSeqIncrement(uint seqNr)
special query to match an increment of the sequence number
ActivityMatch & seq(uint seqNr)
qualifier: additionally require the indicated sequence number
ActivityMatch & timeArg(Time const &time)
qualifier: additionally match the nominal time argument of JobFunctor invocation
Support for verifying the occurrence of events from unit tests.
Collection of small helpers and convenience shortcuts for diagnostics & formatting.
Metaprogramming tools for detecting and transforming function types.
unsigned int uint
Definition integral.hpp:29
Adapter to store and hold an element of arbitrary type in local storage.
Definition of a render job.
JobKind
Definition job.h:64
@ TEST_JOB
test and diagnostic and research
Definition job.h:68
struct InvocationInstanceID::@62 part
opaque ID attached to each individual job invocation.
Definition job.h:105
const_LList llist_cmpfn void * extra
Definition llist.h:580
Helper: prepend a type to an existing type sequence, thus shifting all elements within the sequence t...
Metaprogramming helper to transfer variadic arguments.
boost::rational< int64_t > FSecs
rational representation of fractional seconds
int rani(uint bound=_iBOUND())
Definition random.hpp:135
Test runner and basic definitions for tests.
ostream & showAdr(ostream &stream, void const *addr)
preconfigured format for pretty-printing of addresses
std::string toString(TY const &val) noexcept
get some string representation of any object, reliably.
OBJ * unConst(const OBJ *)
shortcut to save some typing when having to define const and non-const variants of member functions
Definition util.hpp:358
string join(COLL &&coll, string const &delim=", ")
enumerate a collection's contents, separated by delimiter.
bool isnil(lib::time::Duration const &dur)
Proc
Result instruction from Activity activation.
Definition activity.hpp:140
@ PASS
pass on the activation down the chain
Definition activity.hpp:140
Time SCHED_TIME_MARKER
marker value for "current scheduler time" used in tests
lumiera_jobParameter const & JobParameter
Definition job.h:205
Vault-Layer implementation namespace root.
Generic implementation of a JobFunctor to perform no calculations.
Generating (pseudo) random numbers with controlled seed.
#define Type(_EXPR_)
Trait template for uniform access to function signature types.
Definition function.hpp:144
Mock setup of the execution context for Activity activation.
A collection of frequently used helper functions to support unit testing.
a family of time value like entities and their relationships.
Some basic facilities for manipulating type sequences.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Metaprogramming support to rebuild and rebind variadic templates.
Basic set of definitions and includes commonly used together (Vault).