2012-02-04 22:20:21 +01:00
/*
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 0213 9 , USA .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# include "lib/test/run.hpp"
# include "lib/error.hpp"
//#include "proc/engine/procnode.hpp"
2012-02-13 00:37:57 +01:00
# include "proc/play/dummy-play-connection.hpp"
# include "proc/mobject/model-port.hpp"
2012-02-04 22:20:21 +01:00
# include "proc/engine/dispatcher.hpp"
2012-02-05 00:25:22 +01:00
# include "proc/play/timings.hpp"
# include "lib/time/timevalue.hpp"
2013-01-13 18:09:18 +01:00
//#include "lib/time/timequant.hpp"
2012-02-05 00:25:22 +01:00
# include "lib/singleton.hpp"
2012-04-18 03:46:21 +02:00
# include "lib/itertools.hpp"
2013-01-13 16:49:20 +01:00
# include "lib/util-coll.hpp"
2012-02-19 00:43:35 +01:00
# include "lib/util.hpp"
2012-02-04 22:20:21 +01:00
//#include <boost/scoped_ptr.hpp>
//#include <iostream>
2012-04-20 04:05:41 +02:00
# include <tr1/functional>
2012-04-18 03:46:21 +02:00
# include <vector>
2012-02-04 22:20:21 +01:00
using test : : Test ;
2012-02-19 00:43:35 +01:00
using util : : isnil ;
2013-01-13 16:49:20 +01:00
using util : : last ;
2012-04-18 03:46:21 +02:00
using std : : vector ;
2012-04-20 04:05:41 +02:00
using std : : tr1 : : function ;
2012-02-04 22:20:21 +01:00
//using std::cout;
//using std::rand;
namespace proc {
namespace engine {
namespace test {
2012-02-05 00:25:22 +01:00
using lib : : time : : FrameRate ;
2012-02-09 22:24:05 +01:00
using lib : : time : : Duration ;
2012-04-18 03:46:21 +02:00
using lib : : time : : Offset ;
using lib : : time : : TimeVar ;
2012-04-20 04:05:41 +02:00
using lib : : time : : Time ;
2012-02-13 00:37:57 +01:00
using mobject : : ModelPort ;
using play : : Timings ;
2012-02-05 00:25:22 +01:00
2012-02-04 22:20:21 +01:00
namespace { // used internally
2012-02-13 00:37:57 +01:00
using play : : PlayTestFrames_Strategy ;
using play : : ModelPorts ;
typedef play : : DummyPlayConnection < play : : PlayTestFrames_Strategy > DummyPlaybackSetup ;
2012-02-04 22:20:21 +01:00
class MockDispatcherTable
: public Dispatcher
{
2012-02-13 00:37:57 +01:00
DummyPlaybackSetup dummySetup_ ;
/* == mock Dispatcher implementation == */
2012-02-09 22:24:05 +01:00
FrameCoord
2012-02-24 00:29:59 +01:00
locateFrameNext ( uint frameCountOffset , TimeAnchor refPoint )
2012-02-09 22:24:05 +01:00
{
UNIMPLEMENTED ( " dummy implementation of the core dispatch operation " ) ;
}
2012-10-10 04:35:56 +02:00
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 " ) ;
}
2013-01-13 18:09:18 +01:00
2012-10-10 04:35:56 +02:00
JobTicket &
accessJobTicket ( ModelPort , TimeValue nominalTime )
{
UNIMPLEMENTED ( " dummy implementation of the model backbone / segmentation " ) ;
}
2013-01-13 18:09:18 +01:00
2012-02-04 22:20:21 +01:00
public :
2012-02-13 00:37:57 +01:00
ModelPort
provideMockModelPort ( )
{
ModelPorts mockModelPorts = dummySetup_ . provide_testModelPorts ( ) ;
return * mockModelPorts ; // using just the first dummy port
}
2012-02-04 22:20:21 +01:00
} ;
2012-02-05 00:25:22 +01:00
lib : : Singleton < MockDispatcherTable > mockDispatcher ;
2012-02-04 22:20:21 +01:00
# if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
# endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #890
2012-02-13 00:37:57 +01:00
ModelPort
getTestPort ( )
{
return mockDispatcher ( ) . provideMockModelPort ( ) ;
}
2013-01-13 18:09:18 +01:00
/* == test parameters == */
const uint START_FRAME ( 10 ) ;
const uint CHANNEL ( 0 ) ;
bool continuation_has_been_triggered = false ;
2012-02-13 00:37:57 +01:00
2012-02-04 22:20:21 +01:00
} // (End) internal defs
2013-01-13 18:09:18 +01:00
2012-02-04 22:20:21 +01:00
/*******************************************************************
* @ 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 ( ) ;
2012-04-20 04:05:41 +02:00
check_ContinuationBuilder ( ) ;
2012-02-04 22:20:21 +01:00
}
/** @test perform the basic dispatch step
2012-02-18 00:49:53 +01:00
* and verify the generated frame coordinates
2012-02-04 22:20:21 +01:00
*/
void
verify_basicDispatch ( )
{
2012-02-05 00:25:22 +01:00
Dispatcher & dispatcher = mockDispatcher ( ) ;
2012-02-13 00:37:57 +01:00
ModelPort modelPort ( getTestPort ( ) ) ;
2013-02-11 03:19:24 +01:00
Timings timings ( FrameRate : : PAL ) ;
2013-01-13 18:09:18 +01:00
ENSURE ( START_FRAME = = 10 ) ;
2012-02-05 00:25:22 +01:00
2013-01-13 18:09:18 +01:00
TimeAnchor refPoint = TimeAnchor : : build ( timings , START_FRAME ) ;
2012-02-09 22:24:05 +01:00
CHECK ( refPoint = = Time : : ZERO + Duration ( 10 , FrameRate : : PAL ) ) ;
2013-01-13 18:09:18 +01:00
FrameCoord coordinates = dispatcher . onCalcStream ( modelPort , CHANNEL )
2012-02-24 00:29:59 +01:00
. relativeFrameLocation ( refPoint , 15 ) ;
2012-02-09 22:24:05 +01:00
CHECK ( coordinates . absoluteNominalTime = = Time ( 0 , 1 ) ) ;
CHECK ( coordinates . absoluteFrameNumber = = 25 ) ;
2013-01-11 18:12:40 +01:00
CHECK ( refPoint . remainingRealTimeFor ( coordinates ) < Time ( FSecs ( 25 , 25 ) ) ) ;
CHECK ( refPoint . remainingRealTimeFor ( coordinates ) > = Time ( FSecs ( 24 , 25 ) ) ) ;
2012-02-18 00:49:53 +01:00
CHECK ( coordinates . modelPort = = modelPort ) ;
2013-01-13 18:09:18 +01:00
CHECK ( coordinates . channelNr = = CHANNEL ) ;
2012-02-13 00:37:57 +01:00
2012-10-10 04:35:56 +02:00
JobTicket & executionPlan = dispatcher . getJobTicketFor ( coordinates ) ;
2012-02-13 00:37:57 +01:00
CHECK ( executionPlan . isValid ( ) ) ;
2012-02-18 00:49:53 +01:00
2013-01-13 18:09:18 +01:00
Job frameJob = executionPlan . createJobFor ( coordinates ) ;
2012-04-18 03:46:21 +02:00
CHECK ( frameJob . getNominalTime ( ) = = coordinates . absoluteNominalTime ) ;
CHECK ( 0 < frameJob . getInvocationInstanceID ( ) ) ;
2012-04-26 04:11:31 +02:00
# if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
2012-02-05 00:25:22 +01:00
# endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
2012-02-04 22:20:21 +01:00
}
2013-01-13 18:09:18 +01:00
2012-02-04 22:20:21 +01:00
/** @test the standard invocation sequence
* used within the engine for planning new jobs .
* The actual implementation is mocked .
*/
void
verify_standardDispatcherUsage ( )
{
2012-04-18 03:46:21 +02:00
Dispatcher & dispatcher = mockDispatcher ( ) ;
ModelPort modelPort ( getTestPort ( ) ) ;
2013-02-11 03:19:24 +01:00
Timings timings ( FrameRate : : PAL ) ;
2012-04-18 03:46:21 +02:00
2013-01-13 18:09:18 +01:00
TimeAnchor refPoint = TimeAnchor : : build ( timings , START_FRAME ) ;
2012-02-04 22:20:21 +01:00
2013-01-13 18:09:18 +01:00
JobPlanningSequence jobs = dispatcher . onCalcStream ( modelPort , CHANNEL )
2012-07-01 03:42:50 +02:00
. establishNextJobs ( refPoint ) ;
2012-04-18 03:46:21 +02:00
// Verify the planned Jobs
CHECK ( ! isnil ( jobs ) ) ;
vector < Job > plannedChunk ;
2013-02-11 03:19:24 +01:00
lib : : append_all ( jobs , plannedChunk ) ; //////////////////////////TODO probably can't do it this way; rather the JobPlanningSequence is infinite and only partially evaluated
2012-04-18 03:46:21 +02:00
2013-01-13 16:49:20 +01:00
Duration coveredTime ( Offset ( refPoint , last ( plannedChunk ) . getNominalTime ( ) ) ) ;
2013-01-12 08:36:35 +01:00
CHECK ( coveredTime > = timings . getPlanningChunkDuration ( ) ) ;
2012-04-18 03:46:21 +02:00
2013-03-17 03:14:05 +01:00
///TODO nachfolgendes muß komplett umgeschrieben werden
///TODO definieren, wie das scheduler-interface angesprochen wird
///TODO dann stub dafür bauen
///TODO Idee/Frage: kann man nach den Prerequisites nochmal zum Job *zurückkehren* ?
/////////////////// Antwort: nein man kann nicht.
2013-01-13 18:09:18 +01:00
TimeVar frameStart ( refPoint ) ;
2012-04-26 04:11:31 +02:00
InvocationInstanceID prevInvocationID ( 0 ) ;
2012-04-18 03:46:21 +02:00
Offset expectedTimeIncrement ( 1 , FrameRate : : PAL ) ;
2013-01-13 18:09:18 +01:00
for ( uint i = 0 ; i < plannedChunk . size ( ) ; + + i )
2012-04-18 03:46:21 +02:00
{
Job & thisJob = plannedChunk [ i ] ;
CHECK ( prevInvocationID < thisJob . getInvocationInstanceID ( ) ) ;
prevInvocationID = thisJob . getInvocationInstanceID ( ) ;
2013-01-13 18:09:18 +01:00
if ( frameStart ! = thisJob . getNominalTime ( ) )
{
frameStart + = expectedTimeIncrement ;
CHECK ( frameStart = = thisJob . getNominalTime ( ) ) ;
}
2012-04-18 03:46:21 +02:00
}
2013-01-13 18:09:18 +01:00
// 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
2012-04-20 04:05:41 +02:00
# endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
}
2013-01-13 18:09:18 +01:00
2012-04-20 04:05:41 +02:00
/** @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 ( ) ;
ModelPort modelPort ( getTestPort ( ) ) ;
2013-02-11 03:19:24 +01:00
Timings timings ( FrameRate : : PAL ) ;
2012-04-20 04:05:41 +02:00
// prepare the rest of this test to be invoked as "continuation"
function < void ( TimeAnchor ) > testFunc = verify_invocation_of_Continuation ;
2012-04-18 03:46:21 +02:00
2013-01-13 18:09:18 +01:00
TimeAnchor refPoint = TimeAnchor : : build ( timings , START_FRAME ) ;
JobPlanningSequence jobs = dispatcher . onCalcStream ( modelPort , CHANNEL )
2012-07-01 03:42:50 +02:00
. establishNextJobs ( refPoint )
. prepareContinuation ( testFunc ) ;
2012-04-20 04:05:41 +02:00
// an additional "continuation" Job has been prepared....
Job continuation = lib : : pull_last ( jobs ) ;
2012-04-26 04:11:31 +02:00
CHECK ( META_JOB = = continuation . getKind ( ) ) ;
2012-04-18 03:46:21 +02:00
2012-04-27 18:59:08 +02:00
// the Continuation will be scheduled sufficiently ahead of the currently planned chunk's end
2013-01-13 18:09:18 +01:00
CHECK ( continuation . getNominalTime ( ) < Time ( refPoint ) + timings . getPlanningChunkDuration ( ) ) ;
2012-04-20 04:05:41 +02:00
// 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()
2013-01-13 18:09:18 +01:00
continuation_has_been_triggered = false ;
2012-04-20 04:05:41 +02:00
continuation . triggerJob ( ) ;
2013-01-13 18:09:18 +01:00
CHECK ( continuation_has_been_triggered ) ;
# if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
2012-02-05 00:25:22 +01:00
# endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
2012-02-04 22:20:21 +01:00
}
2012-04-20 04:05:41 +02:00
/** 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 ) ;
2013-01-13 18:09:18 +01:00
Time startAnchor = Time : : ZERO + START_FRAME * frameDuration ;
Duration time_to_cover = timings . getPlanningChunkDuration ( ) ;
2012-04-20 04:05:41 +02:00
2013-01-13 18:09:18 +01:00
CHECK ( Time ( nextRefPoint ) > = startAnchor + time_to_cover ) ;
CHECK ( Time ( nextRefPoint ) < startAnchor + time_to_cover + 1 * frameDuration ) ;
continuation_has_been_triggered = true ;
2012-04-20 04:05:41 +02:00
}
2012-02-04 22:20:21 +01:00
} ;
/** Register this test class... */
LAUNCHER ( DispatcherInterface_test , " unit engine " ) ;
} } } // namespace proc::engine::test