Doxygen will only process files with a @file documentation comment. Up to now, none of our test code has such a comment, preventing the cross-links to unit tests from working. This is unfortunate, since unit tests, and even the code comments there, can be considered as the most useful form of technical documentation. Thus I'll start an initiative to fill in those missing comments automatically
312 lines
12 KiB
C++
312 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.
|
|
|
|
* *****************************************************/
|
|
|
|
/** @file dispatcher-interface-test.cpp
|
|
** unit test §§TODO§§
|
|
*/
|
|
|
|
|
|
#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/format-cout.hpp"
|
|
#include "lib/depend.hpp"
|
|
#include "lib/itertools.hpp"
|
|
#include "lib/util-coll.hpp"
|
|
#include "lib/util.hpp"
|
|
|
|
//#include <boost/scoped_ptr.hpp>
|
|
#include <functional>
|
|
#include <vector>
|
|
|
|
using test::Test;
|
|
using util::isnil;
|
|
using util::last;
|
|
using std::vector;
|
|
using std::function;
|
|
//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
|
|
locateRelative (FrameCoord const&, FrameCnt frameOffset)
|
|
{
|
|
UNIMPLEMENTED ("dummy implementation of the core dispatch operation");
|
|
}
|
|
|
|
bool
|
|
isEndOfChunk (FrameCnt, ModelPort port)
|
|
{
|
|
UNIMPLEMENTED ("determine when to finish a planning chunk");
|
|
}
|
|
|
|
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::Depend<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();
|
|
ModelPort modelPort (getTestPort());
|
|
Timings timings (FrameRate::PAL);
|
|
ENSURE (START_FRAME == 10);
|
|
|
|
TimeAnchor refPoint(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);
|
|
#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();
|
|
ModelPort modelPort (getTestPort());
|
|
Timings timings (FrameRate::PAL);
|
|
|
|
TimeAnchor refPoint = TimeAnchor(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());
|
|
|
|
///TODO nachfolgendes muß komplett umgeschrieben werden
|
|
///TODO definieren, wie das scheduler-interface angesprochen wird
|
|
///TODO dann stub dafür bauen
|
|
|
|
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
|
|
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());
|
|
#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()
|
|
{
|
|
#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
|
|
Dispatcher& dispatcher = mockDispatcher();
|
|
ModelPort modelPort (getTestPort());
|
|
Timings timings (FrameRate::PAL);
|
|
|
|
// 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);
|
|
#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
|