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) Lumiera.org
5  2023, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 */
22 
50 #ifndef STEAM_ENGINE_TEST_MOCK_DISPATCHER_H
51 #define STEAM_ENGINE_TEST_MOCK_DISPATCHER_H
52 
53 
54 #include "lib/test/test-helper.hpp"
61 #include "vault/gear/job.h"
62 #include "vault/real-clock.hpp"
63 #include "lib/allocator-handle.hpp"
64 #include "lib/time/timevalue.hpp"
65 #include "lib/diff/gen-node.hpp"
66 #include "lib/linked-elements.hpp"
67 #include "lib/itertools.hpp"
68 #include "lib/depend.hpp"
69 #include "lib/util.hpp"
70 
71 #include <tuple>
72 #include <map>
73 
74 
75 namespace steam {
76 namespace engine {
77 namespace test {
78 
79  using std::make_tuple;
80  using lib::diff::GenNode;
81  using lib::diff::MakeRec;
83  using lib::time::Time;
84  using lib::HashVal;
85  using util::isnil;
86  using util::isSameObject;
87  using fixture::Segmentation;
88  using vault::RealClock;
89  using vault::gear::Job;
91 
92 
93 
94 
95 
101  class MockJob
102  : public Job
103  {
104  static Job build();
105  static Job build (Time nominalTime, int additionalKey);
106 
107  public:
108  MockJob()
109  : Job{build()}
110  { }
111 
112  MockJob (Time nominalTime, int additionalKey)
113  : Job{build (nominalTime,additionalKey)}
114  { }
115 
116 
117  static bool was_invoked (Job const& job);
118  static Time invocationTime (Job const& job);
119  static Time invocationNominalTime (Job const& job);
120  static int invocationAdditionalKey (Job const& job);
121 
122  static bool isNopJob (Job const&);
123  static JobClosure& getFunctor();
124  };
125 
126 
127 
128 
129 
130 
144  : private lib::AllocatorHandle<JobTicket>
145  , public JobTicket
146  {
147  auto&
148  allocator()
149  {
150  return static_cast<lib::AllocatorHandle<JobTicket>&> (*this);
151  }
152 
154  static ExitNode
155  defineSimpleSpec (HashVal seed = 1+rand())
156  {
157  return ExitNode{seed, DUMMY_JOB_RUNTIME
158  ,ExitNodes{}
159  ,& MockJob::getFunctor()};
160  }
161 
162  public:
163  MockJobTicket()
164  : JobTicket{defineSimpleSpec(), allocator()}
165  { }
166 
167  MockJobTicket (HashVal seed)
168  : JobTicket{defineSimpleSpec (seed), allocator()}
169  { }
170 
171 
172  /* ===== Diagnostics ===== */
173 
174  bool verify_associated (Job const&) const;
175  static bool isAssociated (Job const&, JobTicket const&);
176  };
177 
178 
179 
201  : public Segmentation
202  {
203 
204  public:
206  : Segmentation{}
207  { }
208 
209  MockSegmentation (std::initializer_list<GenNode> specs)
210  : MockSegmentation{}
211  {
212  for (auto& spec : specs)
213  {
214  auto start = spec.retrieveAttribute<Time> ("start");
215  auto after = spec.retrieveAttribute<Time> ("after");
216  Segmentation::splitSplice (start, after
217  ,ExitNodes{buildExitNodeFromSpec (spec)}
218  );
219  }
220  }
221 
222 
223  ExitNode
224  buildExitNodeFromSpec (GenNode const& spec)
225  {
226  return ExitNode{buildSeed (spec)
227  ,buildRuntime (spec)
228  ,buildPrerequisites (spec)
229  ,& MockJob::getFunctor()};
230  }
231 
233  void duplicateExitNodeSpec (uint times);
234 
235 
236  private: /* ======== Implementation: build fake ExitNodes from test specification ==== */
237 
238  HashVal
239  buildSeed (GenNode const& spec)
240  {
241  auto seed = spec.retrieveAttribute<int> ("mark");
242  return seed? HashVal(*seed) : HashVal(rand() % 1000);
243  }
244 
245  Duration
246  buildRuntime (GenNode const& spec)
247  {
248  auto runtime = spec.retrieveAttribute<Duration> ("runtime");
249  return runtime? *runtime : DUMMY_JOB_RUNTIME;
250  }
251 
252  ExitNodes
253  buildPrerequisites (GenNode const& spec)
254  {
255  ExitNodes prerequisites;
256  for (auto& child : spec.getChildren())
257  prerequisites.emplace_back (
258  buildExitNodeFromSpec (child));
259  return prerequisites;
260  }
261  };
262 
263 
264 
265 
273  inline void
275  {
276  using Spec = fixture::NodeGraphAttachment;
277 
278  Segmentation::adaptSpecification ([times](Spec const& spec)
279  {
280  return Spec{ExitNodes(times, spec[0])};
281  }); // vector with <times> copies of spec[0]
282  } // (Warning: use parens, not braces for this ctor...)
283 
284 
290  inline bool
292  {
293  JobFunctor& functor = dynamic_cast<JobFunctor&> (static_cast<JobClosure&> (*job.jobClosure));
294  Time nominalTime {TimeValue{job.parameter.nominalTime}};
295  InvocationInstanceID const& invoKey = job.parameter.invoKey;
296  return this->isValid()
297  and this->verifyInstance(functor, invoKey, nominalTime);
298  }
299 
306  inline bool
307  MockJobTicket::isAssociated (Job const& job, JobTicket const& ticket)
308  { // should work always, since storage is the same
309  MockJobTicket const& backdoor = static_cast<MockJobTicket const&> (ticket);
310  return backdoor.verify_associated (job);
311  }
312 
313 
314 
315  namespace { // used internally by MockDispatcher....
318 
320  }
321 
322 
323 
338  : public Dispatcher
339  {
340 
341  DummyPlaybackSetup dummySetup_;
342  MockSegmentation mockSeg_;
343 
344  using PortIdxMap = std::map<ModelPort, size_t>;
345 
346  const PortIdxMap portIdx_;
347 
348  public:
349  /* == mock implementation of the Dispatcher interface == */
350 
351  size_t
352  resolveModelPort (ModelPort modelPort) override
353  {
354  auto entry = portIdx_.find(modelPort);
355  if (entry == portIdx_.end())
356  throw error::Logic{"Invalid ModelPort for this Dispatcher"};
357  else
358  return entry->second;
359  }
360 
361 
362  JobTicket&
363  getJobTicketFor (size_t portIDX, TimeValue nominalTime) override
364  {
365  auto& seg = mockSeg_[nominalTime];
366  return seg.jobTicket(portIDX);
367  }
368 
369 
370  public:
372  : dummySetup_{}
373  , mockSeg_{MakeRec().genNode()} // Node: generate a single active Segment to cover all
374  , portIdx_{buildPortIndex()}
375  {
376  mockSeg_.duplicateExitNodeSpec(portIdx_.size());
377  }
378 
379  MockDispatcher (std::initializer_list<GenNode> specs)
380  : dummySetup_{}
381  , mockSeg_(specs)
382  , portIdx_{buildPortIndex()}
383  {
384  mockSeg_.duplicateExitNodeSpec(portIdx_.size());
385  }
386 
387 
388  ModelPort
389  provideMockModelPort()
390  {
391  ModelPorts mockModelPorts = dummySetup_.getAllModelPorts();
392  return *mockModelPorts; // using just the first dummy port
393  }
394 
404  play::test::DummyOutputLink
405  getDummyConnection(uint index)
406  {
407  return dummySetup_.getModelPort (index);
408  }
409 
413  bool verify(Job const& job, ModelPort const& port, play::DataSink const& sink)
414  {
415  if (not dummySetup_.isSupported (port, sink)) return false;
416 
417  TimeValue nominalTime{job.parameter.nominalTime};
418  size_t portIDX = resolveModelPort (port);
419  JobTicket& ticket = getJobTicketFor (portIDX, nominalTime);
420  return isnil (ticket)? MockJob::isNopJob (job)
421  : MockJobTicket::isAssociated (job, ticket);
422  }
423 
424  private:
425  PortIdxMap
426  buildPortIndex()
427  {
428  PortIdxMap newIndex;
429  uint i{0};
430  for (auto it=dummySetup_.getAllModelPorts()
431  ; bool{it}
432  ; ++it, ++i
433  )
434  newIndex[*it] = i;
435 
436  return newIndex;
437  }
438  };
439 
440 
441 }}} // namespace steam::engine::test
442 #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:49
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.
Internal abstraction: a service within the engine for translating a logical calculation stream (corre...
Definition: dispatcher.hpp:90
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.
play::test::DummyOutputLink getDummyConnection(uint index)
The faked builder/playback setup provides some preconfigured ModelPort and corresponding DataSink han...
Steam-Layer implementation namespace root.
static ExitNode defineSimpleSpec(HashVal seed=1+rand())
provide a test specification wired to MockJob
Placeholder implementation for a custom allocator.
Lumiera&#39;s internal time value datatype.
Definition: timevalue.hpp:308
Derived specific exceptions within Lumiera&#39;s exception hierarchy.
Definition: error.hpp:199
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:72
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:809
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:104
Link from the Fixture datastructure into the render node network.
opaque ID attached to each individual job invocation.
Definition: job.h:112
size_t HashVal
a STL compatible hash value
Definition: hash-value.h:56
static bool isNopJob(Job const &)
Interface of the closure for frame rendering jobs.
Definition: job.h:244
Duration is the internal Lumiera time metric.
Definition: timevalue.hpp:477
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:54
Individual frame rendering task, forwarding to a closure.
Definition: job.h:277
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:142
generic data element node within a tree
Definition: gen-node.hpp:231
execution plan for pulling a specific exit node.
Definition: job-ticket.hpp:87
bool isSameObject(A const &a, B const &b)
compare plain object identity, bypassing any custom comparison operators.
Definition: util.hpp:372