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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2017-02-22 01:54:20 +01:00
/** @file dispatcher-interface-test.cpp
2017-02-22 03:17:18 +01:00
* * unit test \ ref DispatcherInterface_test
2016-11-03 18:20:10 +01:00
*/
2012-02-04 22:20:21 +01:00
# include "lib/test/run.hpp"
# include "lib/error.hpp"
2018-11-15 23:42:43 +01:00
//#include "steam/engine/procnode.hpp"
# include "steam/play/dummy-play-connection.hpp"
# include "steam/mobject/model-port.hpp"
# include "steam/engine/dispatcher.hpp"
# include "steam/play/timings.hpp"
2012-02-05 00:25:22 +01:00
# include "lib/time/timevalue.hpp"
2013-01-13 18:09:18 +01:00
//#include "lib/time/timequant.hpp"
2016-01-07 03:58:29 +01:00
//#include "lib/format-cout.hpp"
2013-10-20 03:19:36 +02:00
# include "lib/depend.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
2014-04-03 22:42:48 +02:00
# include <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 ;
2014-04-03 22:42:48 +02:00
using std : : function ;
2012-02-04 22:20:21 +01:00
//using std::rand;
2018-11-15 23:55:13 +01:00
namespace steam {
2012-02-04 22:20:21 +01:00
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
2013-11-18 02:25:27 +01:00
locateRelative ( FrameCoord const & , FrameCnt frameOffset )
2012-10-10 04:35:56 +02:00
{
UNIMPLEMENTED ( " dummy implementation of the core dispatch operation " ) ;
}
2013-01-13 18:09:18 +01:00
2013-05-30 02:10:56 +02:00
bool
2013-11-18 00:01:43 +01:00
isEndOfChunk ( FrameCnt , ModelPort port )
2013-05-30 02:10:56 +02:00
{
UNIMPLEMENTED ( " determine when to finish a planning chunk " ) ;
}
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
} ;
2013-10-20 03:19:36 +02:00
lib : : Depend < MockDispatcherTable > mockDispatcher ;
2012-02-05 00:25:22 +01:00
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
2013-10-24 23:06:36 +02: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-08-18 03:16:49 +02:00
TimeAnchor refPoint ( 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 ) ;
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-08-18 03:16:49 +02:00
TimeAnchor refPoint = TimeAnchor ( 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-06-02 03:09:18 +02:00
lib : : append_all ( jobs , plannedChunk ) ;
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
2013-08-18 03:16:49 +02:00
# if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
2013-01-13 18:09:18 +01:00
TimeVar frameStart ( refPoint ) ;
C++17: fix detector for STL container iterability
the reason for the failure, as it turned out,
is that 'noexcept' is part of the function signature since C++17
And, since typically a STL container has const and non-const variants
of the begin() and end() function, the match to a member function pointer
became ambuguous, when probing with a signature without 'noexcept'
However, we deliberately want to support "any STL container like" types,
and this IMHO should include types with a possibly throwing iterator.
The rationale is, sometimes we want to expose some element *generator*
behind a container-like interface.
At this point I did an investigation if we can emulate something
in the way of a Concept -- i.e. rather than checking for the presence
of some functions on the interface, better try to cover the necessary
behaviour, like in a type class.
Unfortunately, while doable, this turns out to become quite technical;
and this highlights why the C++20 concepts are such an important addition
to the language.
So for the time being, we'll amend the existing solution
and look ahead to C++20
2020-02-21 18:45:51 +01:00
InvocationInstanceID prevInvocationID ( 0 ) ; ///////////////////////////////////////////////////////TICKET #1138 : C++17 requires explicit ctor for initialisation of union
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 ( ) ) ;
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 ( )
{
2013-06-02 03:09:18 +02:00
# if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #880
2012-04-20 04:05:41 +02:00
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 ) ;
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 " ) ;
2018-11-15 23:55:13 +01:00
} } } // namespace steam::engine::test