LUMIERA.clone/tests/core/proc/engine/dispatcher-interface-test.cpp
Ichthyostega 4ec7c11275 complete dispatcher test-case and interface definition
DispatcherInterface_test now passes the compiler,
meaning that the interfaces are completely defined,
all the generated types are OK and all operations are
at least stubbed.

Replacing all those stubs will be the next step
2013-01-13 18:09:18 +01:00

310 lines
12 KiB
C++

/*
DispatcherInterface(Test) - document and verify dispatcher for frame job creation
Copyright (C) Lumiera.org
2012, 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.
* *****************************************************/
#include "lib/test/run.hpp"
#include "lib/error.hpp"
//#include "proc/engine/procnode.hpp"
#include "proc/play/dummy-play-connection.hpp"
#include "proc/mobject/model-port.hpp"
#include "proc/engine/dispatcher.hpp"
#include "proc/play/timings.hpp"
#include "lib/time/timevalue.hpp"
//#include "lib/time/timequant.hpp"
#include "lib/singleton.hpp"
#include "lib/itertools.hpp"
#include "lib/util-coll.hpp"
#include "lib/util.hpp"
//#include <boost/scoped_ptr.hpp>
//#include <iostream>
#include <tr1/functional>
#include <vector>
using test::Test;
using util::isnil;
using util::last;
using std::vector;
using std::tr1::function;
//using std::cout;
//using std::rand;
namespace proc {
namespace engine{
namespace test {
using lib::time::FrameRate;
using lib::time::Duration;
using lib::time::Offset;
using lib::time::TimeVar;
using lib::time::Time;
using mobject::ModelPort;
using play::Timings;
namespace { // used internally
using play::PlayTestFrames_Strategy;
using play::ModelPorts;
typedef play::DummyPlayConnection<play::PlayTestFrames_Strategy> DummyPlaybackSetup;
class MockDispatcherTable
: public Dispatcher
{
DummyPlaybackSetup dummySetup_;
/* == mock Dispatcher implementation == */
FrameCoord
locateFrameNext (uint frameCountOffset, TimeAnchor refPoint)
{
UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
}
FrameCoord
locateRelative (FrameCoord, uint frameCountOffset)
{
UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
}
FrameCoord
locateRelative (TimeAnchor, uint frameCountOffset)
{
UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
}
JobTicket&
accessJobTicket (ModelPort, TimeValue nominalTime)
{
UNIMPLEMENTED ("dummy implementation of the model backbone / segmentation");
}
public:
ModelPort
provideMockModelPort()
{
ModelPorts mockModelPorts = dummySetup_.provide_testModelPorts();
return *mockModelPorts; // using just the first dummy port
}
};
lib::Singleton<MockDispatcherTable> mockDispatcher;
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #890
ModelPort
getTestPort()
{
return mockDispatcher().provideMockModelPort();
}
/* == test parameters == */
const uint START_FRAME(10);
const uint CHANNEL(0);
bool continuation_has_been_triggered = false;
} // (End) internal defs
/*******************************************************************
* @test document and verify the engine::Dispatcher interface, used
* to translate a CalcStream into individual node jobs.
* This test covers the definition of the interface itself,
* together with the supporting types and the default
* implementation of the basic operations.
* It creates and uses a mock Dispatcher implementation.
*/
class DispatcherInterface_test : public Test
{
virtual void
run (Arg)
{
verify_basicDispatch();
verify_standardDispatcherUsage();
check_ContinuationBuilder();
}
/** @test perform the basic dispatch step
* and verify the generated frame coordinates
*/
void
verify_basicDispatch()
{
Dispatcher& dispatcher = mockDispatcher();
Timings timings (FrameRate::PAL);
ModelPort modelPort (getTestPort());
ENSURE (START_FRAME == 10);
TimeAnchor refPoint = TimeAnchor::build (timings, START_FRAME);
CHECK (refPoint == Time::ZERO + Duration(10, FrameRate::PAL));
FrameCoord coordinates = dispatcher.onCalcStream (modelPort,CHANNEL)
.relativeFrameLocation (refPoint, 15);
CHECK (coordinates.absoluteNominalTime == Time(0,1));
CHECK (coordinates.absoluteFrameNumber == 25);
CHECK (refPoint.remainingRealTimeFor(coordinates) < Time(FSecs(25,25)));
CHECK (refPoint.remainingRealTimeFor(coordinates) >= Time(FSecs(24,25)));
CHECK (coordinates.modelPort == modelPort);
CHECK (coordinates.channelNr == CHANNEL);
JobTicket& executionPlan = dispatcher.getJobTicketFor (coordinates);
CHECK (executionPlan.isValid());
Job frameJob = executionPlan.createJobFor (coordinates);
CHECK (frameJob.getNominalTime() == coordinates.absoluteNominalTime);
CHECK (0 < frameJob.getInvocationInstanceID());
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
}
/** @test the standard invocation sequence
* used within the engine for planning new jobs.
* The actual implementation is mocked.
*/
void
verify_standardDispatcherUsage()
{
Dispatcher& dispatcher = mockDispatcher();
Timings timings (FrameRate::PAL);
ModelPort modelPort (getTestPort());
TimeAnchor refPoint = TimeAnchor::build (timings, START_FRAME);
JobPlanningSequence jobs = dispatcher.onCalcStream(modelPort,CHANNEL)
.establishNextJobs(refPoint);
// Verify the planned Jobs
CHECK (!isnil (jobs));
vector<Job> plannedChunk;
lib::append_all (jobs, plannedChunk);
Duration coveredTime (Offset(refPoint, last(plannedChunk).getNominalTime()));
CHECK (coveredTime >= timings.getPlanningChunkDuration());
TimeVar frameStart (refPoint);
InvocationInstanceID prevInvocationID(0);
Offset expectedTimeIncrement (1, FrameRate::PAL);
for (uint i=0; i < plannedChunk.size(); ++i )
{
Job& thisJob = plannedChunk[i];
CHECK (prevInvocationID < thisJob.getInvocationInstanceID());
prevInvocationID = thisJob.getInvocationInstanceID();
if (frameStart != thisJob.getNominalTime())
{
frameStart += expectedTimeIncrement;
CHECK (frameStart == thisJob.getNominalTime());
}
}
// now, after having passed over the whole planned chunk
CHECK (frameStart == Time(refPoint) + coveredTime);
CHECK (frameStart >= Time(refPoint) + timings.getPlanningChunkDuration());
CHECK (frameStart + expectedTimeIncrement > Time(refPoint) + timings.getPlanningChunkDuration());
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
}
/** @test usually at the end of each standard invocation,
* after scheduling a chunk of new Jobs, an additional
* continuation job is created to re-invoke this
* scheduling step.
* - the refPoint gets bumped beyond the planned segment
* - the continuation job embodies a suitable closure,
* usable for self-re-invocation
*/
void
check_ContinuationBuilder()
{
Dispatcher& dispatcher = mockDispatcher();
Timings timings (FrameRate::PAL);
ModelPort modelPort (getTestPort());
// prepare the rest of this test to be invoked as "continuation"
function<void(TimeAnchor)> testFunc = verify_invocation_of_Continuation;
TimeAnchor refPoint = TimeAnchor::build (timings, START_FRAME);
JobPlanningSequence jobs = dispatcher.onCalcStream(modelPort,CHANNEL)
.establishNextJobs(refPoint)
.prepareContinuation(testFunc);
// an additional "continuation" Job has been prepared....
Job continuation = lib::pull_last(jobs);
CHECK (META_JOB == continuation.getKind());
// the Continuation will be scheduled sufficiently ahead of the currently planned chunk's end
CHECK (continuation.getNominalTime() < Time(refPoint) + timings.getPlanningChunkDuration());
// now invoke the rest of this test, which has been embedded into the continuation job.
// Since we passed testFunc as action for the continuation, we expect the invocation
// of the function verify_invocation_of_Continuation()
continuation_has_been_triggered = false;
continuation.triggerJob();
CHECK (continuation_has_been_triggered);
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
}
/** action used as "continuation" in #check_ContinuationBuilder
* This function expects to be invoked with a time anchor bumped up
* to point exactly behind the end of the previously planned chunk of Jobs
*/
static void
verify_invocation_of_Continuation (TimeAnchor nextRefPoint)
{
Timings timings (FrameRate::PAL);
Duration frameDuration (1, FrameRate::PAL);
Time startAnchor = Time::ZERO + START_FRAME*frameDuration;
Duration time_to_cover = timings.getPlanningChunkDuration();
CHECK (Time(nextRefPoint) >= startAnchor + time_to_cover);
CHECK (Time(nextRefPoint) < startAnchor + time_to_cover + 1*frameDuration);
continuation_has_been_triggered = true;
}
};
/** Register this test class... */
LAUNCHER (DispatcherInterface_test, "unit engine");
}}} // namespace proc::engine::test