2011-07-09 02:44:24 +02:00
/*
DIAGNOSTIC - OUTPUT - SLOT . hpp - helper for testing against the OutputSlot interface
Copyright ( C ) Lumiera . org
2011 , 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 .
*/
/** @file diagnostic-output-slot.hpp
* * An facility for writing unit - tests against the OutputSlot interface .
* *
* * @ see output - slot - protocol - test . cpp
*/
# ifndef PROC_PLAY_DIAGNOSTIC_OUTPUT_SLOT_H
# define PROC_PLAY_DIAGNOSTIC_OUTPUT_SLOT_H
# include "lib/error.hpp"
2011-11-07 01:57:33 +01:00
# include "include/logging.h"
2011-07-09 02:44:24 +02:00
# include "proc/play/output-slot.hpp"
2011-11-07 01:57:33 +01:00
# include "proc/play/output-slot-connection.hpp"
2011-12-09 01:03:02 +01:00
# include "proc/asset/meta/time-grid.hpp"
2011-11-07 01:57:33 +01:00
# include "proc/engine/buffhandle.hpp"
# include "proc/engine/tracking-heap-block-provider.hpp"
2011-12-09 01:03:02 +01:00
# include "lib/time/timevalue.hpp"
2011-12-08 23:02:19 +01:00
# include "lib/scoped-ptrvect.hpp"
# include "lib/iter-source.hpp"
2011-12-09 01:03:02 +01:00
# include "lib/util.hpp"
2011-10-19 02:47:11 +02:00
# include "proc/engine/testframe.hpp"
2011-07-09 02:44:24 +02:00
//#include "lib/sync.hpp"
2011-11-07 01:57:33 +01:00
# include <boost/noncopyable.hpp>
2011-07-09 02:44:24 +02:00
//#include <string>
//#include <vector>
2011-12-09 01:03:02 +01:00
# include <tr1/unordered_set>
2011-11-07 01:57:33 +01:00
# include <tr1/memory>
2011-07-09 02:44:24 +02:00
//#include <boost/scoped_ptr.hpp>
namespace proc {
namespace play {
//using std::string;
2011-12-09 01:03:02 +01:00
using util : : contains ;
using lib : : time : : FrameRate ;
using proc : : asset : : meta : : PGrid ;
using proc : : asset : : meta : : TimeGrid ;
2011-12-02 17:50:44 +01:00
using proc : : engine : : BufferDescriptor ;
using proc : : engine : : test : : TestFrame ;
using proc : : engine : : TrackingHeapBlockProvider ;
2011-12-08 23:02:19 +01:00
namespace diagn = proc : : engine : : diagn ;
2011-07-09 02:44:24 +02:00
//using std::vector;
2011-11-07 01:57:33 +01:00
using std : : tr1 : : shared_ptr ;
2011-07-09 02:44:24 +02:00
//using boost::scoped_ptr;
2011-12-09 01:03:02 +01:00
/**
* Diagnostic output connection for a single channel ,
* allowing to track generated frames and verify
* the processing protocol for output buffers .
*/
2011-11-07 01:57:33 +01:00
class TrackingInMemoryBlockSequence
: public OutputSlot : : Connection
{
2011-12-09 01:03:02 +01:00
typedef std : : tr1 : : unordered_set < FrameID > FrameTrackingInfo ;
2011-11-11 23:33:59 +01:00
shared_ptr < TrackingHeapBlockProvider > buffProvider_ ;
2011-11-07 01:57:33 +01:00
BufferDescriptor bufferType_ ;
2011-12-09 01:03:02 +01:00
FrameTrackingInfo frameTrackingIndex_ ;
PGrid frameGrid_ ;
BuffHandle
trackFrame ( FrameID frameNr , BuffHandle const & newBuffer )
{
REQUIRE ( contains ( frameTrackingIndex_ , frameNr ) ,
" attempt to lock already used frame %lu " , frameNr ) ;
frameTrackingIndex_ . insert ( frameNr ) ;
return newBuffer ;
}
TimeValue
deadlineFor ( FrameID frameNr )
{
return frameGrid_ - > timeOf ( frameNr ) ;
}
2011-11-07 01:57:33 +01:00
/* === Connection API === */
2011-11-08 02:59:56 +01:00
BuffHandle
claimBufferFor ( FrameID frameNr )
2011-11-07 01:57:33 +01:00
{
2011-12-09 01:03:02 +01:00
return trackFrame ( frameNr ,
buffProvider_ - > lockBuffer ( bufferType_ ) ) ;
2011-11-08 02:59:56 +01:00
}
bool
isTimely ( FrameID frameNr , TimeValue currentTime )
{
2011-12-09 01:03:02 +01:00
if ( Time : : ANYTIME = = currentTime )
2011-11-08 02:59:56 +01:00
return true ;
2011-12-09 01:03:02 +01:00
return currentTime < deadlineFor ( frameNr ) ;
2011-11-08 02:59:56 +01:00
}
void
transfer ( BuffHandle const & filledBuffer )
{
pushout ( filledBuffer ) ;
2011-11-07 01:57:33 +01:00
}
void
2011-11-08 02:59:56 +01:00
pushout ( BuffHandle const & data4output )
2011-11-07 01:57:33 +01:00
{
2011-11-15 04:47:31 +01:00
buffProvider_ - > emitBuffer ( data4output ) ;
buffProvider_ - > releaseBuffer ( data4output ) ;
2011-11-07 01:57:33 +01:00
}
void
2011-11-08 02:59:56 +01:00
discard ( BuffHandle const & superseededData )
2011-11-07 01:57:33 +01:00
{
2011-11-08 02:59:56 +01:00
buffProvider_ - > releaseBuffer ( superseededData ) ;
2011-11-07 01:57:33 +01:00
}
2011-11-08 02:59:56 +01:00
void
shutDown ( )
{
buffProvider_ . reset ( ) ;
}
2011-11-07 01:57:33 +01:00
public :
TrackingInMemoryBlockSequence ( )
: buffProvider_ ( new TrackingHeapBlockProvider ( ) )
, bufferType_ ( buffProvider_ - > getDescriptor < TestFrame > ( ) )
2011-12-09 01:03:02 +01:00
, frameTrackingIndex_ ( )
, frameGrid_ ( TimeGrid : : build ( " DiagnosticOutputSlot-buffer-grid " , FrameRate : : PAL ) ) /////////////TODO should rather pass that in as part of a "timings" definition
2011-11-07 01:57:33 +01:00
{
INFO ( engine_dbg , " building in-memory diagnostic output sequence " ) ;
}
virtual
~ TrackingInMemoryBlockSequence ( )
{
INFO ( engine_dbg , " releasing diagnostic output sequence " ) ;
}
2011-12-08 23:02:19 +01:00
/* === Diagnostic API === */
TestFrame *
2011-12-09 01:03:02 +01:00
accessEmittedFrame ( uint frameNr ) const
2011-12-08 23:02:19 +01:00
{
REQUIRE ( buffProvider_ ) ;
2011-12-09 01:03:02 +01:00
if ( frameNr < = buffProvider_ - > emittedCnt ( ) )
return & buffProvider_ - > accessAs < TestFrame > ( frameNr ) ;
2011-12-08 23:02:19 +01:00
else
return 0 ; ////////////////////////////////TICKET #856
}
diagn : : Block *
2011-12-09 01:03:02 +01:00
accessEmittedBuffer ( uint bufferNr ) const
2011-12-08 23:02:19 +01:00
{
REQUIRE ( buffProvider_ ) ;
2011-12-09 01:03:02 +01:00
if ( bufferNr < = buffProvider_ - > emittedCnt ( ) )
return & buffProvider_ - > access_emitted ( bufferNr ) ;
2011-12-08 23:02:19 +01:00
else
return 0 ;
}
2011-12-09 01:03:02 +01:00
bool
wasAllocated ( uint frameNr ) const
{
REQUIRE ( buffProvider_ ) ;
return contains ( frameTrackingIndex_ , frameNr ) ;
}
2011-11-07 01:57:33 +01:00
} ;
2011-12-09 01:03:02 +01:00
/**
* Special diagnostic connection state implementation ,
* establishing diagnostic output connections for each channel ,
* thus allowing to verify the handling of individual buffers
*/
2011-11-07 01:57:33 +01:00
class SimulatedOutputSequences
: public ConnectionStateManager < TrackingInMemoryBlockSequence >
{
TrackingInMemoryBlockSequence
buildConnection ( )
{
return TrackingInMemoryBlockSequence ( ) ;
}
2011-11-08 02:59:56 +01:00
public :
SimulatedOutputSequences ( uint numChannels )
{
init ( numChannels ) ;
}
2011-11-07 01:57:33 +01:00
} ;
2011-07-09 02:44:24 +02:00
/********************************************************************
* Helper for unit tests : Mock output sink .
2011-12-23 02:22:38 +01:00
* Complete implementation of the OutputSlot interface , with some
* additional stipulations to support unit testing .
* - the implementation uses a special protocol output buffer ,
* which stores each " frame " in memory for later investigation
* - the output data in the buffers handed over from client
* actually hold an TestFrame instance
* - the maximum number of channels and the maximum number
* of acceptable frames is limited to 5 and 100.
* @ warning any Captured ( test ) data from all individual instances
* remains in memory until shutdown of the current executable
2011-07-09 02:44:24 +02:00
*/
class DiagnosticOutputSlot
: public OutputSlot
{
2011-11-08 02:59:56 +01:00
static const uint MAX_CHANNELS = 5 ;
2011-12-23 02:22:38 +01:00
/** @note a real OutputSlot implementation
* would rely on some kind of embedded
* configuration here */
uint
getOutputChannelCount ( )
{
return MAX_CHANNELS ;
}
2011-12-08 23:02:19 +01:00
/** hook into the OutputSlot frontend */
2011-11-07 01:57:33 +01:00
ConnectionState *
buildState ( )
{
2011-12-23 02:22:38 +01:00
return new SimulatedOutputSequences (
getOutputChannelCount ( ) ) ;
2011-11-07 01:57:33 +01:00
}
2011-12-08 23:02:19 +01:00
/** @internal is self-managed and non-copyable.
* Clients use # build ( ) to get an instance */
DiagnosticOutputSlot ( ) { }
/** @internal access the implementation object
* representing a single stream connection
*/
TrackingInMemoryBlockSequence const &
accessSequence ( uint channel )
{
REQUIRE ( ! isFree ( ) , " diagnostic OutputSlot not (yet) connected " ) ;
2011-12-23 02:22:38 +01:00
REQUIRE ( channel < = getOutputChannelCount ( ) ) ;
2011-12-08 23:02:19 +01:00
return static_cast < SimulatedOutputSequences & > ( * state_ ) . at ( channel ) ;
}
2011-11-07 01:57:33 +01:00
2011-07-09 02:44:24 +02:00
public :
2011-08-18 12:31:26 +02:00
/** build a new Diagnostic Output Slot instance,
* discard the existing one . Use the static query API
* for investigating collected data . */
static OutputSlot &
build ( )
{
2011-12-08 23:02:19 +01:00
static lib : : ScopedPtrVect < OutputSlot > diagnosticSlots ;
return diagnosticSlots . manage ( new DiagnosticOutputSlot ) ;
2011-08-18 12:31:26 +02:00
}
2011-07-09 02:44:24 +02:00
2011-10-19 02:47:11 +02:00
static DiagnosticOutputSlot &
access ( OutputSlot & to_investigate )
{
2011-12-08 23:02:19 +01:00
return dynamic_cast < DiagnosticOutputSlot & > ( to_investigate ) ;
2011-10-19 02:47:11 +02:00
}
2011-12-08 23:02:19 +01:00
2011-10-19 02:47:11 +02:00
/* === diagnostics API === */
/**
2011-12-08 23:02:19 +01:00
* diagnostic facility to verify test data frames
* written to this Test / Dummy " output " . It exposes
* the emitted Data as a sequence of TestFrame objects .
2011-10-19 02:47:11 +02:00
*/
2011-12-08 23:02:19 +01:00
class OutputFramesLog
: public lib : : IterSource < TestFrame >
, boost : : noncopyable
2011-10-19 02:47:11 +02:00
{
2011-12-08 23:02:19 +01:00
TrackingInMemoryBlockSequence const & outSeq_ ;
uint currentFrame_ ;
virtual Pos
firstResult ( )
{
REQUIRE ( 0 = = currentFrame_ ) ;
return outSeq_ . accessEmittedFrame ( currentFrame_ ) ;
}
virtual void
nextResult ( Pos & pos )
{
+ + currentFrame_ ;
pos = outSeq_ . accessEmittedFrame ( currentFrame_ ) ;
}
public :
OutputFramesLog ( TrackingInMemoryBlockSequence const & bs )
: outSeq_ ( bs )
, currentFrame_ ( 0 )
{ }
2011-10-19 02:47:11 +02:00
} ;
2011-12-08 23:02:19 +01:00
typedef OutputFramesLog : : iterator OutFrames ;
2011-10-19 02:47:11 +02:00
OutFrames
getChannel ( uint channel )
{
2011-12-08 23:02:19 +01:00
REQUIRE ( channel < MAX_CHANNELS ) ;
return OutputFramesLog : : build (
new OutputFramesLog (
accessSequence ( channel ) ) ) ;
2011-10-19 02:47:11 +02:00
}
bool
2011-11-06 02:37:22 +01:00
buffer_was_used ( uint channel , FrameID frame )
2011-10-19 02:47:11 +02:00
{
2011-12-09 01:03:02 +01:00
return accessSequence ( channel )
. wasAllocated ( frame ) ;
2011-10-19 02:47:11 +02:00
}
bool
2011-11-06 02:37:22 +01:00
buffer_unused ( uint channel , FrameID frame )
2011-10-19 02:47:11 +02:00
{
2011-12-08 23:02:19 +01:00
return ! buffer_was_used ( channel , frame ) ;
2011-10-19 02:47:11 +02:00
}
bool
2011-11-06 02:37:22 +01:00
buffer_was_closed ( uint channel , FrameID frame )
2011-10-19 02:47:11 +02:00
{
2011-12-09 01:03:02 +01:00
diagn : : Block * block = accessSequence ( channel )
. accessEmittedBuffer ( frame ) ;
return block
& & block - > was_closed ( ) ;
2011-10-19 02:47:11 +02:00
}
bool
2011-11-06 02:37:22 +01:00
emitted ( uint channel , FrameID frame )
2011-10-19 02:47:11 +02:00
{
2011-12-09 01:03:02 +01:00
diagn : : Block * block = accessSequence ( channel )
. accessEmittedBuffer ( frame ) ;
return block
& & block - > was_used ( ) ;
2011-10-19 02:47:11 +02:00
}
2011-07-09 02:44:24 +02:00
private :
} ;
} } // namespace proc::play
# endif