Dispatcher-Pipeline: mocked Dispatcher implementation complete (closes: #1294)

`steam/engine/mock-dispatcher.hpp |cpp` now integrates this
''complete mock setup for render jobs and frame dispatching.''
The exising `DummyJob` has been slightly adapted and renamed
to `MockJob` and is tightly integrated with the other mocks.

The implementation of a `MockDispatcher` necessitated to change
the use of `MockJobTicket`. The initial attempts used a complete
mock implementation, but this approach turned out not to be viable.
Instead — based on the ideas developed for the mock setup —
now the prospective real implementation of `JobTicket` is available
and will be used by the mock setup too. Instead of a synthetic spec,
now a setup of recursively connected `ExitNode`(s) is used; the latter
seems to develop into some kind of Facade for the render node network.

Based on this mock setup, we can now demonstrate the (mostly) complete
Job-Planning pipeline, starting from a segmentation up to render jobs,
and verify proper connectivity and job invocation.
✔
This commit is contained in:
Fischlurch 2023-06-13 20:23:33 +02:00
parent 0b9705692b
commit 542017aa65
13 changed files with 231 additions and 276 deletions

View file

@ -48,41 +48,11 @@
#include "lib/iter-adapter.hpp"
#include <boost/type_traits/remove_const.hpp>
#include <type_traits>
namespace std {
template <typename _Tp, typename _Dp>
class unique_ptr;
}
namespace lib {
namespace {
/** helper to remove pointer,
* while retaining const */
template<typename T>
struct RemovePtr { typedef T Type; };
template<typename T>
struct RemovePtr<T*> { typedef T Type; };
template<typename T>
struct RemovePtr<const T*> { typedef const T Type; };
template<typename T>
struct RemovePtr<T* const> { typedef const T Type; };
template<typename T>
struct RemovePtr<const T* const> { typedef const T Type; };
/** allow automatic dereferencing of std::unique_ptr */
template<typename T, typename D>
struct RemovePtr<std::unique_ptr<T,D>> { typedef T Type; };
}
/**
* wrapper for an existing Iterator type,
@ -98,26 +68,30 @@ namespace lib {
public:
typedef typename IT::value_type pointer;
typedef typename RemovePtr<pointer>::Type value_type;
typedef value_type& reference;
/** this iterator adapter is meant to wrap an iterator yielding pointer values */
using pointer = typename meta::ValueTypeBinding<IT>::value_type;
static_assert(std::is_pointer_v<pointer>);
using value_type = typename std::remove_pointer_t<pointer>;
using reference = value_type&;
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (PtrDerefIter);
// the purpose of the following typedefs is to ease building a correct "const iterator"
// the purpose of the following typedefs is to support building a correct "const iterator"
typedef typename boost::remove_const<value_type>::type ValueTypeBase; // value_type without const
using ValueTypeBase = typename std::remove_const_t<value_type>; // value_type without const
typedef typename IterType<IT>::template SimilarIter< ValueTypeBase* * >::Type WrappedIterType;
typedef typename IterType<IT>::template SimilarIter<const ValueTypeBase* * >::Type WrappedConstIterType;
using WrappedIterType = typename IterType<IT>::template SimilarIter< ValueTypeBase* * >::Type;
using WrappedConstIterType = typename IterType<IT>::template SimilarIter<const ValueTypeBase* * >::Type;
typedef PtrDerefIter<WrappedIterType> IterType;
typedef PtrDerefIter<WrappedConstIterType> ConstIterType;
using IterType = PtrDerefIter<WrappedIterType>;
using ConstIterType = PtrDerefIter<WrappedConstIterType>;
/** PtrDerefIter is always created
/** PtrDerefIter is always created
* by wrapping an existing iterator.
*/
explicit
@ -244,7 +218,7 @@ namespace lib {
/**
/**
* wrapper for an existing Iterator type to expose the address of each value yielded.
* Typically this can be used to build visitation sequences based on values living
* within a stable data structure (e.g. unmodifiable STL vector)
@ -280,7 +254,7 @@ namespace lib {
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (AddressExposingIter);
/** AddressExposingIter is always created
/** AddressExposingIter is always created
* by wrapping an existing iterator.
*/
explicit
@ -304,7 +278,7 @@ namespace lib {
/** @return address of the source iteraor's current result
* @warning exposing a reference to an internal pointer for sake of compatibility.
* Clients must not store that reference, but rather use it to initialise
* a copy. The internal pointer exposed here will be changed on increment.
* a copy. The internal pointer exposed here will be changed on increment.
*/
reference
operator*() const

View file

@ -32,7 +32,6 @@
#include "steam/common.hpp"
#include "steam/mobject/model-port.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/time/timequant.hpp"
#include <limits>

View file

@ -36,19 +36,14 @@
#define STEAM_ENGINE_JOB_TICKET_H
#include "steam/common.hpp"
//#include "steam/state.hpp"
#include "vault/engine/job.h"
#include "steam/engine/frame-coord.hpp"
#include "steam/engine/exit-node.hpp"
//#include "lib/time/timevalue.hpp"
//#include "lib/time/timequant.hpp"
#include "lib/hierarchy-orientation-indicator.hpp"
#include "lib/linked-elements.hpp"
#include "lib/util-foreach.hpp"
#include "lib/iter-adapter.hpp"
#include "lib/itertools.hpp"
#include "lib/util-foreach.hpp"
#include "lib/meta/tuple-helper.hpp"
#include "lib/meta/trait.hpp"
#include "lib/util.hpp"
#include <utility>
@ -58,10 +53,6 @@
namespace steam {
namespace engine {
//using lib::time::TimeSpan;
//using lib::time::Duration;
//using lib::time::FSecs;
//using lib::time::Time;
using vault::engine::Job;
using vault::engine::JobFunctor;
using vault::engine::JobClosure; /////////////////////////////////////////////////////////////////////TICKET #1287 : fix actual interface down to JobFunctor (after removing C structs)

View file

@ -39,7 +39,6 @@
#include "steam/engine/job-ticket.hpp"
#include "lib/allocator-handle.hpp"
#include "lib/time/timevalue.hpp"
#include "lib/itertools.hpp"
#include "lib/util.hpp"
#include <utility>

View file

@ -54,6 +54,7 @@
#include <list>
#include <optional>
#include <functional>
namespace steam {
@ -83,19 +84,17 @@ namespace fixture {
class Segmentation
: util::NonCopyable
{
protected:
/** segments of the engine in ordered sequence. */
list<Segment> segments_;
public:
virtual ~Segmentation(); ///< this is an interface
protected:
Segmentation() ///< there is always a single cover-all Segment initially
: segments_{1}
{ }
public:
virtual ~Segmentation(); ///< this is an interface
size_t
size() const
{
@ -121,6 +120,16 @@ namespace fixture {
/** rework the existing Segmentation to include a new Segment as specified */
Segment const&
splitSplice (OptTime start, OptTime after, engine::ExitNodes&& modelLink =ExitNodes{});
protected:
/** @internal rewrite the NodeGraphAttachment in each Segment */
void
adaptSpecification (std::function<NodeGraphAttachment(NodeGraphAttachment const&)> rewrite)
{
for (fixture::Segment& seg : segments_)
seg.exitNode = move(rewrite (seg.exitNode));
}
};

View file

@ -29,16 +29,18 @@
#include "lib/util.hpp"
#include "vault/real-clock.hpp"
#include "vault/engine/dummy-job.hpp"
#include "steam/engine/mock-dispatcher.hpp"
#include <boost/functional/hash.hpp>
namespace vault {
namespace engine {
namespace test {
namespace steam {
namespace engine{
namespace test {
using util::isSameObject;
using vault::engine::Job;
using vault::engine::JobParameter;
@ -67,23 +69,23 @@ namespace test {
void
verify_simple_job_properties()
{
Job job = DummyJob::build();
MockJob job;
CHECK (job.isValid());
Time beforeInvocation = RealClock::now();
job.triggerJob();
CHECK (DummyJob::was_invoked (job));
CHECK (RealClock::now() > DummyJob::invocationTime (job));
CHECK (beforeInvocation < DummyJob::invocationTime (job));
CHECK (MockJob::was_invoked (job));
CHECK (RealClock::now() > MockJob::invocationTime (job));
CHECK (beforeInvocation < MockJob::invocationTime (job));
}
void
verify_job_identity()
{
Job job1 = DummyJob::build();
Job job2 = DummyJob::build();
MockJob job1;
MockJob job2;
CHECK (job1 != job2, "random test data clash");
@ -130,4 +132,4 @@ namespace test {
/** Register this test class... */
LAUNCHER(JobHash_test, "unit engine");
}}} // namespace vault::engine::test
}}} // namespace steam::engine::test

View file

@ -28,19 +28,14 @@
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "steam/engine/mock-dispatcher.hpp"
#include "vault/engine/dummy-job.hpp"
#include "lib/format-cout.hpp"///////////////////////TODO
#include "lib/iter-tree-explorer.hpp"
#include "lib/format-util.hpp"
#include "lib/util.hpp"
//#include "steam/engine/job-planning.hpp"
//#include <ctime>
using test::Test;
//using std::rand;
using lib::eachNum;
using lib::treeExplore;
using lib::time::PQuant;
@ -52,7 +47,6 @@ namespace steam {
namespace engine{
namespace test {
using vault::engine::DummyJob;
using lib::time::FixedFrameQuantiser;
namespace { // test fixture...
@ -83,7 +77,7 @@ namespace test {
* - integration: generate a complete sequence of (dummy)Jobs
* - scaffolding and mocking used for this test
*
* @todo WIP-WIP-WIP 4/2023
* @todo WIP-WIP 4/2023
*
* @see DispatcherInterface_test
* @see MockSupport_test
@ -112,12 +106,12 @@ namespace test {
Time nominalTime = lib::test::randTime();
int additionalKey = rand() % 5000;
Job mockJob = DummyJob::build (nominalTime, additionalKey);
MockJob mockJob{nominalTime, additionalKey};
mockJob.triggerJob();
CHECK (DummyJob::was_invoked (mockJob));
CHECK (RealClock::wasRecently (DummyJob::invocationTime (mockJob)));
CHECK (nominalTime == DummyJob::invocationNominalTime (mockJob) );
CHECK (additionalKey == DummyJob::invocationAdditionalKey(mockJob));
CHECK (MockJob::was_invoked (mockJob));
CHECK (RealClock::wasRecently (MockJob::invocationTime (mockJob)));
CHECK (nominalTime == MockJob::invocationNominalTime (mockJob) );
CHECK (additionalKey == MockJob::invocationAdditionalKey(mockJob));
// Build a simple Segment at [10s ... 20s[
MockSegmentation mockSegs{MakeRec()
@ -143,10 +137,15 @@ namespace test {
jobP.triggerJob();
jobM.triggerJob();
CHECK (123 == DummyJob::invocationAdditionalKey (jobM)); // verify each job was invoked and linked to the correct spec,
CHECK (555 == DummyJob::invocationAdditionalKey (jobP)); // indicating that in practice it will activate the proper render node
//
////////////////////////////////////////////////////////////////////TODO: extract Dispatcher-Mock from DispatcherInterface_test
CHECK (123 == MockJob::invocationAdditionalKey (jobM)); // verify each job was invoked and linked to the correct spec,
CHECK (555 == MockJob::invocationAdditionalKey (jobP)); // indicating that in practice it will activate the proper render node
coord.modelPortIDX = 1;
coord.absoluteNominalTime = Time{0,30};
MockDispatcher dispatcher; // a complete dispatcher backed by a mock Segment for the whole timeline
auto [port1,sink1] = dispatcher.getDummyConnection(1); // also some fake ModelPort and DataSink entries are registered
Job jobD = dispatcher.getJobTicketFor(coord).createJobFor(coord);
CHECK (dispatcher.verify(jobD, port1, sink1)); // the generated job uses the associated ModelPort and DataSink and JobTicket
}

View file

@ -1,5 +1,5 @@
/*
DummyJob - diagnostic job for unit tests
MockDispatcher - diagnostic render job and frame dispatcher
Copyright (C) Lumiera.org
2013, Hermann Vosseler <Ichthyostega@web.de>
@ -20,7 +20,7 @@
* *****************************************************/
/** @file dummy-job.cpp
/** @file mock-dispatcher.cpp
** Implementation of a dummy render job for unit tests.
** Based on using a specifically rigged DummyClosure as JobFunctor,
** where the actual Job invocation does nothing other than storing
@ -32,14 +32,14 @@
**
** # Usage front-end
**
** The static functions in vault::engine::DummyJob allow to
** - build such a mock job, possibly with random (or well defined) parameters
** - when passing back this job instance, verify invocation and extract data
** A MockJob can directly created, and then sliced down to the Job baseclass,
** since it has no additional data fields. The static functions in MockJob allow
** to verify that a given job instance was created from this setup, that it was
** invoked, and verify invocation time and extract data
*/
#include "vault/engine/dummy-job.hpp"
#include "steam/engine/mock-dispatcher.hpp"
#include "vault/engine/nop-job-functor.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/time/timevalue.hpp"
@ -50,14 +50,15 @@
#include "lib/util.hpp"
#include <cstdlib>
#include <unordered_map>
#include <functional>
#include <unordered_map>
namespace vault{
namespace engine {
namespace steam {
namespace engine{
namespace test {
namespace { // DummyJob implementation details...
namespace { // MockJob and DummyClosure implementation details...
using lib::HashVal;
using lib::NullValue;
@ -65,14 +66,16 @@ namespace engine {
using std::unordered_map;
using util::access_or_default;
using vault::engine::JobParameter;
const int MAX_PARAM_A(1000); ///< random test values 0...1000
const int MAX_PARAM_B(10); ///< random test values -10...+10
/**
* test dummy jobs are backed by this closure.
* DummyJob invocations are recorded in a hashtable
* MockJob objects are backed by this closure.
* Invocations of this job functor are recorded in a hashtable
* @note as of 5/2023, we use a simplistic map-based implementation,
* causing a consecutive invocation of the same job instance
* with identical JobParameter to overwrite the previous log entry.
@ -158,7 +161,7 @@ namespace engine {
{ }
};
/** recording DummyJob invocations */
/** recording MockJob invocations */
unordered_map<HashVal,Invocation> invocationLog_;
@ -195,7 +198,7 @@ namespace engine {
Job
DummyJob::build()
MockJob::build()
{
InvocationInstanceID invoKey;
invoKey.part.a = rand() % MAX_PARAM_A;
@ -208,7 +211,7 @@ namespace engine {
Job
DummyJob::build (Time nominalTime, int additionalKey)
MockJob::build (Time nominalTime, int additionalKey)
{
InvocationInstanceID invoKey;
invoKey.part.a = additionalKey;
@ -219,7 +222,7 @@ namespace engine {
bool
DummyJob::was_invoked (Job const& job)
MockJob::was_invoked (Job const& job)
{
REQUIRE (job.usesClosure (dummyClosure));
@ -228,7 +231,7 @@ namespace engine {
Time
DummyJob::invocationTime (Job const& job)
MockJob::invocationTime (Job const& job)
{
REQUIRE (job.usesClosure (dummyClosure));
@ -237,7 +240,7 @@ namespace engine {
Time
DummyJob::invocationNominalTime (Job const& job)
MockJob::invocationNominalTime (Job const& job)
{
REQUIRE (job.usesClosure (dummyClosure));
@ -246,7 +249,7 @@ namespace engine {
int
DummyJob::invocationAdditionalKey (Job const& job)
MockJob::invocationAdditionalKey (Job const& job)
{
REQUIRE (job.usesClosure (dummyClosure));
@ -256,7 +259,7 @@ namespace engine {
/** @internal for collaboration with other Mock/Dummy facilities */
JobClosure&
DummyJob::getFunctor()
MockJob::getFunctor()
{
return dummyClosure;
}
@ -268,7 +271,7 @@ namespace engine {
* @see JobTicket::JobTicket::createJobFor(FrameCoord)
*/
bool
DummyJob::isNopJob (Job const& job)
MockJob::isNopJob (Job const& job)
{
InvocationInstanceID empty; ///////////////////////////////////////////////////////////////////////TICKET #1287 : temporary workaround until we get rid of the C base structs
JobClosure& jobFunctor = static_cast<JobClosure&> (*job.jobClosure); //////////////////////////TICKET #1287 : fix actual interface down to JobFunctor (after removing C structs)
@ -278,4 +281,4 @@ namespace engine {
}} // namespace vault::engine
}}} // namespace steam::engine::test

View file

@ -22,19 +22,25 @@
/** @file mock-dispatcher.hpp
** Mock data structures to support implementation testing of render job
** planning and frame dispatch. Together with dummy-job.hpp, this provides
** a specifically rigged test setup, allowing to investigate and verify
** designated functionality in isolation, without backing by the actual
** render engine implementation.
** - the MockDispatcherTable emulates the frame dispatch from the Fixture
** planning and frame dispatch. This specifically rigged test setup allows to
** investigate and verify designated functionality in isolation, without backing
** by the actual render engine and low-level-Model implementation.
** - a MockJob is a render Job, wired to a DummyFunctor, which does nothing,
** but records any invocation into an internal diagnostics Map.
** - MockJobTicket is a builder / adapter on top of the actual steam::engine::JobTicket,
** allowing to generate simple JobTicket instances with an embedded ExitNode and
** a (configurable) pipelineID. From this setup, »mock jobs« can be generated,
** which use the MockJob functor and thus record any invocation without performing
** actual work. The internal connection to the MockJobTicket can then be verified.
** - MockSegmentation is a mocked variant of the »Segmentation« datastructure,
** which forms the backbone of the Fixture and is the top-level attachment
** point for the »low-level-Model« (the render nodes network)
** - MockJobTicket is a builder / adapter on top of the actual steam::engine::JobTicket,
** allowing to generate a complete rigged MockSegmentation setup from a generic
** test specification written as nested lib::diff::GenNode elements. From this
** setup, »mock jobs« can be generated, which use the DummyJob functor and
** just record any invocation without performing actual work.
** point for the »low-level-Model« (the render nodes network). It can be
** configured with a test specification of ExitNode(s) defined by a
** GenNode tree, and defining Segments of the timeline and prerequisites.
** - finally, the MockDispatcher combines all these facilities to emulate
** frame dispatch from the Fixture without actually using any data model.
** Similar to MockSegmentation, a GenNode-based specification is used.
**
** @remark in spring 2023, this setup was created as a means to define and
** then build the actual implementation of frame dispatch and scheduling.
** @see MockSupport_test
@ -53,7 +59,6 @@
#include "steam/engine/dispatcher.hpp"
#include "steam/engine/job-ticket.hpp"
#include "vault/engine/job.h"
#include "vault/engine/dummy-job.hpp"
#include "vault/real-clock.hpp"
#include "lib/allocator-handle.hpp"
#include "lib/time/timevalue.hpp"
@ -79,45 +84,57 @@ namespace test {
using lib::HashVal;
using util::isnil;
using util::isSameObject;
///////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1294 : organisation of namespaces / includes??
using fixture::Segmentation;
using vault::engine::Job;
using vault::engine::JobClosure;
namespace { // used internally
using play::test::ModelPorts;
using play::test::PlayTestFrames_Strategy;
using vault::engine::JobClosure;
using vault::engine::JobParameter;
using vault::engine::DummyJob;
using DummyPlaybackSetup = play::test::DummyPlayConnection<PlayTestFrames_Strategy>;
/* ===== specify a mock JobTicket setup for tests ===== */
inline ExitNode
defineSimpleSpec (HashVal seed = 1+rand())
{
return ExitNode{seed
,ExitNodes{}
,& DummyJob::getFunctor()};
}
}//(End)internal test helpers....
/**
* Mock setup for a JobTicket to generate DummyJob invocations.
* Mock setup for a render Job with NO action but built-in diagnostics.
* Each invocation of such a MockJob will be logged internally
* and can be investigated and verified afterwards.
*/
class MockJob
: public Job
{
static Job build(); ///< uses random job definition values
static Job build (Time nominalTime, int additionalKey);
public:
MockJob()
: Job{build()}
{ }
MockJob (Time nominalTime, int additionalKey)
: Job{build (nominalTime,additionalKey)}
{ }
static bool was_invoked (Job const& job);
static Time invocationTime (Job const& job);
static Time invocationNominalTime (Job const& job);
static int invocationAdditionalKey (Job const& job);
static bool isNopJob (Job const&);
static JobClosure& getFunctor();
};
/**
* Mock setup for a JobTicket to generate dummy render Job invocations.
* Implemented as subclass, it provides a specification DSL for tests,
* and is able to probe some otherwise opaque internals of JobTicket.
* Beyond that, MockJobTicket has the same storage size; and behaves
* like the regular JobTicket after construction -- but any Job
* created by JobTicket::createJobFor(FrameCoord) will be wired
* with the DummyJob functor and can thus be related back to
* with the MockJob functor and can thus be related back to
* the test specification setup.
* @see JobPlanningSetup_test
* @see DispatcherInterface_test
@ -132,6 +149,15 @@ namespace test {
return static_cast<lib::AllocatorHandle<JobTicket>&> (*this);
}
/** provide a test specification wired to MockJob */
static ExitNode
defineSimpleSpec (HashVal seed = 1+rand())
{
return ExitNode{seed
,ExitNodes{}
,& MockJob::getFunctor()};
}
public:
MockJobTicket()
: JobTicket{defineSimpleSpec(), allocator()}
@ -157,14 +183,14 @@ namespace test {
* test, by passing a test specification in »GenNode« notation to the
* constructor. This specification defines the segments to create
* and allows to associate a marker number, which can later be
* verified from the actual DummyJob invocation.
* verified from the actual DummyClosure invocation.
* - the ctor accepts a sequence of GenNode elements,
* each corresponding to a segment to created
* - optionally, attributes "start" and "after" can be defined
* to provide the lib::time::Time values of segment start/end
* - in addition, optionally a "mark" attribute can be defined;
* the given integer number will be "hidden" in the job instance
* hash, and can be [verified](\ref DummyJob::invocationAdditionalKey)
* hash, and can be [verified](\ref MockJob::invocationAdditionalKey)
* - the _scope_ of each top-level GenNode may hold a sequence of
* nested nodes corresponding to _prerequisite_ JobTicket instances
* - these can in turn hold further nested prerequisites, and so on
@ -198,7 +224,7 @@ namespace test {
{
return ExitNode{buildSeed (spec)
,buildPrerequisites (spec)
,& DummyJob::getFunctor()};
,& MockJob::getFunctor()};
}
/** @internal helper for MockDispatcher */
@ -238,8 +264,12 @@ namespace test {
inline void
MockSegmentation::duplicateExitNodeSpec (uint times)
{
for (fixture::Segment& seg : segments_)
seg.exitNode = move(fixture::NodeGraphAttachment{ExitNodes{times, seg.exitNode[0]}});
using Spec = fixture::NodeGraphAttachment;
Segmentation::adaptSpecification ([times](Spec const& spec)
{
return Spec{ExitNodes{times, spec[0]}};
});
}
@ -273,6 +303,28 @@ namespace test {
namespace { // used internally by MockDispatcher....
using play::test::ModelPorts;
using play::test::PlayTestFrames_Strategy;
using DummyPlaybackSetup = play::test::DummyPlayConnection<PlayTestFrames_Strategy>;
}
/**
* A mocked frame Dispatcher setup without any backing model.
* Instantiating such a MockDispatcher will automatically create some fake
* model structures and some ModelPort and DisplaySink handles (and thereby
* push aside and shadow any existing ModelPort registry).
*
* The configuration is similar to MockSegmentation, using a test spec
* given as GenNode-tree to define Segments of the timeline and possibly
* pipeline-IDs and prerequisites. One notable difference is that here
* the default ctor always creates a single Segment covering the whole
* time axis, and that the ExitNode specification is automatically
* duplicated for all faked ModelPort(s).
*/
class MockDispatcher
: public Dispatcher
{
@ -285,8 +337,9 @@ namespace test {
const PortIdxMap portIdx_;
public:
/* == mock Dispatcher implementation == */
/* == mock implementation of the Dispatcher interface == */
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete
FrameCoord
locateRelative (FrameCoord const&, FrameCnt frameOffset) override
{
@ -298,7 +351,7 @@ namespace test {
{
UNIMPLEMENTED ("determine when to finish a planning chunk");
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete
size_t
resolveModelPort (ModelPort modelPort) override
@ -319,6 +372,7 @@ namespace test {
}
public:
MockDispatcher()
: dummySetup_{}
, mockSeg_{MakeRec().genNode()} // Node: generate a single active Segment to cover all
@ -368,7 +422,7 @@ namespace test {
TimeValue nominalTime{job.parameter.nominalTime};
size_t portIDX = resolveModelPort (port);
JobTicket& ticket = accessJobTicket (portIDX, nominalTime);
return isnil (ticket)? DummyJob::isNopJob (job)
return isnil (ticket)? MockJob::isNopJob (job)
: MockJobTicket::isAssociated (job, ticket);
}
@ -388,9 +442,6 @@ namespace test {
}
};
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1221
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #1221
}}} // namespace steam::engine::test
#endif /*STEAM_ENGINE_TEST_MOCK_DISPATCHER_H*/

View file

@ -29,7 +29,6 @@
#include "lib/test/test-helper.hpp"
#include "steam/engine/mock-dispatcher.hpp"
#include "vault/engine/nop-job-functor.hpp"
#include "vault/engine/dummy-job.hpp"
#include "lib/iter-tree-explorer.hpp"
#include "lib/util-tuple.hpp"
#include "lib/util.hpp"
@ -43,7 +42,6 @@ namespace engine{
namespace test {
using steam::fixture::Segment;
using vault::engine::DummyJob;
using lib::singleValIterator;
using util::isSameObject;
using util::seqTuple;
@ -55,6 +53,7 @@ namespace test {
* - creating and invoking mock render jobs
* - a mocked JobTicket, generating mock render jobs
* - configurable test setup for a mocked Segmentation datastructure
* - configurable setup of a complete frame Dispatcher
* @see JobPlanningSetup_test
* @see Dispatcher
* @see vault::engine::Job
@ -95,7 +94,7 @@ namespace test {
CHECK (MockJobTicket::isAssociated (job, ticket));
job.triggerJob();
CHECK (DummyJob::was_invoked (job));
CHECK (MockJob::was_invoked (job));
}
@ -107,21 +106,21 @@ namespace test {
{
Time nominalTime = lib::test::randTime();
int additionalKey = rand() % 5000;
Job mockJob = DummyJob::build (nominalTime, additionalKey);
MockJob mockJob{nominalTime, additionalKey};
CHECK (mockJob.getNominalTime() == nominalTime);
CHECK (not DummyJob::was_invoked (mockJob));
CHECK (not MockJob::was_invoked (mockJob));
mockJob.triggerJob();
CHECK (DummyJob::was_invoked (mockJob));
CHECK (RealClock::wasRecently (DummyJob::invocationTime (mockJob)));
CHECK (nominalTime == DummyJob::invocationNominalTime (mockJob) );
CHECK (additionalKey == DummyJob::invocationAdditionalKey(mockJob));
CHECK (MockJob::was_invoked (mockJob));
CHECK (RealClock::wasRecently (MockJob::invocationTime (mockJob)));
CHECK (nominalTime == MockJob::invocationNominalTime (mockJob) );
CHECK (additionalKey == MockJob::invocationAdditionalKey(mockJob));
Time prevInvocation = DummyJob::invocationTime (mockJob);
Time prevInvocation = MockJob::invocationTime (mockJob);
mockJob.triggerJob();
CHECK (prevInvocation < DummyJob::invocationTime (mockJob)); // invoked again, recorded new invocation time
CHECK (nominalTime == DummyJob::invocationNominalTime (mockJob) ); // all other Job parameter recorded again unaltered
CHECK (additionalKey == DummyJob::invocationAdditionalKey(mockJob));
CHECK (prevInvocation < MockJob::invocationTime (mockJob)); // invoked again, recorded new invocation time
CHECK (nominalTime == MockJob::invocationNominalTime (mockJob) ); // all other Job parameter recorded again unaltered
CHECK (additionalKey == MockJob::invocationAdditionalKey(mockJob));
}
@ -182,12 +181,12 @@ namespace test {
Job someJob = ticket.createJobFor(coord); // JobTicket uses, but does not check the time given in FrameCoord
CHECK (someJob.parameter.nominalTime == _raw(coord.absoluteNominalTime));
CHECK (MockJobTicket::isAssociated (someJob, ticket)); // but the generated Job is linked to the Closure backed by the JobTicket
CHECK (not DummyJob::was_invoked (someJob));
CHECK (not MockJob::was_invoked (someJob));
someJob.triggerJob();
CHECK (DummyJob::was_invoked (someJob));
CHECK (RealClock::wasRecently (DummyJob::invocationTime (someJob)));
CHECK (someTime == DummyJob::invocationNominalTime (someJob));
CHECK (MockJob::was_invoked (someJob));
CHECK (RealClock::wasRecently (MockJob::invocationTime (someJob)));
CHECK (someTime == MockJob::invocationNominalTime (someJob));
}
//-----------------------------------------------------------------/// Segmentation with a segment spanning part of the timeline > 10s
{
@ -214,9 +213,9 @@ namespace test {
CHECK (marker == job.parameter.invoKey.part.a);
job.triggerJob();
CHECK (DummyJob::was_invoked (job));
CHECK (RealClock::wasRecently (DummyJob::invocationTime (job)));
CHECK (marker == DummyJob::invocationAdditionalKey (job)); // DummyClosure is rigged such as to feed back the seed in `part.a`
CHECK (MockJob::was_invoked (job));
CHECK (RealClock::wasRecently (MockJob::invocationTime (job)));
CHECK (marker == MockJob::invocationAdditionalKey (job)); // DummyClosure is rigged such as to feed back the seed in `part.a`
// and thus we can prove this job really belongs to the marked segment
// create another job from the (empty) seg1
job = seg1.jobTicket(0).createJobFor (coord);
@ -250,7 +249,7 @@ namespace test {
Job job = s2.jobTicket(0).createJobFor(coord);
job.triggerJob();
CHECK (marker == DummyJob::invocationAdditionalKey (job));
CHECK (marker == MockJob::invocationAdditionalKey (job));
}
//-----------------------------------------------------------------/// Segmentation with several segments built in specific order
{
@ -293,10 +292,10 @@ namespace test {
Job job = segment.jobTicket(0).createJobFor(coord);
job.triggerJob();
CHECK (DummyJob::was_invoked (job));
CHECK (RealClock::wasRecently (DummyJob::invocationTime (job)));
CHECK (MockJob::was_invoked (job));
CHECK (RealClock::wasRecently (MockJob::invocationTime (job)));
return DummyJob::invocationAdditionalKey (job);
return MockJob::invocationAdditionalKey (job);
};
CHECK (2 == probeKey(s1)); // verify all generated jobs are wired back to the correct segment
CHECK (3 == probeKey(s2));
@ -339,8 +338,8 @@ namespace test {
job1.triggerJob();
job2.triggerJob();
CHECK (23 == DummyJob::invocationAdditionalKey (job1));
CHECK (11 == DummyJob::invocationAdditionalKey (job2));
CHECK (23 == MockJob::invocationAdditionalKey (job1));
CHECK (11 == MockJob::invocationAdditionalKey (job2));
}
//-----------------------------------------------------------------/// a tree of deep nested prerequisites
{

View file

@ -31,7 +31,7 @@
#include "vault/real-clock.hpp"
#include "vault/engine/scheduler-frontend.hpp"
#include "vault/engine/scheduler-diagnostics.hpp"
#include "vault/engine/dummy-job.hpp"
#include "steam/engine/mock-dispatcher.hpp"
namespace vault {
@ -44,6 +44,8 @@ namespace test {
using lib::time::Offset;
using lib::time::FSecs;
using steam::engine::test::MockJob;
namespace { // test fixture: scheduling a dummy job operation...
@ -92,7 +94,7 @@ namespace test {
{
SchedulerDiagnostics monitor(scheduler);
Job job = DummyJob::build();
MockJob job;
Time deadline(TEST_START_TIME);
scheduler.startJobTransaction()
@ -112,8 +114,8 @@ namespace test {
JobTransaction tx = scheduler.startJobTransaction();
Job job1 = DummyJob::build();
Job job2 = DummyJob::build();
MockJob job1;
MockJob job2;
tx.addFreewheeling(job1);
tx.addBackground (job2);
@ -178,7 +180,7 @@ namespace test {
Time nominalTime(dummyFrameStart(frameNr));
Time deadline(TEST_START_TIME + nominalTime);
Job job = DummyJob::build(nominalTime, frameNr);
MockJob job{nominalTime, frameNr};
currentTx.addJob (deadline, job);

View file

@ -1,75 +0,0 @@
/*
DUMMY-JOB.hpp - diagnostic job for unit tests
Copyright (C) Lumiera.org
2013, 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 02139, USA.
*/
/** @file dummy-job.hpp
** Unit test helper to generate dummy render jobs.
** Render Jobs generated from this setup will not actually perform
** any action, other than recording this invocation and the used
** parameters into a map table managed behind the scenes. Using
** the provided query function, it is possible to probe for such
** an invocation and to extract the recorded parameter data.
**
** This setup is used both for stand-alone tests, which just require
** "some job", but also as part of a complete hierarchy of mocked
** data structures related to frame job dispatch and invocation
** @see mock-dispatcher.hpp
** @see MockSupport_test
**
*/
#ifndef VAULT_ENGINE_DUMMY_JOB_H
#define VAULT_ENGINE_DUMMY_JOB_H
#include "vault/engine/job.h"
#include "lib/time/timevalue.hpp"
namespace vault{
namespace engine {
using lib::time::Time;
/**
* Test helper: generate test dummy jobs with built-in diagnostics.
* Each invocation of such a dummy job will be logged internally
* and can be investigated and verified afterwards.
*/
struct DummyJob
{
static Job build(); ///< uses random job definition values
static Job build (Time nominalTime, int additionalKey);
static bool was_invoked (Job const& job);
static Time invocationTime (Job const& job);
static Time invocationNominalTime (Job const& job);
static int invocationAdditionalKey (Job const& job);
static JobClosure& getFunctor();
static bool isNopJob (Job const&);
};
}} // namespace vault::engine
#endif

View file

@ -70119,7 +70119,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1681742255171" ID="ID_1531898349" MODIFIED="1681744077783" TEXT="Draft im Test-Setup">
<icon BUILTIN="pencil"/>
<node COLOR="#435e98" CREATED="1681742836996" FOLDED="true" ID="ID_1978512771" MODIFIED="1686608177837" TEXT="scaffolding and mocking used for this test">
<node COLOR="#435e98" CREATED="1681742836996" FOLDED="true" ID="ID_1978512771" MODIFIED="1686679637400" TEXT="scaffolding and mocking used for this test">
<icon BUILTIN="full-1"/>
<node CREATED="1681742863121" ID="ID_101929835" MODIFIED="1684878277170" TEXT="ben&#xf6;tigte Mocks">
<icon BUILTIN="info"/>
@ -70128,9 +70128,10 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1684878221645" ID="ID_1469919037" MODIFIED="1684878223961" TEXT="neu bauen"/>
<node CREATED="1684878224453" ID="ID_1519678460" LINK="#ID_1176991982" MODIFIED="1684878249666" TEXT="dabei Grundlage schaffen: SplitSplice-Algo"/>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1681742879175" ID="ID_1375353236" MODIFIED="1684878270195" TEXT="Dispatcher">
<icon BUILTIN="hourglass"/>
<node COLOR="#338800" CREATED="1681742879175" ID="ID_1375353236" MODIFIED="1686679650140" TEXT="Dispatcher">
<icon BUILTIN="button_ok"/>
<node CREATED="1681743458393" ID="ID_891681445" MODIFIED="1681743469323" TEXT="extrahieren aus DispatcherInterface_test"/>
<node CREATED="1686679652411" ID="ID_111433590" MODIFIED="1686679666968" TEXT="MockDispatcher nun integriert mit dem sonstigen Mock setup"/>
</node>
<node COLOR="#338800" CREATED="1681742890385" ID="ID_1681918468" MODIFIED="1684878273498" TEXT="JobTicket">
<icon BUILTIN="button_ok"/>
@ -70277,7 +70278,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node CREATED="1682384630010" ID="ID_1719484347" MODIFIED="1682385357439" TEXT="Struktur">
<node COLOR="#435e98" CREATED="1682384630010" ID="ID_1719484347" MODIFIED="1686679804479" TEXT="Test-Spezifikation">
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="info"/>
<node CREATED="1682384645189" ID="ID_384048903" MODIFIED="1682384657607" TEXT="1 Record pro Segment">
<node CREATED="1682384866271" ID="ID_1100765917" MODIFIED="1682384873170" TEXT="start=Time"/>
@ -70316,7 +70318,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="button_cancel"/>
</node>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1682384817342" ID="ID_1041945883" MODIFIED="1683809828917" TEXT="Channel-Zuordnung nur per Filter/Marker">
<node COLOR="#5b280f" CREATED="1682384817342" ID="ID_1041945883" MODIFIED="1686679760838" TEXT="Channel-Zuordnung nur per Filter/Marker">
<richcontent TYPE="NOTE"><html>
<head>
@ -70327,17 +70329,18 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</p>
</body>
</html></richcontent>
<icon BUILTIN="hourglass"/>
<icon BUILTIN="button_cancel"/>
<node COLOR="#435e98" CREATED="1683809711717" ID="ID_1916276594" LINK="#ID_1346581014" MODIFIED="1685987685173" TEXT="Channel sind tats&#xe4;chlich ModelPorts">
<icon BUILTIN="idea"/>
</node>
<node COLOR="#338800" CREATED="1683809741762" ID="ID_610603117" MODIFIED="1686581258295" TEXT="daher wandern sie vom JobTicket in das Segment">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1683809804057" ID="ID_733275282" MODIFIED="1683809818893" TEXT="Thema vorerst ignorieren (immer ein Channel und gut is)">
<icon BUILTIN="bell"/>
<node COLOR="#338800" CREATED="1686679744191" ID="ID_1546924020" MODIFIED="1686679756422" TEXT="Channel-Differenzierung wurde aus dem JobTicket entfernt">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1685988083711" ID="ID_1973560674" MODIFIED="1686581324371" TEXT="Differenzierung nach Channel schwer umsetzbar">
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1685988083711" ID="ID_1973560674" MODIFIED="1686679703593" TEXT="Differenzierung nach ModelPort schwer umsetzbar">
<linktarget COLOR="#811354" DESTINATION="ID_1973560674" ENDARROW="Default" ENDINCLINATION="-1463;83;" ID="Arrow_ID_345386605" SOURCE="ID_1499657819" STARTARROW="None" STARTINCLINATION="-217;-8;"/>
<icon BUILTIN="hourglass"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1686498710958" ID="ID_200132237" MODIFIED="1686498774995" TEXT="nicht klar ob das f&#xfc;r die Mock-L&#xf6;sung &#xfc;berhaupt gebraucht wird">
@ -70379,7 +70382,6 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="idea"/>
</node>
</node>
</node>
<node CREATED="1685987717673" ID="ID_1636681352" MODIFIED="1685987743599" TEXT="Umgang mit ModelPorts">
<node CREATED="1685987749304" ID="ID_771112192" MODIFIED="1685987767020" TEXT="das Haupt-Ticket ist f&#xfc;r jeden ModelPort stets vorhanden"/>
<node CREATED="1685987950917" ID="ID_1508383680" MODIFIED="1685988010119" TEXT="Prerequisites standardm&#xe4;&#xdf;ig auch aufdoppeln">