LUMIERA.clone/tests/core/proc/engine/dispatcher-interface-test.cpp
Ichthyostega 24b3bec4be Doxygen: prepare all unit tests for inclusion in the documentation
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
2017-02-22 01:54:20 +01:00

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