2023-04-17 17:10:53 +02:00
|
|
|
/*
|
2023-05-24 03:38:12 +02:00
|
|
|
MOCK-DISPATCHER.hpp - test scaffolding to verify render job planning and dispatch
|
2023-04-17 17:10:53 +02:00
|
|
|
|
|
|
|
|
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 02139, USA.
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
2023-05-24 03:38:12 +02:00
|
|
|
/** @file mock-dispatcher.hpp
|
|
|
|
|
** Mock data structures to support implementation testing of render job
|
2023-06-13 20:23:33 +02:00
|
|
|
** 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.
|
2023-05-24 03:38:12 +02:00
|
|
|
** - MockSegmentation is a mocked variant of the »Segmentation« datastructure,
|
|
|
|
|
** which forms the backbone of the Fixture and is the top-level attachment
|
2023-06-13 20:23:33 +02:00
|
|
|
** 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.
|
|
|
|
|
**
|
2023-05-24 03:38:12 +02:00
|
|
|
** @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
|
2023-04-17 17:10:53 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 03:38:12 +02:00
|
|
|
#ifndef STEAM_ENGINE_TEST_MOCK_DISPATCHER_H
|
|
|
|
|
#define STEAM_ENGINE_TEST_MOCK_DISPATCHER_H
|
2023-04-17 17:10:53 +02:00
|
|
|
|
|
|
|
|
|
2023-05-24 03:38:12 +02:00
|
|
|
#include "lib/test/test-helper.hpp"
|
2023-06-02 02:31:34 +02:00
|
|
|
#include "steam/play/dummy-play-connection.hpp"
|
2023-06-07 04:03:00 +02:00
|
|
|
#include "steam/fixture/node-graph-attachment.hpp"
|
2023-04-25 18:27:16 +02:00
|
|
|
#include "steam/fixture/segmentation.hpp"
|
2023-04-17 17:10:53 +02:00
|
|
|
#include "steam/mobject/model-port.hpp"
|
|
|
|
|
#include "steam/engine/dispatcher.hpp"
|
2023-04-18 20:02:36 +02:00
|
|
|
#include "steam/engine/job-ticket.hpp"
|
2023-05-24 03:38:12 +02:00
|
|
|
#include "vault/engine/job.h"
|
|
|
|
|
#include "vault/real-clock.hpp"
|
2023-06-11 04:37:38 +02:00
|
|
|
#include "lib/allocator-handle.hpp"
|
2023-04-20 23:55:02 +02:00
|
|
|
#include "lib/time/timevalue.hpp"
|
2023-04-25 13:40:20 +02:00
|
|
|
#include "lib/diff/gen-node.hpp"
|
2023-05-23 06:40:18 +02:00
|
|
|
#include "lib/linked-elements.hpp"
|
2023-04-20 18:53:17 +02:00
|
|
|
#include "lib/itertools.hpp"
|
2023-05-24 03:38:12 +02:00
|
|
|
#include "lib/depend.hpp"
|
2023-06-13 03:47:42 +02:00
|
|
|
#include "lib/util.hpp"
|
2023-04-17 17:10:53 +02:00
|
|
|
|
2023-04-20 23:55:02 +02:00
|
|
|
#include <tuple>
|
2023-06-13 03:47:42 +02:00
|
|
|
#include <map>
|
2023-04-17 17:10:53 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace steam {
|
|
|
|
|
namespace engine {
|
|
|
|
|
namespace test {
|
|
|
|
|
|
2023-05-24 03:38:12 +02:00
|
|
|
using std::make_tuple;
|
2023-04-25 13:40:20 +02:00
|
|
|
using lib::diff::GenNode;
|
|
|
|
|
using lib::diff::MakeRec;
|
2023-04-20 23:55:02 +02:00
|
|
|
using lib::time::TimeValue;
|
2023-05-02 04:16:39 +02:00
|
|
|
using lib::time::Time;
|
2023-04-30 02:18:56 +02:00
|
|
|
using lib::HashVal;
|
2023-06-13 03:47:42 +02:00
|
|
|
using util::isnil;
|
|
|
|
|
using util::isSameObject;
|
2023-04-25 18:27:16 +02:00
|
|
|
using fixture::Segmentation;
|
2023-06-13 20:23:33 +02:00
|
|
|
using vault::engine::Job;
|
|
|
|
|
using vault::engine::JobClosure;
|
2023-04-17 17:10:53 +02:00
|
|
|
|
2023-06-13 20:23:33 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
2023-05-01 17:02:11 +02:00
|
|
|
{
|
2023-06-13 20:23:33 +02:00
|
|
|
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();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-06-12 19:21:14 +02:00
|
|
|
|
2023-05-24 03:38:12 +02:00
|
|
|
|
|
|
|
|
|
2023-04-17 17:10:53 +02:00
|
|
|
|
|
|
|
|
/**
|
2023-06-13 20:23:33 +02:00
|
|
|
* Mock setup for a JobTicket to generate dummy render Job invocations.
|
2023-05-24 03:38:12 +02:00
|
|
|
* 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
|
2023-06-13 20:23:33 +02:00
|
|
|
* with the MockJob functor and can thus be related back to
|
2023-05-24 03:38:12 +02:00
|
|
|
* the test specification setup.
|
2023-04-18 20:02:36 +02:00
|
|
|
* @see JobPlanningSetup_test
|
2023-04-17 17:10:53 +02:00
|
|
|
* @see DispatcherInterface_test
|
|
|
|
|
*/
|
|
|
|
|
class MockJobTicket
|
2023-06-11 04:37:38 +02:00
|
|
|
: private lib::AllocatorHandle<JobTicket>
|
|
|
|
|
, public JobTicket
|
2023-04-17 17:10:53 +02:00
|
|
|
{
|
2023-06-11 04:37:38 +02:00
|
|
|
auto&
|
|
|
|
|
allocator()
|
|
|
|
|
{
|
|
|
|
|
return static_cast<lib::AllocatorHandle<JobTicket>&> (*this);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-13 20:23:33 +02:00
|
|
|
/** provide a test specification wired to MockJob */
|
|
|
|
|
static ExitNode
|
|
|
|
|
defineSimpleSpec (HashVal seed = 1+rand())
|
|
|
|
|
{
|
|
|
|
|
return ExitNode{seed
|
|
|
|
|
,ExitNodes{}
|
|
|
|
|
,& MockJob::getFunctor()};
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-17 17:10:53 +02:00
|
|
|
public:
|
2023-04-20 23:55:02 +02:00
|
|
|
MockJobTicket()
|
2023-06-11 04:37:38 +02:00
|
|
|
: JobTicket{defineSimpleSpec(), allocator()}
|
2023-05-01 17:02:11 +02:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
MockJobTicket (HashVal seed)
|
2023-06-11 04:37:38 +02:00
|
|
|
: JobTicket{defineSimpleSpec (seed), allocator()}
|
2023-05-01 17:02:11 +02:00
|
|
|
{ }
|
2023-04-17 17:10:53 +02:00
|
|
|
|
2023-05-11 22:47:56 +02:00
|
|
|
|
2023-05-24 03:38:12 +02:00
|
|
|
/* ===== Diagnostics ===== */
|
|
|
|
|
|
2023-04-30 02:18:56 +02:00
|
|
|
bool verify_associated (Job const&) const;
|
2023-05-01 17:02:11 +02:00
|
|
|
static bool isAssociated (Job const&, JobTicket const&);
|
2023-04-17 17:10:53 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-05-24 03:38:12 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Mock setup for a complete Segmentation to emulate the structure
|
|
|
|
|
* of the actual fixture, without the need of building a low-level Model.
|
|
|
|
|
* MockSegmentation instances can be instantiated directly within the
|
|
|
|
|
* 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
|
2023-06-13 20:23:33 +02:00
|
|
|
* verified from the actual DummyClosure invocation.
|
2023-05-24 03:38:12 +02:00
|
|
|
* - 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
|
2023-06-13 20:23:33 +02:00
|
|
|
* hash, and can be [verified](\ref MockJob::invocationAdditionalKey)
|
2023-05-24 03:38:12 +02:00
|
|
|
* - 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
|
|
|
|
|
* @see MockSetup_test::verify_MockSegmentation
|
|
|
|
|
*/
|
2023-04-25 13:40:20 +02:00
|
|
|
class MockSegmentation
|
|
|
|
|
: public Segmentation
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
MockSegmentation()
|
2023-04-27 22:30:49 +02:00
|
|
|
: Segmentation{}
|
2023-04-25 13:40:20 +02:00
|
|
|
{ }
|
|
|
|
|
|
|
|
|
|
MockSegmentation (std::initializer_list<GenNode> specs)
|
2023-04-27 19:38:37 +02:00
|
|
|
: MockSegmentation{}
|
2023-04-25 13:40:20 +02:00
|
|
|
{
|
2023-05-01 17:02:11 +02:00
|
|
|
for (auto& spec : specs)
|
|
|
|
|
{
|
2023-05-02 04:16:39 +02:00
|
|
|
auto start = spec.retrieveAttribute<Time> ("start");
|
|
|
|
|
auto after = spec.retrieveAttribute<Time> ("after");
|
2023-06-10 04:52:40 +02:00
|
|
|
Segmentation::splitSplice (start, after
|
|
|
|
|
,ExitNodes{buildExitNodeFromSpec (spec)}
|
|
|
|
|
);
|
2023-05-01 17:02:11 +02:00
|
|
|
}
|
2023-04-25 13:40:20 +02:00
|
|
|
}
|
2023-05-11 22:47:56 +02:00
|
|
|
|
2023-05-24 03:38:12 +02:00
|
|
|
|
2023-06-07 04:03:00 +02:00
|
|
|
ExitNode
|
|
|
|
|
buildExitNodeFromSpec (GenNode const& spec)
|
|
|
|
|
{
|
2023-06-07 17:21:31 +02:00
|
|
|
return ExitNode{buildSeed (spec)
|
2023-06-10 04:52:40 +02:00
|
|
|
,buildPrerequisites (spec)
|
2023-06-13 20:23:33 +02:00
|
|
|
,& MockJob::getFunctor()};
|
2023-06-10 04:52:40 +02:00
|
|
|
}
|
2023-06-07 04:03:00 +02:00
|
|
|
|
2023-06-13 03:47:42 +02:00
|
|
|
/** @internal helper for MockDispatcher */
|
|
|
|
|
void duplicateExitNodeSpec (uint times);
|
|
|
|
|
|
2023-06-07 04:03:00 +02:00
|
|
|
|
2023-06-07 17:21:31 +02:00
|
|
|
private: /* ======== Implementation: build fake ExitNodes from test specification ==== */
|
2023-05-24 03:38:12 +02:00
|
|
|
|
2023-05-11 22:47:56 +02:00
|
|
|
HashVal
|
|
|
|
|
buildSeed (GenNode const& spec)
|
|
|
|
|
{
|
|
|
|
|
auto seed = spec.retrieveAttribute<int> ("mark");
|
|
|
|
|
return seed? HashVal(*seed) : HashVal(rand() % 1000);
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-07 17:21:31 +02:00
|
|
|
ExitNodes
|
2023-05-11 22:47:56 +02:00
|
|
|
buildPrerequisites (GenNode const& spec)
|
|
|
|
|
{
|
2023-06-07 17:21:31 +02:00
|
|
|
ExitNodes prerequisites;
|
|
|
|
|
for (auto& child : spec.getChildren())
|
|
|
|
|
prerequisites.emplace_back (
|
|
|
|
|
buildExitNodeFromSpec (child));
|
|
|
|
|
return prerequisites;
|
2023-05-11 22:47:56 +02:00
|
|
|
}
|
2023-04-25 13:40:20 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-04-17 17:10:53 +02:00
|
|
|
|
2023-05-24 03:38:12 +02:00
|
|
|
|
2023-06-13 03:47:42 +02:00
|
|
|
/**
|
|
|
|
|
* This is some trickery to allow handling of multiple ModelPort(s) in MockDispatcher;
|
|
|
|
|
* actually the code using this mock setup does not need any elaborate differentiation
|
|
|
|
|
* of the ExitNodes structure per port, thus the first entry of the existing configuration
|
|
|
|
|
* is just duplicated for the given number of further ModelPorts.
|
|
|
|
|
* @warning this manipulation must be done prior to generating any JobTicket
|
|
|
|
|
*/
|
|
|
|
|
inline void
|
|
|
|
|
MockSegmentation::duplicateExitNodeSpec (uint times)
|
|
|
|
|
{
|
2023-06-13 20:23:33 +02:00
|
|
|
using Spec = fixture::NodeGraphAttachment;
|
|
|
|
|
|
|
|
|
|
Segmentation::adaptSpecification ([times](Spec const& spec)
|
|
|
|
|
{
|
2023-06-14 04:20:50 +02:00
|
|
|
return Spec{ExitNodes(times, spec[0])};
|
|
|
|
|
}); // vector with <times> copies of spec[0]
|
|
|
|
|
} // (Warning: use parens, not braces for this ctor...)
|
2023-06-13 03:47:42 +02:00
|
|
|
|
|
|
|
|
|
2023-04-30 02:18:56 +02:00
|
|
|
/**
|
|
|
|
|
* verify the given job instance was actually generated from this JobTicket.
|
2023-05-24 03:38:12 +02:00
|
|
|
* @remark this test support function relies on some specific rigging,
|
2023-04-30 02:18:56 +02:00
|
|
|
* which typically is prepared by setup of a MockJobTicket.
|
|
|
|
|
*/
|
|
|
|
|
inline bool
|
|
|
|
|
MockJobTicket::verify_associated (Job const& job) const
|
|
|
|
|
{
|
|
|
|
|
JobFunctor& functor = dynamic_cast<JobFunctor&> (static_cast<JobClosure&> (*job.jobClosure));
|
|
|
|
|
Time nominalTime {TimeValue{job.parameter.nominalTime}};
|
2023-05-01 14:07:21 +02:00
|
|
|
InvocationInstanceID const& invoKey = job.parameter.invoKey;
|
2023-04-30 02:18:56 +02:00
|
|
|
return this->isValid()
|
2023-05-01 14:07:21 +02:00
|
|
|
and this->verifyInstance(functor, invoKey, nominalTime);
|
2023-04-30 02:18:56 +02:00
|
|
|
}
|
2023-04-17 17:10:53 +02:00
|
|
|
|
2023-05-01 17:02:11 +02:00
|
|
|
/**
|
|
|
|
|
* convenience shortcut to perform [this test](\ref MockJobTicket::verify_associated)
|
|
|
|
|
* on arbitrary JobTicket and Job instances.
|
|
|
|
|
* @warning a positive test result however relies on some casting trickery and there is no
|
|
|
|
|
* guarantee this test works if the JobTicket was not created from this mock framework.
|
|
|
|
|
*/
|
|
|
|
|
inline bool
|
|
|
|
|
MockJobTicket::isAssociated (Job const& job, JobTicket const& ticket)
|
2023-05-24 03:38:12 +02:00
|
|
|
{ // should work always, since storage is the same
|
2023-05-01 17:02:11 +02:00
|
|
|
MockJobTicket const& backdoor = static_cast<MockJobTicket const&> (ticket);
|
|
|
|
|
return backdoor.verify_associated (job);
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-17 17:10:53 +02:00
|
|
|
|
2023-06-02 02:31:34 +02:00
|
|
|
|
2023-06-13 20:23:33 +02:00
|
|
|
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).
|
|
|
|
|
*/
|
2023-06-02 02:31:34 +02:00
|
|
|
class MockDispatcher
|
|
|
|
|
: public Dispatcher
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
DummyPlaybackSetup dummySetup_;
|
|
|
|
|
MockSegmentation mockSeg_;
|
|
|
|
|
|
2023-06-13 03:47:42 +02:00
|
|
|
using PortIdxMap = std::map<ModelPort, size_t>;
|
|
|
|
|
|
|
|
|
|
const PortIdxMap portIdx_;
|
2023-06-02 02:31:34 +02:00
|
|
|
|
2023-06-12 23:15:39 +02:00
|
|
|
public:
|
2023-06-13 20:23:33 +02:00
|
|
|
/* == mock implementation of the Dispatcher interface == */
|
2023-06-02 02:31:34 +02:00
|
|
|
|
2023-06-13 20:23:33 +02:00
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete
|
2023-06-02 02:31:34 +02:00
|
|
|
FrameCoord
|
2023-06-12 19:21:14 +02:00
|
|
|
locateRelative (FrameCoord const&, FrameCnt frameOffset) override
|
2023-06-02 02:31:34 +02:00
|
|
|
{
|
|
|
|
|
UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
2023-06-12 19:21:14 +02:00
|
|
|
isEndOfChunk (FrameCnt, ModelPort port) override
|
2023-06-02 02:31:34 +02:00
|
|
|
{
|
|
|
|
|
UNIMPLEMENTED ("determine when to finish a planning chunk");
|
|
|
|
|
}
|
2023-06-13 20:23:33 +02:00
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////TICKET #1276 : likely to become obsolete
|
2023-06-13 03:47:42 +02:00
|
|
|
|
2023-06-12 19:21:14 +02:00
|
|
|
size_t
|
|
|
|
|
resolveModelPort (ModelPort modelPort) override
|
|
|
|
|
{
|
2023-06-13 03:47:42 +02:00
|
|
|
auto entry = portIdx_.find(modelPort);
|
|
|
|
|
if (entry == portIdx_.end())
|
|
|
|
|
throw error::Logic{"Invalid ModelPort for this Dispatcher"};
|
|
|
|
|
else
|
|
|
|
|
return entry->second;
|
2023-06-12 19:21:14 +02:00
|
|
|
}
|
2023-06-13 03:47:42 +02:00
|
|
|
|
|
|
|
|
|
2023-06-02 02:31:34 +02:00
|
|
|
JobTicket&
|
2023-06-13 03:47:42 +02:00
|
|
|
accessJobTicket (size_t portIDX, TimeValue nominalTime) override
|
2023-06-02 02:31:34 +02:00
|
|
|
{
|
2023-06-13 03:47:42 +02:00
|
|
|
auto& seg = mockSeg_[nominalTime];
|
|
|
|
|
return seg.jobTicket(portIDX);
|
2023-06-02 02:31:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2023-06-13 20:23:33 +02:00
|
|
|
public:
|
2023-06-13 03:47:42 +02:00
|
|
|
MockDispatcher()
|
|
|
|
|
: dummySetup_{}
|
|
|
|
|
, mockSeg_{MakeRec().genNode()} // Node: generate a single active Segment to cover all
|
|
|
|
|
, portIdx_{buildPortIndex()}
|
|
|
|
|
{
|
|
|
|
|
mockSeg_.duplicateExitNodeSpec(portIdx_.size());
|
|
|
|
|
}
|
2023-06-02 02:31:34 +02:00
|
|
|
|
|
|
|
|
MockDispatcher (std::initializer_list<GenNode> specs)
|
2023-06-13 03:47:42 +02:00
|
|
|
: dummySetup_{}
|
|
|
|
|
, mockSeg_(specs)
|
|
|
|
|
, portIdx_{buildPortIndex()}
|
|
|
|
|
{
|
|
|
|
|
mockSeg_.duplicateExitNodeSpec(portIdx_.size());
|
|
|
|
|
}
|
2023-06-02 02:31:34 +02:00
|
|
|
|
2023-06-12 19:21:14 +02:00
|
|
|
|
2023-06-02 02:31:34 +02:00
|
|
|
ModelPort
|
|
|
|
|
provideMockModelPort()
|
|
|
|
|
{
|
|
|
|
|
ModelPorts mockModelPorts = dummySetup_.getAllModelPorts();
|
|
|
|
|
return *mockModelPorts; // using just the first dummy port
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The faked builder/playback setup provides some preconfigured ModelPort and
|
|
|
|
|
* corresponding DataSink handles. These are stored into a dummy registry and only available
|
|
|
|
|
* during the lifetime of the DummyPlaybackSetup instance.
|
|
|
|
|
* @param index number of the distinct port / connection
|
|
|
|
|
* @return a `std::pair<ModelPort,DataSink>`
|
|
|
|
|
* @warning as of 5/2023, there are two preconfigured "slots",
|
2023-06-13 03:47:42 +02:00
|
|
|
* and they are not usable in any way other then referring to their identity
|
2023-06-02 02:31:34 +02:00
|
|
|
*/
|
|
|
|
|
play::test::DummyOutputLink
|
|
|
|
|
getDummyConnection(uint index)
|
|
|
|
|
{
|
|
|
|
|
return dummySetup_.getModelPort (index);
|
|
|
|
|
}
|
2023-06-06 04:25:12 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Test support: verify the given Job is consistent with this Dispatcher.
|
|
|
|
|
*/
|
|
|
|
|
bool verify(Job const& job, ModelPort const& port, play::DataSink const& sink)
|
|
|
|
|
{
|
2023-06-13 03:47:42 +02:00
|
|
|
if (not dummySetup_.isSupported (port, sink)) return false;
|
|
|
|
|
|
|
|
|
|
TimeValue nominalTime{job.parameter.nominalTime};
|
|
|
|
|
size_t portIDX = resolveModelPort (port);
|
|
|
|
|
JobTicket& ticket = accessJobTicket (portIDX, nominalTime);
|
2023-06-13 20:23:33 +02:00
|
|
|
return isnil (ticket)? MockJob::isNopJob (job)
|
2023-06-13 03:47:42 +02:00
|
|
|
: MockJobTicket::isAssociated (job, ticket);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
PortIdxMap
|
|
|
|
|
buildPortIndex()
|
|
|
|
|
{
|
|
|
|
|
PortIdxMap newIndex;
|
|
|
|
|
uint i{0};
|
|
|
|
|
for (auto it=dummySetup_.getAllModelPorts()
|
|
|
|
|
; bool{it}
|
|
|
|
|
; ++it, ++i
|
|
|
|
|
)
|
|
|
|
|
newIndex[*it] = i;
|
|
|
|
|
|
|
|
|
|
return newIndex;
|
2023-06-06 04:25:12 +02:00
|
|
|
}
|
2023-06-02 02:31:34 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2023-04-17 17:10:53 +02:00
|
|
|
}}} // namespace steam::engine::test
|
2023-05-24 03:38:12 +02:00
|
|
|
#endif /*STEAM_ENGINE_TEST_MOCK_DISPATCHER_H*/
|