Lumiera  0.pre.03
»edit your freedom«
mock-dispatcher.hpp
Go to the documentation of this file.
1 /*
2  MOCK-DISPATCHER.hpp - test scaffolding to verify render job planning and dispatch
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 
41 #ifndef STEAM_ENGINE_TEST_MOCK_DISPATCHER_H
42 #define STEAM_ENGINE_TEST_MOCK_DISPATCHER_H
43 
44 
45 #include "lib/test/test-helper.hpp"
52 #include "vault/gear/job.h"
53 #include "vault/real-clock.hpp"
54 #include "lib/allocator-handle.hpp"
55 #include "lib/time/timevalue.hpp"
56 #include "lib/diff/gen-node.hpp"
57 #include "lib/linked-elements.hpp"
58 #include "lib/itertools.hpp"
59 #include "lib/depend.hpp"
60 #include "lib/util.hpp"
61 
62 #include <tuple>
63 #include <map>
64 
65 
66 namespace steam {
67 namespace engine {
68 namespace test {
69 
70  using std::make_tuple;
71  using lib::diff::GenNode;
72  using lib::diff::MakeRec;
74  using lib::time::Time;
75  using lib::HashVal;
76  using lib::ranHash;
77  using lib::rani;
78  using util::isnil;
79  using util::isSameObject;
80  using fixture::Segmentation;
81  using vault::RealClock;
82  using vault::gear::Job;
84 
85 
86 
87 
88 
94  class MockJob
95  : public Job
96  {
97  static Job build();
98  static Job build (Time nominalTime, int additionalKey);
99 
100  public:
101  MockJob()
102  : Job{build()}
103  { }
104 
105  MockJob (Time nominalTime, int additionalKey)
106  : Job{build (nominalTime,additionalKey)}
107  { }
108 
109 
110  static bool was_invoked (Job const& job);
111  static Time invocationTime (Job const& job);
112  static Time invocationNominalTime (Job const& job);
113  static int invocationAdditionalKey (Job const& job);
114 
115  static bool isNopJob (Job const&);
116  static JobClosure& getFunctor();
117  };
118 
119 
120 
121 
122 
123 
137  : private lib::AllocatorHandle<JobTicket>
138  , public JobTicket
139  {
140  auto&
141  allocator()
142  {
143  return static_cast<lib::AllocatorHandle<JobTicket>&> (*this);
144  }
145 
147  static ExitNode
149  {
150  return ExitNode{seed, DUMMY_JOB_RUNTIME
151  ,ExitNodes{}
152  ,& MockJob::getFunctor()};
153  }
154 
155  public:
156  MockJobTicket()
157  : JobTicket{defineSimpleSpec(), allocator()}
158  { }
159 
160  MockJobTicket (HashVal seed)
161  : JobTicket{defineSimpleSpec (seed), allocator()}
162  { }
163 
164 
165  /* ===== Diagnostics ===== */
166 
167  bool verify_associated (Job const&) const;
168  static bool isAssociated (Job const&, JobTicket const&);
169  };
170 
171 
172 
194  : public Segmentation
195  {
196 
197  public:
199  : Segmentation{}
200  { }
201 
202  MockSegmentation (std::initializer_list<GenNode> specs)
203  : MockSegmentation{}
204  {
205  for (auto& spec : specs)
206  {
207  auto start = spec.retrieveAttribute<Time> ("start");
208  auto after = spec.retrieveAttribute<Time> ("after");
209  Segmentation::splitSplice (start, after
210  ,ExitNodes{buildExitNodeFromSpec (spec)}
211  );
212  }
213  }
214 
215 
216  ExitNode
217  buildExitNodeFromSpec (GenNode const& spec)
218  {
219  return ExitNode{buildSeed (spec)
220  ,buildRuntime (spec)
221  ,buildPrerequisites (spec)
222  ,& MockJob::getFunctor()};
223  }
224 
226  void duplicateExitNodeSpec (uint times);
227 
228 
229  private: /* ======== Implementation: build fake ExitNodes from test specification ==== */
230 
231  HashVal
232  buildSeed (GenNode const& spec)
233  {
234  auto seed = spec.retrieveAttribute<int> ("mark");
235  return seed? HashVal(*seed) : HashVal(1 +rani(1000));
236  }
237 
238  Duration
239  buildRuntime (GenNode const& spec)
240  {
241  auto runtime = spec.retrieveAttribute<Duration> ("runtime");
242  return runtime? *runtime : DUMMY_JOB_RUNTIME;
243  }
244 
245  ExitNodes
246  buildPrerequisites (GenNode const& spec)
247  {
248  ExitNodes prerequisites;
249  for (auto& child : spec.getChildren())
250  prerequisites.emplace_back (
251  buildExitNodeFromSpec (child));
252  return prerequisites;
253  }
254  };
255 
256 
257 
258 
266  inline void
268  {
269  using Spec = fixture::NodeGraphAttachment;
270 
271  Segmentation::adaptSpecification ([times](Spec const& spec)
272  {
273  return Spec{ExitNodes(times, spec[0])};
274  }); // vector with <times> copies of spec[0]
275  } // (Warning: use parens, not braces for this ctor...)
276 
277 
283  inline bool
285  {
286  JobFunctor& functor = dynamic_cast<JobFunctor&> (static_cast<JobClosure&> (*job.jobClosure));
287  Time nominalTime {TimeValue{job.parameter.nominalTime}};
288  InvocationInstanceID const& invoKey = job.parameter.invoKey;
289  return this->isValid()
290  and this->verifyInstance(functor, invoKey, nominalTime);
291  }
292 
299  inline bool
300  MockJobTicket::isAssociated (Job const& job, JobTicket const& ticket)
301  { // should work always, since storage is the same
302  MockJobTicket const& backdoor = static_cast<MockJobTicket const&> (ticket);
303  return backdoor.verify_associated (job);
304  }
305 
306 
307 
308  namespace { // used internally by MockDispatcher....
311 
313  }
314 
315 
316 
331  : public Dispatcher
332  {
333 
334  DummyPlaybackSetup dummySetup_;
335  MockSegmentation mockSeg_;
336 
337  using PortIdxMap = std::map<ModelPort, size_t>;
338 
339  const PortIdxMap portIdx_;
340 
341  public:
342  /* == mock implementation of the Dispatcher interface == */
343 
344  size_t
345  resolveModelPort (ModelPort modelPort) override
346  {
347  auto entry = portIdx_.find(modelPort);
348  if (entry == portIdx_.end())
349  throw error::Logic{"Invalid ModelPort for this Dispatcher"};
350  else
351  return entry->second;
352  }
353 
354 
355  JobTicket&
356  getJobTicketFor (size_t portIDX, TimeValue nominalTime) override
357  {
358  auto& seg = mockSeg_[nominalTime];
359  return seg.jobTicket(portIDX);
360  }
361 
362 
363  public:
365  : dummySetup_{}
366  , mockSeg_{MakeRec().genNode()} // Node: generate a single active Segment to cover all
367  , portIdx_{buildPortIndex()}
368  {
369  mockSeg_.duplicateExitNodeSpec(portIdx_.size());
370  }
371 
372  MockDispatcher (std::initializer_list<GenNode> specs)
373  : dummySetup_{}
374  , mockSeg_(specs)
375  , portIdx_{buildPortIndex()}
376  {
377  mockSeg_.duplicateExitNodeSpec(portIdx_.size());
378  }
379 
380 
381  ModelPort
382  provideMockModelPort()
383  {
384  ModelPorts mockModelPorts = dummySetup_.getAllModelPorts();
385  return *mockModelPorts; // using just the first dummy port
386  }
387 
397  play::test::DummyOutputLink
398  getDummyConnection(uint index)
399  {
400  return dummySetup_.getModelPort (index);
401  }
402 
406  bool verify(Job const& job, ModelPort const& port, play::DataSink const& sink)
407  {
408  if (not dummySetup_.isSupported (port, sink)) return false;
409 
410  TimeValue nominalTime{job.parameter.nominalTime};
411  size_t portIDX = resolveModelPort (port);
412  JobTicket& ticket = getJobTicketFor (portIDX, nominalTime);
413  return isnil (ticket)? MockJob::isNopJob (job)
414  : MockJobTicket::isAssociated (job, ticket);
415  }
416 
417  private:
418  PortIdxMap
419  buildPortIndex()
420  {
421  PortIdxMap newIndex;
422  uint i{0};
423  for (auto it=dummySetup_.getAllModelPorts()
424  ; bool{it}
425  ; ++it, ++i
426  )
427  newIndex[*it] = i;
428 
429  return newIndex;
430  }
431  };
432 
433 
434 }}} // namespace steam::engine::test
435 #endif /*STEAM_ENGINE_TEST_MOCK_DISPATCHER_H*/
Organising the output data calculation possibilities.
size_t resolveModelPort(ModelPort modelPort) override
translate a generic ModelPort spec into the specific index number applicable at the Timeline referred...
Execution plan to generate render jobs within a specific render process.
static JobClosure & getFunctor()
JobTicket & getJobTicketFor(size_t portIDX, TimeValue nominalTime) override
Core Dispatcher operation: locate the appropriate Segment and retrieve/derive a »blueprint« for rende...
Mock setup for a JobTicket to generate dummy render Job invocations.
Framework for dummy playback and rendering.
For the purpose of building and rendering, the fixture (for each timeline) is partitioned such that e...
AnyPair entry(Query< TY > const &query, typename WrapReturn< TY >::Wrapper &obj)
helper to simplify creating mock table entries, wrapped correctly
Definition: run.hpp:40
bool verify_associated(Job const &) const
verify the given job instance was actually generated from this JobTicket.
Service abstraction within the render engine for generating render jobs.
A mocked frame Dispatcher setup without any backing model.
void duplicateExitNodeSpec(uint times)
This is some trickery to allow handling of multiple ModelPort(s) in MockDispatcher; actually the code...
A front-end/concept to allow access to custom memory management.
int rani(uint bound=_iBOUND())
Definition: random.hpp:135
Internal abstraction: a service within the engine for translating a logical calculation stream (corre...
Definition: dispatcher.hpp:81
Segment const & splitSplice(OptTime start, OptTime after, engine::ExitNodes &&modelLink=ExitNodes{})
rework the existing Segmentation to include a new Segment as specified
Mock setup for a render Job with NO action but built-in diagnostics.
denotes an opened connection ready to receive media data for output.
lib::HashVal ranHash()
Definition: random.hpp:155
play::test::DummyOutputLink getDummyConnection(uint index)
The faked builder/playback setup provides some preconfigured ModelPort and corresponding DataSink han...
Steam-Layer implementation namespace root.
Placeholder implementation for a custom allocator.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:299
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:190
static ExitNode defineSimpleSpec(HashVal seed=ranHash())
provide a test specification wired to MockJob
Dummy and test setup of playback and rendering, omitting most of the Lumiera engine.
A top-level point in the render node network where data generation can be driven. ...
Definition: exit-node.hpp:63
bool verify(Job const &job, ModelPort const &port, play::DataSink const &sink)
Test support: verify the given Job is consistent with this Dispatcher.
Tiny helper functions and shortcuts to be used everywhere Consider this header to be effectively incl...
Generic building block for tree shaped (meta)data structures.
Binding and access point from a given Segment to access the actual render nodes.
Mock setup for a complete Segmentation to emulate the structure of the actual fixture, without the need of building a low-level Model.
A collection of frequently used helper functions to support unit testing.
static Job build()
uses random job definition values
Intrusive single linked list with optional ownership.
std::optional< X > retrieveAttribute(string key) const
mismatch tolerant convenience shortcut to peek into the attributes of a nested Record ...
Definition: gen-node.hpp:800
void adaptSpecification(std::function< NodeGraphAttachment(NodeGraphAttachment const &)> rewrite)
Definition of a render job.
Singleton services and Dependency Injection.
static bool isAssociated(Job const &, JobTicket const &)
convenience shortcut to perform this test on arbitrary JobTicket and Job instances.
Handle designating a point within the model, where actually output data can be pulled.
Definition: model-port.hpp:95
Link from the Fixture datastructure into the render node network.
opaque ID attached to each individual job invocation.
Definition: job.h:103
size_t HashVal
a STL compatible hash value
Definition: hash-value.h:52
static bool isNopJob(Job const &)
Interface of the closure for frame rendering jobs.
Definition: job.h:235
Duration is the internal Lumiera time metric.
Definition: timevalue.hpp:468
Helpers for working with iterators based on the pipeline model.
Part of the Fixture datastructure to manage time segments of constant structure.
Convenience frontend to access the current raw system time.
Definition: real-clock.hpp:45
Individual frame rendering task, forwarding to a closure.
Definition: job.h:268
a family of time value like entities and their relationships.
Front-end for simplified access to the current wall clock time.
basic constant internal time value.
Definition: timevalue.hpp:133
generic data element node within a tree
Definition: gen-node.hpp:222
execution plan for pulling a specific exit node.
Definition: job-ticket.hpp:78
bool isSameObject(A const &a, B const &b)
compare plain object identity, based directly on the referee&#39;s memory identities. ...
Definition: util.hpp:421