Merge Fixture datastructure and Testsuite work

This commit is contained in:
Fischlurch 2010-12-17 22:51:27 +01:00
commit 4410830f72
78 changed files with 10392 additions and 1401 deletions

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 217 KiB

File diff suppressed because it is too large Load diff

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View file

@ -132,6 +132,12 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( proc, progress);
NOBUG_CPP_DEFINE_FLAG_PARENT ( command, proc);
/** progress log for session datastructure */
NOBUG_CPP_DEFINE_FLAG_PARENT ( session, proc);
/** progress log for the builder and build process */
NOBUG_CPP_DEFINE_FLAG_PARENT ( builder, proc);
/** progress log for running the engine */
NOBUG_CPP_DEFINE_FLAG_PARENT ( engine, proc);
/** progress log for play- and render subsystem */
NOBUG_CPP_DEFINE_FLAG_PARENT ( play, proc);
/** progress log for the gui */
NOBUG_CPP_DEFINE_FLAG_PARENT ( gui, progress);
/** progress log for the support lib */
@ -158,13 +164,13 @@ NOBUG_CPP_DEFINE_FLAG_PARENT ( config, logging);
/** base flag for software testing */
NOBUG_CPP_DEFINE_FLAG_PARENT ( test, logging);
/** base flag for syncronization logging */
/** base flag for synchronisation logging */
NOBUG_CPP_DEFINE_FLAG_PARENT ( sync, logging); // do we need subsections here? backend_mutex_sync proc_mutex_sync etc?
NOBUG_CPP_DEFINE_FLAG_PARENT ( mutex_sync, sync); //locking/unlocking mutexes
NOBUG_CPP_DEFINE_FLAG_PARENT ( cond_sync, sync); //waiting and signalling condition vars
/** base flag for memory related logging */
NOBUG_CPP_DEFINE_FLAG_PARENT ( memory, logging);
/** memory busines of the proc layer */
/** proc layer memory handling */
NOBUG_CPP_DEFINE_FLAG_PARENT ( proc_mem, memory);
NOBUG_CPP_DEFINE_FLAG_PARENT ( mobject_mem, proc_mem);
NOBUG_CPP_DEFINE_FLAG_PARENT ( builder_mem, proc_mem);

View file

@ -54,7 +54,7 @@
#include "lib/error.hpp"
#include "lib/sync-classlock.hpp"
#include "lib/scoped-holder.hpp"
#include "lib/scopedholdertransfer.hpp"
#include "lib/scoped-holder-transfer.hpp"

View file

@ -28,7 +28,7 @@
#include <string>
#include <typeinfo>
#include <boost/format.hpp>
#include <boost/format.hpp> /////////////////////////////////////////TICKET #166 Oh RLY ... need to do away with this
#include "lib/symbol.hpp"
@ -64,7 +64,7 @@ namespace lumiera {
{
public:
explicit Query (string const& predicate="") : string(predicate) {}
explicit Query (format& pattern) : string(str(pattern)) {}
// explicit Query (format& pattern) : string(str(pattern)) {} //////////////TICKET #166 outch... that needs to disappear
const string asKey() const
{

View file

@ -1,5 +1,5 @@
/*
SCOPEDHOLDERVECTOR.hpp - using ScopedHolder within a STL vector
SCOPED-HOLDER-TRANSFER.hpp - using ScopedHolder within a STL vector
Copyright (C) Lumiera.org
2008, Hermann Vosseler <Ichthyostega@web.de>
@ -22,8 +22,8 @@
#ifndef LIB_SCOPEDHOLDERVECTOR_H
#define LIB_SCOPEDHOLDERVECTOR_H
#ifndef LIB_SCOPEDHOLDER_TRANSFER_H
#define LIB_SCOPEDHOLDER_TRANSFER_H
#include "lib/error.hpp"
#include <memory>

View file

@ -47,7 +47,7 @@
** friend function.
**
** @see scoped-holder-test.cpp
** @see scopedholdertransfer.hpp use in std::vector
** @see scoped-holder-transfer.hpp use in std::vector
** @see AllocationCluster usage example
** @see scoped-ptrvect.hpp simple pointer-holding collection
*/

View file

@ -171,7 +171,7 @@ namespace lib {
private:
/** @internal element access, including null check */
/** @internal element access, including range and null check */
T*
get (size_type i)
{

View file

@ -37,6 +37,7 @@
#include "lib/symbol.hpp"
#include "lib/query.hpp"
#include "proc/asset/entry-id.hpp"
#include <boost/noncopyable.hpp>
@ -47,7 +48,7 @@ namespace lumiera {
/**
*
* TODO write type comment
*/
struct StreamType : boost::noncopyable
{
@ -72,6 +73,8 @@ namespace lumiera {
class ImplFacade;
class ImplConstraint;
typedef asset::EntryID<StreamType> ID;
Prototype const& prototype;
ImplFacade * implType; /////////////TODO: really by ptr???
@ -149,9 +152,9 @@ namespace lumiera {
* and use it to create a new framebuffer */
virtual DataBuffer* createFrame () const =0;
/** similarily create a impl type which complies to this constraint
/** Similarly create a impl type which complies to this constraint
* as well as to the additional constraints (e.g. frame size).
* Create a new framebuffer of the resutling type */
* Create a new frame buffer of the resulting type */
virtual DataBuffer* createFrame (ImplConstraint const& furtherConstraints) const =0;
//TODO: do we need functions to represent and describe this constraint?

View file

@ -109,7 +109,8 @@ liblumiprocmobjectbuilder_la_SOURCES = \
$(liblumiprocmobjectbuilder_la_srcdir)/assembler.cpp \
$(liblumiprocmobjectbuilder_la_srcdir)/conmanager.cpp \
$(liblumiprocmobjectbuilder_la_srcdir)/nodecreatortool.cpp \
$(liblumiprocmobjectbuilder_la_srcdir)/segmentationtool.cpp \
$(liblumiprocmobjectbuilder_la_srcdir)/segmentation.cpp \
$(liblumiprocmobjectbuilder_la_srcdir)/segmentation-tool.cpp \
$(liblumiprocmobjectbuilder_la_srcdir)/toolfactory.cpp
@ -219,7 +220,8 @@ noinst_HEADERS += \
$(liblumiproc_la_srcdir)/mobject/builder/assembler.hpp \
$(liblumiproc_la_srcdir)/mobject/builder/buildertool.hpp \
$(liblumiproc_la_srcdir)/mobject/builder/conmanager.hpp \
$(liblumiproc_la_srcdir)/mobject/builder/segmentationtool.hpp \
$(liblumiproc_la_srcdir)/mobject/builder/segmentation.hpp \
$(liblumiproc_la_srcdir)/mobject/builder/segmentation-tool.hpp \
$(liblumiproc_la_srcdir)/mobject/builder/toolfactory.hpp \
$(liblumiproc_la_srcdir)/mobject/builderfacade.hpp \
$(liblumiproc_la_srcdir)/mobject/explicitplacement.hpp \

View file

@ -103,11 +103,13 @@ namespace asset {
template<class KIND>
class ID
{
HashVal hash_;
public:
const HashVal hash;
ID (HashVal id) : hash(id) {}
ID (const KIND& asset) : hash(asset.getID()) {}
operator HashVal() const { return hash; }
ID (HashVal id) : hash_(id) {}
ID (const KIND& asset) : hash_(asset.getID()) {}
operator HashVal() const { return hash_; }
static ID INVALID;
};
class DB;
@ -303,53 +305,59 @@ namespace asset {
/* ====== ordering of Assets and Asset-Pointers ====== */
/** ordering of Assets is based on the ordering
* of Ident tuples, which are supposed to be unique.
* By using our customised lumiera::P as smart ptr,
* comparison on P<Asset> ptrs will be automatically
* forwarded to the Asset comparison operators.
* @note version info is irrelevant */
inline int
Asset::Ident::compare (Asset::Ident const& oi) const
{
int res;
if (0 != (res=category.compare (oi.category))) return res;
if (0 != (res=org.compare (oi.org))) return res;
return name.compare (oi.name);
}
/** promote subtype-ptr to PAsset, e.g. for comparing */
template<class A>
inline const PcAsset
pAsset (shared_ptr<A> const& subPtr)
{
return static_pointer_cast<const Asset,A> (subPtr);
}
/** type trait for detecting a shared-ptr-to-asset */
template <class X>
struct is_pAsset : boost::false_type {};
template <class A>
struct is_pAsset<shared_ptr<A> >
: boost::is_base_of<Asset, A> {};
/** convenient for debugging */
inline string str (PcAsset const& a)
{
if (a)
return string (*a.get());
else
return "Asset(NULL)";
}
/* ====== ordering of Assets and Asset-Pointers ====== */
/** ordering of Assets is based on the ordering
* of Ident tuples, which are supposed to be unique.
* By using our customised lumiera::P as smart ptr,
* comparison on P<Asset> ptrs will be automatically
* forwarded to the Asset comparison operators.
* @note version info is irrelevant */
inline int
Asset::Ident::compare (Asset::Ident const& oi) const
{
int res;
if (0 != (res=category.compare (oi.category))) return res;
if (0 != (res=org.compare (oi.org))) return res;
return name.compare (oi.name);
}
/** promote subtype-ptr to PAsset, e.g. for comparing */
template<class A>
inline const PcAsset
pAsset (shared_ptr<A> const& subPtr)
{
return static_pointer_cast<const Asset,A> (subPtr);
}
/** type trait for detecting a shared-ptr-to-asset */
template <class X>
struct is_pAsset : boost::false_type {};
template <class A>
struct is_pAsset<shared_ptr<A> >
: boost::is_base_of<Asset, A> {};
/** marker constant denoting a NIL asset */
template<class KIND>
ID<KIND> ID<KIND>::INVALID = ID(0);
/** convenient for debugging */
inline string str (PcAsset const& a)
{
if (a)
return string (*a.get());
else
return "Asset(NULL)";
}
} // namespace asset

View file

@ -241,7 +241,7 @@ namespace asset {
}
friend ostream& operator<< (ostream& os, EntryID const& id) { return os << string(id); }
friend bool operator< (EntryID const& i1, EntryID const& i2) { return i1.getSym() < i2.getSym(); }
friend bool operator< (EntryID const& i1, EntryID const& i2) { return i1.getSym() < i2.getSym(); }
};

View file

@ -38,7 +38,7 @@ namespace asset {
* default wiring.
*/
Pipe::Pipe ( const Asset::Ident& idi
, string const& streamID
, string const& streamID ////////////////////////////////////////TICKET #648
, PProcPatt& wiring
, string shortName
, string longName

View file

@ -26,6 +26,7 @@
#include "proc/asset/struct.hpp"
#include "proc/asset/procpatt.hpp"
#include "lib/streamtype.hpp"
#include <string>
@ -34,6 +35,7 @@
namespace asset {
using lumiera::P;
using lumiera::StreamType;
using std::string;
class Pipe;
@ -45,7 +47,16 @@ namespace asset {
{
public:
ID (HashVal id);
ID (const Pipe&);
ID (Pipe const&);
ID (PPipe const&);
/** allows a Pipe-ID to stand in for a full Pipe Asset
* @throw error::Invalid when there is no corresponding Pipe */
operator PPipe() const;
/** allows to fetch the StreamType directly just from a Pipe-ID
* @throw error::Invalid when there is no corresponding Pipe */
StreamType::ID streamType() const;
};
@ -59,7 +70,7 @@ namespace asset {
: public Struct
{
PProcPatt wiringTemplate_;
const string streamID_; ///< @todo just a placeholder for now 10/10
StreamType::ID streamID_; ////////////////////////////////////////TICKET #648
public:
string shortDesc;
@ -78,7 +89,7 @@ namespace asset {
public:
string const& getPipeID() const { return ident.name; }
string const& getStreamID() const { return streamID_; }
StreamType::ID getStreamID() const { return streamID_; } ////////////////////////////////////////TICKET #648
PProcPatt const& getProcPatt() const { return wiringTemplate_; }
@ -95,9 +106,21 @@ namespace asset {
// catch up with postponed definition of ID<Struct> ctors...
//
inline ID<Pipe>::ID(HashVal id) : ID<Struct> (id) {};
inline ID<Pipe>::ID(Pipe const& pipe) : ID<Struct> (pipe.getID()) {};
inline ID<Pipe>::ID(HashVal id) : ID<Struct> (id) {};
inline ID<Pipe>::ID(Pipe const& pipe) : ID<Struct> (pipe.getID()) {};
inline ID<Pipe>::ID(PPipe const& pipe) : ID<Struct> (pipe->getID()) {};
inline
ID<Pipe>::operator PPipe() const
{
return Pipe::lookup(*this);
}
inline StreamType::ID
ID<Pipe>::streamType() const
{
return Pipe::lookup(*this)->getStreamID();
}

View file

@ -52,6 +52,9 @@ namespace session {
class Clip;
}}
namespace lumiera {
class StreamType;
}
namespace asset{
@ -104,6 +107,12 @@ namespace asset{
static Symbol catFolder() { return "pipes";}
static Symbol idSymbol() { return "pipe"; }
};
template<> struct StructTraits<lumiera::StreamType>
{
static Symbol namePrefix() { return "type"; }
static Symbol catFolder() { return "stream-types";}
static Symbol idSymbol() { return "stype"; }
};
template<> struct StructTraits<const ProcPatt>
{
static Symbol namePrefix() { return "patt"; }

View file

@ -148,7 +148,7 @@ namespace asset {
normaliseID (pipeID);
normaliseID (streamID);
static format descriptor("pipe(%s), stream(%s).");
Pipe* pP = impl_->fabricate (Query<Pipe> (descriptor % pipeID % streamID));
Pipe* pP = impl_->fabricate (Query<Pipe> (str(descriptor % pipeID % streamID)));
return AssetManager::instance().wrap (*pP);
}

View file

@ -79,6 +79,14 @@ namespace control {
}
/** */
StreamType const&
STypeManager::getType (StreamType::ID stID)
{
UNIMPLEMENTED ("get type just by symbolic ID (query defaults manager)");
}
StreamType const&
STypeManager::getType (StreamType::Prototype const& protoType)
{

View file

@ -53,6 +53,8 @@ namespace control {
* just a symbolic ID. Effectively this queries a default */
StreamType const& getType (Symbol sTypeID) ;
StreamType const& getType (StreamType::ID stID) ;
/** build or retrieve a complete StreamType implementing the given Prototype */
StreamType const& getType (StreamType::Prototype const& protoType) ;

View file

@ -23,7 +23,7 @@
/** @file styperegistry.hpp
** This is part of the \i implementation of the stream type manager (include).
** Only used in stypemanager.cpp and accompaning unit tests.
** Only used in stypemanager.cpp and accompanying unit tests.
**
** @see control::STypeManager
** @see lumiera::StreamType
@ -62,7 +62,7 @@ namespace control {
/**
* @internal Helper for organizing preconfigured default objects.
* @internal Helper for organising preconfigured default objects.
* Maintaines a collection of objects known or encountered as "default"
* for a given type. This collection is ordered by "degree of constriction",
* which is implemented by counting the number of predicates in the query

View file

@ -24,10 +24,10 @@
#include "proc/engine/renderengine.hpp"
namespace engine {
/** */
} // namespace engine

View file

@ -32,19 +32,26 @@
using std::list;
namespace engine
{
namespace engine {
/**
* @todo this is planned to become the frontend
* to the render node network, which can be considered
* at the lower end of the middle layer; the actual
* render operations are mostly implemented by the backend
* ////////TODO WIP as of 12/2010
*/
class RenderEngine : public RenderGraph
{
public:
///// TODO: find out about the public operations
// note: the play controller lives in the backend
// note: the play controller lives in the proc-layer,
// but is a subsystem separate of the sesison.
private:
list<RenderGraph> renderSegments;
};
} // namespace engine

View file

@ -26,17 +26,17 @@
#include "proc/state.hpp"
namespace lumiera {
/** storage for the unique node-ID counter */
ulong NodeID::currID (0);
}
namespace engine {
/** */
} // namespace engine

View file

@ -29,25 +29,27 @@
namespace engine
{
namespace engine {
class ExitNode;
/**
* @todo likely to be reworked into the engine backbone /////////////TODO WIP as of 12/2010
*/
class RenderGraph
{
protected:
ExitNode * output;
/** begin of the timerange covered by this RenderGraph */
lumiera::Time start;
/**end (exclusive) of the timerange */
lumiera::Time end;
};
} // namespace engine
#endif

View file

@ -62,7 +62,7 @@
namespace mobject {
class Buildable;
namespace builder {
@ -105,7 +105,7 @@ namespace mobject {
* as we simply store a pointer within the BuilderTool instance.
*/
class BuilderTool
: public lumiera::visitor::Tool<void, InvokeCatchAllFunction>
: public lumiera::visitor::Tool<void, InvokeCatchAllFunction>
{
lumiera::WrapperPtr currentWrapper_;
@ -172,7 +172,9 @@ namespace mobject {
using lumiera::typelist::Types; // convenience for the users of "Applicable"
} // namespace mobject::builder
}// namespace mobject::builder
@ -185,8 +187,15 @@ namespace mobject {
namespace builder { // to be found by ADL
namespace builder {
/** to be picked up by ADL: redirect tool invocation for double dispatch.
* The purpose of this function is to apply a visitor, while the actual target
* is managed by a generic wrapper (smart-ptr). This template function serves
* to generate forwarding functions, which pass on the \c apply() call to the
* actual embedded target, while passing on the fully wrapped object for later
* referral and usage too.
*/
template<typename WRA>
inline Buildable::ReturnType
apply (BuilderTool& tool, WRA& wrappedTargetObj)
@ -195,8 +204,7 @@ namespace mobject {
wrappedTargetObj->apply (tool); // dispatch to suitable treat() function
tool.forgetWrapper();
}
} // namespace mobject::builder
} // namespace mobject
}} // namespace mobject::builder
#endif

View file

@ -24,25 +24,14 @@
#ifndef MOBJECT_BUILDER_COMMON_H
#define MOBJECT_BUILDER_COMMON_H
#include "lib/error.hpp"
#include "include/logging.h"
namespace mobject {
namespace builder {
namespace builder {
// TODO NOBUG_DECLARE_FLAG (builder_mem);
} // namespace builder
} // namespace mobject
}} // namespace mobject::builder
#endif
/*
// Local Variables:
// mode: C
// c-file-style: "gnu"
// indent-tabs-mode: nil
// End:
*/

View file

@ -0,0 +1,81 @@
/*
FIXTURE-CHANGE-DETECTOR.hpp - isolating changed segments and tainted processes
Copyright (C) Lumiera.org
2010, 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 fixture-change-detector.hpp
** Work out the part of the Fixture changed by a build process.
** This facility helps to deal with ongoing render/playback processes, which might be
** affected by the results of a build process. It's comprised of two distinct parts:
** - a comparison tool allowing to spot equal and changed segments when considering
** the old and the new version of the fixture before/after a build process.
** - a registration service to establish a relation between play/render processes
** and specific segments of the fixture.
** Together, these allow to identify those ongoing processes which need to be cancelled
** or restarted, because their results might be tainted by the changes induced by the
** build process. Typically, these detection process runs just before commiting the
** newly built fixture datastructure.
**
** @todo WIP-WIP-WIP as of 12/2010
**
** @see Fixture
** @see ModelPort
*/
#ifndef PROC_MOBJECT_BUILDER_FIXTURE_CHANGE_DETECTOR_H
#define PROC_MOBJECT_BUILDER_FIXTURE_CHANGE_DETECTOR_H
#include "lib/error.hpp"
//#include "lib/optional-ref.hpp"
#include "proc/asset/pipe.hpp"
//#include "proc/asset/struct.hpp"
//#include "proc/mobject/model-port.hpp"
//#include <map>
namespace mobject {
namespace builder {
using asset::ID;
using asset::Pipe;
//using asset::Struct;
//LUMIERA_ERROR_DECLARE (DUPLICATE_MODEL_PORT); ///< Attempt to define a new model port with an pipe-ID already denoting an existing port
/**
* TODO type comment
*/
class FixtureChangeDetector
: boost::noncopyable
{
typedef ID<Pipe> PID;
// typedef ID<Struct> StID;
public:
};
}} // namespace mobject::builder
#endif

View file

@ -0,0 +1,302 @@
/*
ModelPortRegistry - creating and organising and accessing model ports
Copyright (C) Lumiera.org
2010, 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 model-port-registry.cpp
** Implementation details of model port descriptors and references.
** Essentially the handling of the ModelPortRegistry datastructure is
** kept an opaque implementation detail and confined entirely within
** this translation unit. Both the client interface (ModelPort) and
** the management interface (ModelPortRegistry) are backed by this
** common translation unit.
**
** @see OutputDesignation
** @see OutputMapping
** @see Timeline
**
*/
#include "lib/error.hpp"
#include "include/logging.h"
#include "lib/sync-classlock.hpp"
#include "proc/mobject/builderfacade.hpp"
#include "proc/mobject/model-port.hpp"
#include "proc/mobject/builder/model-port-registry.hpp"
namespace mobject {
namespace builder {
namespace error = lumiera::error;
typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor;
typedef lib::ClassLock<ModelPortRegistry> LockRegistry;
/** storage for the link to the global
Registry instance currently in charge */
lib::OptionalRef<ModelPortRegistry> ModelPortRegistry::theGlobalRegistry;
/** globally deactivate access to model ports */
void
ModelPortRegistry::shutdown ()
{
INFO (builder, "disabling ModelPort registry....");
LockRegistry global_lock;
theGlobalRegistry.clear();
}
/** switch the implicit link to \em the global ModelPort registry
* to point to the given implementation instance. Typically used
* within the Builder subsystem lifecycle methods, or for
* temporarily exchanging the registry for unit tests
* @return the registry instance previously in use or \c NULL
*/
ModelPortRegistry*
ModelPortRegistry::setActiveInstance (ModelPortRegistry& newRegistry)
{
LockRegistry global_lock;
ModelPortRegistry *previous = theGlobalRegistry.isValid()?
&( theGlobalRegistry()) : 0;
INFO_IF (!previous, builder, "activating new ModelPort registry.");
WARN_IF ( previous, builder, "switching ModelPort registry instance.");
theGlobalRegistry.link_to (newRegistry);
return previous;
}
/** access the globally valid registry instance.
* @throw error::State if this global registry is
* already closed or not yet initialised. */
ModelPortRegistry&
ModelPortRegistry::globalInstance()
{
LockRegistry global_lock;
if (theGlobalRegistry.isValid())
return theGlobalRegistry();
throw error::State ("global model port registry is not accessible"
, LUMIERA_ERROR_BUILDER_LIFECYCLE);
}
/** does the <i>transaction currently being built</i>
* already contain a model port registration for the given ID?
* @note this does \em not query registration state of the
* global registry; use #isRegistered for that...*/
bool
ModelPortRegistry::contains (ID<Pipe> key) const
{
return bool(key)
&& util::contains (transaction_, key);
}
/** @return true if the given pipe-ID actually denotes an
* existing, connected and usable model port.
* @note reflects the state of the publicly visible
* model port registry, \em not any model ports
* being registered within a pending transaction
* (ongoing build process). */
bool
ModelPortRegistry::isRegistered (ID<Pipe> key) const
{
return bool(key)
&& util::contains (currentReg_, key);
}
/** basic access operation: access the descriptor
* of a currently valid model port.
* @note no locking (but #accessDescriptor does lock!)
* @throw error::Logic if accessing a non registered port
* @throw error::State if accessing an invalid / disconnected port
*/
MPDescriptor
ModelPortRegistry::get (ID<Pipe> key) const
{
if (!key)
throw error::State ("This model port is disconnected or NIL"
, LUMIERA_ERROR_UNCONNECTED_MODEL_PORT);
if (!isRegistered (key))
throw error::Logic ("Model port was never registered, or got unregistered meanwhile."
,LUMIERA_ERROR_INVALID_MODEL_PORT);
MPTable::const_iterator pos = currentReg_.find (key);
ASSERT (pos != currentReg_.end());
ASSERT (pos->second.isValid());
return pos->second;
}
/** access \em the globally valid model port for the given pipe.
* This (static) function locks and accesses the global model port registry
* to fetch the descriptor record. Typically invoked by client code
* through the ModelPort frontend
* @throw error::State when registry is down or the model port is disconnected
* @throw error::Logic when the given key wasn't registered for a model port */
MPDescriptor
ModelPortRegistry::accessDescriptor (ID<Pipe> key)
{
LockRegistry global_lock;
return theGlobalRegistry().get(key);
}
/* === Mutations === */
/** create and register a new model port entry,
* within the pending transaction */
MPDescriptor
ModelPortRegistry::definePort (ID<Pipe> pipe, ID<Struct> element_exposing_this_port)
{
LockRegistry global_lock;
if (contains (pipe))
throw error::Logic ("attempt to register a model port with a pipe-ID, "
"which has already been used to register a "
"model port within this transaction (build process)."
, LUMIERA_ERROR_DUPLICATE_MODEL_PORT);
return (transaction_[pipe] = ModelPortDescriptor(pipe, element_exposing_this_port));
}
/** remove a model port entry from the pending transaction */
void
ModelPortRegistry::remove (PID key)
{
LockRegistry global_lock;
transaction_.erase (key);
}
/** schedule removal of all registry contents.
* When the currently pending transaction is committed,
* all registered model ports will be removed */
void
ModelPortRegistry::clear()
{
LockRegistry global_lock;
transaction_.clear();
}
/** transactional switch for new/modified model ports.
* Promote the registered model ports from the currently
* pending transaction to become the globally valid model ports
* @note automatically starts a new transaction, initialised
* with the now published mappings.
*/
void
ModelPortRegistry::commit()
{
LockRegistry global_lock;
MPTable newTransaction(transaction_);
TRACE (builder, "committing new ModelPort list....");
swap (currentReg_, transaction_);
swap (transaction_, newTransaction);
}
/** discard current transaction.
* The global port registration thus
* remains unaltered. */
void
ModelPortRegistry::rollback()
{
LockRegistry global_lock;
TRACE (builder, "discarding changes to ModelPort list (rollback)....");
MPTable newTransaction(currentReg_);
swap (transaction_, newTransaction);
}
LUMIERA_ERROR_DEFINE (DUPLICATE_MODEL_PORT, "Attempt to define a new model port with an pipe-ID already denoting an existing port");
}// namespace builder
LUMIERA_ERROR_DEFINE (INVALID_MODEL_PORT, "Referral to unknown model port");
LUMIERA_ERROR_DEFINE (UNCONNECTED_MODEL_PORT, "Attempt to operate on an existing but unconnected model port");
ModelPort::ModelPort (ID<asset::Pipe> refID)
: id_(refID)
{
builder::ModelPortRegistry::accessDescriptor (refID);
}
/** check if the global model port registration
* contains a mapping for the given pipe-ID*/
bool
ModelPort::exists (ID<asset::Pipe> key)
{
return builder::ModelPortRegistry::globalInstance().isRegistered (key);
}
/** access the Pipe (ID) of the global model port registered
* with the ID underlying this model port.
* @throw error::Logic if no model port is registered for this Pipe-ID
*/
ID<asset::Pipe>
ModelPort::pipe() const
{
ENSURE (this->id_ == builder::ModelPortRegistry::accessDescriptor(this->id_).id());
return builder::ModelPortRegistry::accessDescriptor(this->id_).id();
}
/** access the timeline (or similar structural element) holding
* a global pipe which corresponds to this model port
* @throw error::Logic if no model port is registered for this Pipe-ID
*/
ID<asset::Struct>
ModelPort::holder() const
{
return builder::ModelPortRegistry::accessDescriptor(this->id_).holder();
}
/** convenience shortcut to access the stream type
* associated with the pipe-ID corresponding to this model port.
* @note no check if this model port actually is valid
* @throw error::Invalid in case of unknown/unregistered Pipe-ID
*/
StreamType::ID
ModelPort::streamType() const
{
return this->id_.streamType();
}
} // namespace mobject

View file

@ -0,0 +1,181 @@
/*
MODEL-PORT-REGISTRY.hpp - creating and organising and accessing model ports
Copyright (C) Lumiera.org
2010, 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 model-port-registry.hpp
** Mutation and management facility for model ports within the builder.
** Model ports denote the points where output might possibly be produced.
** While client code accesses model ports only as immutable descriptors handled
** through an (opaque) reference, the builder is in charge of detecting and organising
** any (new) model ports arising as the result of the build process. Changes to the set
** of current model ports are to be activated with an atomic <i>transactional switch.</i>
**
** builder::ModelPortRegistry thus acts as management interface and factory for model ports.
** A given instance of this registry can be promoted to be "the" model port registry reflecting
** the current active model ports. Within the Lumiera application, the builder subsystem cares
** for setting up such a registry, while all other parts of the system just access the current
** model ports through the mobject::ModelPort frontend.
**
** @note the locking is rather coarse grained; basically we're using just one
** single global lock for all ModelPortRegistry instances and all access/mutations,
** as well as for accessing the globally valid Registry through the ModelPort frontend.
** Assumed that usually there is just one Registry maintained by the builder, this is
** likely to be sufficient.
**
** @see ModelPort
** @see OutputDesignation
** @see ModelPortRegistry_test
*/
#ifndef PROC_MOBJECT_BUILDER_MODEL_PORT_REGISTRY_H
#define PROC_MOBJECT_BUILDER_MODEL_PORT_REGISTRY_H
#include "lib/error.hpp"
#include "lib/optional-ref.hpp"
#include "proc/asset/pipe.hpp"
#include "proc/asset/struct.hpp"
#include "proc/mobject/model-port.hpp"
#include <map>
namespace mobject {
namespace builder {
using asset::ID;
using asset::Pipe;
using asset::Struct;
LUMIERA_ERROR_DECLARE (DUPLICATE_MODEL_PORT); ///< Attempt to define a new model port with an pipe-ID already denoting an existing port
/**
* Management facility for tracking model ports.
* ModelPort handles are exposed as frontend for usage
* by client code. Model ports are discovered by the builder
* when re-creating the low-level model; during such an ongoing
* build process, newly discovered ports are accumulated within
* a transaction, which then gets committed atomically when the
* new model is complete and ready for use.
*/
class ModelPortRegistry
: boost::noncopyable
{
typedef ID<Pipe> PID;
typedef ID<Struct> StID;
public:
/** @internal record to describe a model port */
struct ModelPortDescriptor;
static void shutdown ();
static ModelPortRegistry*
setActiveInstance (ModelPortRegistry& newRegistry);
static ModelPortRegistry&
globalInstance();
static ModelPortDescriptor const&
accessDescriptor (PID);
ModelPortDescriptor const&
definePort (PID pipe, StID element_exposing_this_port);
bool contains (PID) const;
bool isRegistered (PID) const;
ModelPortDescriptor const&
get (PID) const;
void remove (PID);
void clear();
/** activate pending model port changes.
* Any accumulated changes and newly defined model ports
* are promoted to become part of the current active configuration
* with a single atomic (transactional) switch.
*/
void commit();
/** discard pending changes.
* Silently drop model port definition changes since the last commit.
*/
void rollback();
private:
static lib::OptionalRef<ModelPortRegistry> theGlobalRegistry;
typedef std::map<PID, ModelPortDescriptor> MPTable;
MPTable currentReg_;
MPTable transaction_;
};
/** ModelPortDescriptor records are used as actual storage
* within the model port registration table; they are immutable
* value objects and never exposed to client code directly.
*/
class ModelPortRegistry::ModelPortDescriptor
{
PID id_;
StID holder_;
protected:
ModelPortDescriptor (PID pipe, StID element_exposing_this_port)
: id_(pipe)
, holder_(element_exposing_this_port)
{ }
friend class ModelPortRegistry;
public:
ModelPortDescriptor()
: id_(PID::INVALID)
, holder_(StID::INVALID)
{ }
// default copy operations permitted
bool
isValid() const
{
return bool(id_);
}
const PID id() const { return id_; }
const StID holder() const { return holder_; }
};
}} // namespace mobject::builder
#endif

View file

@ -21,7 +21,7 @@
* *****************************************************/
#include "proc/mobject/builder/segmentationtool.hpp"
#include "proc/mobject/builder/segmentation-tool.hpp"
using mobject::Buildable;
@ -30,52 +30,50 @@ using mobject::session::Effect;
namespace mobject {
namespace builder {
/////////////////////////////////TICKET #414
namespace builder {
SegmentationTool::SegmentationTool(mobject::session::Fixture&)
{
UNIMPLEMENTED ("create new SegmentationTool");
}
void
SegmentationTool::treat (Buildable& something)
{
UNIMPLEMENTED ("??? when partitioning timeline");
}
void
SegmentationTool::treat (Clip& clip)
{
UNIMPLEMENTED ("consider clip when partitioning timeline");
}
void
SegmentationTool::treat (Effect& effect)
{
UNIMPLEMENTED ("note applied effect when partitioning timeline");
}
void
SegmentationTool::onUnknown (Buildable& target)
{
UNIMPLEMENTED ("catch-all when partitioning timeline");
}
bool
SegmentationTool::empty() const
{
UNIMPLEMENTED ("detect an empty segmentation");
}
} // namespace mobject::builder
} // namespace mobject
/////////////////////////////////TICKET #414
SegmentationTool::SegmentationTool(mobject::session::Fixture&)
{
UNIMPLEMENTED ("create new SegmentationTool");
}
void
SegmentationTool::treat (Buildable& something)
{
UNIMPLEMENTED ("??? when partitioning timeline");
}
void
SegmentationTool::treat (Clip& clip)
{
UNIMPLEMENTED ("consider clip when partitioning timeline");
}
void
SegmentationTool::treat (Effect& effect)
{
UNIMPLEMENTED ("note applied effect when partitioning timeline");
}
void
SegmentationTool::onUnknown (Buildable& target)
{
UNIMPLEMENTED ("catch-all when partitioning timeline");
}
bool
SegmentationTool::empty() const
{
UNIMPLEMENTED ("detect an empty segmentation");
}
}} // namespace mobject::builder

View file

@ -0,0 +1,78 @@
/*
SEGMENTATION-TOOL.hpp - Tool for creating a partitioning of the current timeline
Copyright (C) Lumiera.org
2008, 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.
*/
#ifndef MOBJECT_BUILDER_SEGMENTATION_TOOL_H
#define MOBJECT_BUILDER_SEGMENTATION_TOOL_H
#include "proc/mobject/builder/applicable-builder-target-types.hpp"
#include "proc/mobject/session/segmentation.hpp"
#include "proc/mobject/session/fixture.hpp" //////TODO really on the header??
#include <list>
using std::list;
namespace mobject {
namespace builder {
/**
* Tool implementation for deriving a partitioning of the current
* timeline, such that each Segment has a constant configuration.
* "Constant" means here, that any remaining changes over time
* can be represented by automation solely, without the need
* to change the node connections.
*/
class SegmentationTool
: public ApplicableBuilderTargetTypes<SegmentationTool>
{
public:
SegmentationTool (session::Fixture &) ;
void treat (mobject::session::Clip& clip) ;
void treat (mobject::session::Effect& effect) ;
void treat (mobject::Buildable& something) ;
void onUnknown (Buildable& target) ; /////////TODO why doesn't the treat(Buildable) function shadow this??
bool empty() const;
private:
typedef mobject::session::Segment Segment;
/** Partitioning of the Timeline to be created by this tool. */
//session::Segmentation& segments_;
///////////////////////////////////////////TODO: either put it inline, or use a scopend_ptr!!!!!!!!!!
};
}} // namespace mobject::builder
#endif

View file

@ -0,0 +1,85 @@
/*
Segmentation - partitioning the effective timeline
Copyright (C) Lumiera.org
2010, 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 segmentation.cpp
** Implementation details of fixture data structures.
**
** /////TODO file comment necessary?
**
** @see mobject::session::Fixture
**
*/
#include "lib/error.hpp"
//#include "include/logging.h"
//#include "lib/sync-classlock.hpp"
//#include "proc/mobject/builderfacade.hpp"
#include "proc/mobject/builder/segmentation.hpp"
#include "proc/mobject/builder/fixture-change-detector.hpp"
namespace mobject {
namespace builder {
namespace error = lumiera::error;
// typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor;
/** storage for the link to the global
Registry instance currently in charge */
// lib::OptionalRef<ModelPortRegistry> ModelPortRegistry::theGlobalRegistry;
/** access the globally valid registry instance.
* @throw error::State if this global registry is
* already closed or not yet initialised. */
//ModelPortRegistry&
//ModelPortRegistry::globalInstance()
//{
// LockRegistry global_lock;
// if (theGlobalRegistry.isValid())
// return theGlobalRegistry();
//
// throw error::State ("global model port registry is not accessible"
// , LUMIERA_ERROR_BUILDER_LIFECYCLE);
//}
/** */
// bool
// ModelPortRegistry::contains (ID<Pipe> key) const
// {
// return bool(key)
// && util::contains (transaction_, key);
// }
//LUMIERA_ERROR_DEFINE (DUPLICATE_MODEL_PORT, "Attempt to define a new model port with an pipe-ID already denoting an existing port");
}}// namespace mobject::builder

View file

@ -0,0 +1,87 @@
/*
SEGMENTATION.hpp - partitioning the effective timeline
Copyright (C) Lumiera.org
2010, 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 segmentation.hpp
** Part of the Fixture datastructure to manage time segments of constant structure.
** The Fixture is result of the build process and separation between high-level and
** low-level model. It's kind of an effective resulting timeline, and split into segments
** of constant wiring structure: whenever the processing nodes need to be wired differently
** for some timespan, we start a new segment of the timeline. This might be for the duration
** of a clip, or just for the duration of a transition, when the pipes of both clips need to
** be wired up in parallel.
**
** @see Fixture
** @see ModelPort
*/
#ifndef PROC_MOBJECT_BUILDER_SEGMENTATION_H
#define PROC_MOBJECT_BUILDER_SEGMENTATION_H
#include "lib/error.hpp"
//#include "lib/optional-ref.hpp"
#include "proc/asset/pipe.hpp"
//#include "proc/asset/struct.hpp"
//#include "proc/mobject/model-port.hpp"
//#include <map>
namespace mobject {
namespace builder {
using asset::ID;
using asset::Pipe;
//using asset::Struct;
//LUMIERA_ERROR_DECLARE (DUPLICATE_MODEL_PORT); ///< Attempt to define a new model port with an pipe-ID already denoting an existing port
/**
* TODO type comment
*/
class Segment
: boost::noncopyable
{
typedef ID<Pipe> PID;
// typedef ID<Struct> StID;
public:
};
/**
* TODO type comment
*/
class Segmentation
{
public:
};
}} // namespace mobject::builder
#endif

View file

@ -1,80 +0,0 @@
/*
SEGMENTATIONTOOL.hpp - Tool for creating a partitioning of the current timeline
Copyright (C) Lumiera.org
2008, 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.
*/
#ifndef MOBJECT_BUILDER_SEGMENTATIONTOOL_H
#define MOBJECT_BUILDER_SEGMENTATIONTOOL_H
#include "proc/mobject/builder/applicable-builder-target-types.hpp"
#include "proc/mobject/session/segmentation.hpp"
#include "proc/mobject/session/fixture.hpp" //////TODO really on the header??
#include <list>
using std::list;
namespace mobject {
namespace builder {
/**
* Tool implementation for deriving a partitioning of the current
* timeline, such that each Segment has a constant configuration.
* "Constant" means here, that any remaining changes over time
* can be represented by automation solely, without the need
* to change the node connections.
*/
class SegmentationTool
: public ApplicableBuilderTargetTypes<SegmentationTool>
{
public:
SegmentationTool (session::Fixture &) ;
void treat (mobject::session::Clip& clip) ;
void treat (mobject::session::Effect& effect) ;
void treat (mobject::Buildable& something) ;
void onUnknown (Buildable& target) ; /////////TODO why doesn't the treat(Buildable) function shadow this??
bool empty() const;
private:
typedef mobject::session::Segment Segment;
/** Partitioning of the Timeline to be created by this tool. */
//session::Segmentation& segments_;
///////////////////////////////////////////TODO: either put it inline, or use a scopend_ptr!!!!!!!!!!
};
} // namespace mobject::builder
} // namespace mobject
#endif

View file

@ -25,7 +25,7 @@
#define MOBJECT_BUILDER_TOOLFACTORY_H
#include "proc/mobject/session/fixture.hpp"
#include "proc/mobject/builder/segmentationtool.hpp"
#include "proc/mobject/builder/segmentation-tool.hpp"
#include "proc/mobject/builder/nodecreatortool.hpp"
#include "proc/mobject/builder/mould.hpp"
#include "proc/engine/rendergraph.hpp"

View file

@ -26,12 +26,7 @@
namespace mobject {
namespace builder {
using ::NOBUG_FLAG(memory);
NOBUG_CPP_DEFINE_FLAG_PARENT(buildermem, memory);
}
/**
* Main Operation of the Builder:
* create a render engine for a given part of the timeline
@ -41,7 +36,10 @@ namespace mobject {
{
//////////////////////TODO
}
LUMIERA_ERROR_DEFINE (BUILDER_LIFECYCLE, "Builder activated while in non operational state");
} // namespace mobject

View file

@ -30,8 +30,9 @@
namespace mobject {
LUMIERA_ERROR_DECLARE (BUILDER_LIFECYCLE); ///< Builder activated while in non operational state
/**
* Provides unified access to the builder functionality.
* While individual components of the builder subsystem may be called
@ -44,12 +45,14 @@ namespace mobject {
/**
* Main Operation of the Builder:
* create a render engine for a given part of the timeline
* @deprecated thats a placeholder! the real invocation is now
* in the course of being worked out ////////////TODO 12/2010
*/
engine::RenderEngine & buildEngine () ;
// TODO: allocation, GC??????
};
} // namespace mobject
#endif

View file

@ -0,0 +1,152 @@
/*
MODEL-PORT.hpp - point to pull output data from the model
Copyright (C) Lumiera.org
2010, 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 model-port.hpp
** Organising the output data calculation possibilities.
** Model ports are conceptual entities, denoting the points where output might
** possibly be produced. There is an actual representation, a collection of small
** descriptor objects managed by the Fixture and organised within the registry
** datastructure. Thus, while the actual ModelPort descriptor entities are located
** within and managed by the Fixture -- model port as a concept spans the high-level
** and low-level view. A model port can be associated both to a pipe within a timeline
** in the HighLevelModel, as well as to denote a set of corresponding exit nodes within
** the segments of the render nodes network. Model ports are keyed by Pipe-ID and thus
** are bound to be unique within the application.
**
** A model port is rather derived than configured; it emerges during the build process
** when a pipe claims an OutputDesignation and some other entity actually uses this
** designation as a target, either directly or indirectly. This match of provision
** and usage is detected by the Builder and produces an entry in the fixture's
** ModelPortTable. Because of the 1:1 association with a pipe, each model port
** has an associated StreamType.
**
** Because model ports are discovered this way, dynamically during the build process,
** at some point there is a <i>transactional switch</i> to promote the new configuration
** to become the valid current model port configuration. After that switch, model ports
** are immutable.
**
** Model ports are to be accessed, enumerated and grouped in various ways, because
** each port belongs to a specific timeline and is used to produce data of a single
** StreamType solely. But all those referrals, searching and grouping happens only
** after the build process has discovered all model ports currently available.
** Thus actually the ModelPort elements handed out to client code are just
** smart-handles, accessing a global ModelPortRegistry behind the scenes.
** Validity of these handles will be checked on each access. The actual
** model port descriptors are owned and managed by the fixture;
** @todo they might bulk allocated in a similar manner than the
** ProcNode and WiringDescriptor objects are.
**
** @see ModelPortRegistry_test abstract usage example
** @see ModelPortRegistry management interface
** @see OutputDesignation
** @see OutputMapping
** @see Timeline
*/
#ifndef PROC_MOBJECT_MODEL_PORT_H
#define PROC_MOBJECT_MODEL_PORT_H
#include "proc/asset/pipe.hpp"
#include "lib/bool-checkable.hpp"
#include "lib/streamtype.hpp"
namespace mobject {
LUMIERA_ERROR_DECLARE (INVALID_MODEL_PORT); ///< Referral to unknown model port
LUMIERA_ERROR_DECLARE (UNCONNECTED_MODEL_PORT); ///< Attempt to operate on an existing but unconnected model port
using asset::ID;
using lumiera::StreamType;
/**
* Handle denoting a point within the model,
* where actually output data can be pulled.
* ModelPort is a frontend to be used by clients.
* These ModelPort handle objects may be copied and stored
* at will, but their validity will be verified on each access.
* Actually, the Builder will discover any model ports and
* maintain a ModelPortRegistry behind the scenes.
*
* Each model port corresponds to a (global) pipe within a
* specific Timeline ("holder"); consequently each such port is also
* bound to produce data of a specific StreamType (as defined by
* the corresponding pipe). A model port may be in \em unconnected
* state, which can be checked through \c bool conversion. While
* the ModelPort handles are value objects, the identity of the
* underlying model port (descriptor) is given by the
* corresponding pipe-ID, thus effectively resulting
* in a global namespace for model ports.
*
* @see builder::ModelPortRegistry management interface
* @see ModelPortRegistry_test abstract usage example
*/
class ModelPort
: public lib::BoolCheckable<ModelPort>
{
ID<asset::Pipe> id_;
public:
ModelPort() ///< \em unconnected model port
: id_(ID<asset::Pipe>::INVALID)
{ }
ModelPort (ID<asset::Pipe> refID); ///< @note conversion from pipe-ID
// using default copy operations
static bool exists (ID<asset::Pipe>);
ID<asset::Pipe> pipe() const;
ID<asset::Struct> holder() const;
StreamType::ID streamType() const;
bool
isValid() const
{
return exists (this->id_);
}
friend bool
operator== (ModelPort const& mp1, ModelPort const& mp2)
{
return mp1.id_ == mp2.id_;
}
friend bool
operator!= (ModelPort const& mp1, ModelPort const& mp2)
{
return mp1.id_ != mp2.id_;
}
private:
};
} // namespace mobject
#endif

View file

@ -152,6 +152,7 @@ namespace session {
closeSessionInterface()
{ /////////////////////// TICKET #699
INFO (session, "closing session interfaces.");
TODO ("actually close session interfaces :) and don't babble in the log when NOT closing anything...");
}

View file

@ -15,7 +15,22 @@ PLANNED "BuildSegment_test" BuildSegment_test <<END
END
PLANNED "detecting Fixture changes" FixtureChangeDetector_test <<END
return: 0
END
TEST "ModelPort registry" ModelPortRegistry_test <<END
return: 0
END
TEST "Output pipe mapping" OutputMapping_test <<END
return: 0
END
PLANNED "Segmentation datastructure" SegmentationDatastructure_test <<END
return: 0
END

View file

@ -73,6 +73,9 @@ test_components_SOURCES = \
$(testcomponents_srcdir)/proc/engine/node-source-test.cpp \
$(testcomponents_srcdir)/proc/mobject/builder/buildertooltest.cpp \
$(testcomponents_srcdir)/proc/mobject/builder/buildsegmenttest.cpp \
$(testcomponents_srcdir)/proc/mobject/builder/model-port-registry-test.cpp \
$(testcomponents_srcdir)/proc/mobject/builder/fixture-change-detector-test.cpp \
$(testcomponents_srcdir)/proc/mobject/builder/segmentation-datastructure-test.cpp\
$(testcomponents_srcdir)/proc/mobject/controller/rendersegmenttest.cpp \
$(testcomponents_srcdir)/proc/mobject/mobject-interface-test.cpp \
$(testcomponents_srcdir)/proc/mobject/mobject-ref-test.cpp \

View file

@ -42,13 +42,13 @@ using std::string;
using std::cout;
namespace asset
{
namespace test
{
namespace asset {
namespace test {
using mobject::Session;
using lumiera::Query;
using lumiera::query::normaliseID;
using lumiera::StreamType;
@ -88,7 +88,7 @@ namespace asset
CHECK (thePipe);
CHECK (thePipe->getProcPatt());
CHECK (thePipe->getPipeID() == pID_sane);
CHECK (thePipe->getStreamID() == sID);
CHECK (thePipe->getStreamID() == StreamType::ID(sID));
CHECK (thePipe->shortDesc == pID_sane);
Asset::Ident idi = thePipe->ident;
@ -147,7 +147,7 @@ namespace asset
string sID = pipe1->getStreamID(); // sort of a "default stream type"
PPipe pipe3 = Pipe::query ("stream("+sID+")");
CHECK (pipe3);
CHECK (pipe3->getStreamID() == sID);
CHECK (pipe3->getStreamID() == StreamType::ID(sID));
CHECK (pipe3->getProcPatt() == Session::current->defaults (Query<const ProcPatt>("stream("+sID+")")));
}

View file

@ -35,74 +35,70 @@ using util::isnil;
using std::string;
namespace asset
{
namespace test
namespace asset {
namespace test {
/***********************************************************************
* @test creating several Assets and checking object identity,
* detection of duplicates and version handling.
* @see proc_interface::AssetManager#reg
*/
class IdentityOfAssets_test : public Test
{
/***********************************************************************
* @test creating several Assets and checking object identity,
* detection of duplicates and version handling.
* @see proc_interface::AssetManager#reg
*/
class IdentityOfAssets_test : public Test
{
virtual void run(Arg arg)
{
createDuplicate();
if (!isnil (arg))
dumpAssetManager();
TRACE (asset_mem, "leaving IdentityOfAssets_test::run()");
}
typedef shared_ptr<asset::Media> PM;
/** @test produce an ID clash.
* documents the current behaviour of the code as of 9/07
* @todo this test is expected to break when the detection
* of duplicate registrations is implemented.
*/
void createDuplicate()
{
PM mm1 = asset::Media::create ("testfile1.mov", VIDEO);
Asset::Ident idi (mm1->ident); // duplicate Ident record
PM mm1X = asset::Media::create (idi); // note: we actually don't call any ctor
CHECK (mm1 == mm1X); // instead, we got mm1 back.
PM mm2 = asset::Media::create (idi,"testfile2.mov");
CHECK (mm1->getID() == mm2->getID()); // different object, same hash
AssetManager& aMang = AssetManager::instance();
CHECK (aMang.getAsset (mm1->getID()) == mm2); // record of mm1 was replaced by mm2
CHECK (aMang.getAsset (mm2->getID()) == mm2);
CHECK (aMang.known (mm1->getID()));
CHECK (aMang.known (mm2->getID()));
CHECK (mm1->ident.name == "testfile1");
CHECK (mm2->ident.name == "testfile1");
CHECK (mm1->getFilename() == "testfile1.mov");
CHECK (mm2->getFilename() == "testfile2.mov");
TRACE (asset_mem, "leaving test method scope");
}
};
/** Register this test class... */
LAUNCHER (IdentityOfAssets_test, "unit asset");
} // namespace test
} // namespace asset
virtual void run(Arg arg)
{
createDuplicate();
if (!isnil (arg))
dumpAssetManager();
TRACE (asset_mem, "leaving IdentityOfAssets_test::run()");
}
typedef shared_ptr<asset::Media> PM;
/** @test produce an ID clash.
* documents the current behaviour of the code as of 9/07
* @todo this test is expected to break when the detection
* of duplicate registrations is implemented.
*/
void createDuplicate()
{
PM mm1 = asset::Media::create ("testfile1.mov", VIDEO);
Asset::Ident idi (mm1->ident); // duplicate Ident record
PM mm1X = asset::Media::create (idi); // note: we actually don't call any ctor
CHECK (mm1 == mm1X); // instead, we got mm1 back.
PM mm2 = asset::Media::create (idi,"testfile2.mov");
CHECK (mm1->getID() == mm2->getID()); // different object, same hash
AssetManager& aMang = AssetManager::instance();
CHECK (aMang.getAsset (mm1->getID()) == mm2); // record of mm1 was replaced by mm2
CHECK (aMang.getAsset (mm2->getID()) == mm2);
CHECK (aMang.known (mm1->getID()));
CHECK (aMang.known (mm2->getID()));
CHECK (mm1->ident.name == "testfile1");
CHECK (mm2->ident.name == "testfile1");
CHECK (mm1->getFilename() == "testfile1.mov");
CHECK (mm2->getFilename() == "testfile2.mov");
TRACE (asset_mem, "leaving test method scope");
}
};
/** Register this test class... */
LAUNCHER (IdentityOfAssets_test, "unit asset");
}} // namespace asset::test

View file

@ -36,29 +36,29 @@
namespace control {
namespace test {
using lib::Symbol;
using std::tr1::function;
using std::rand;
namespace { // test data and helpers...
Symbol TEST_CMD = "test.command1.handling";
HandlingPattern::ID TEST_PATTERN = HandlingPattern::DUMMY;
}
typedef shared_ptr<CommandImpl> PCommandImpl;
typedef HandlingPattern const& HaPatt;
/**********************************************************************************
* @test operate and verify a simple dummy command handling pattern.
* @note this test covers mainly the behaviour of a handling pattern as a concept,
* not so much the behaviour of the (standard) handling pattern implementations.
*
*
* @see HandlingPattern
* @see BasicHandlingPattern
* @see command.hpp
@ -66,28 +66,28 @@ namespace test {
*/
class HandlingPatternBasics_test : public Test
{
uint cnt_inst;
virtual void
run (Arg)
{
CommandRegistry& registry = CommandRegistry::instance();
CHECK (&registry);
cnt_inst = registry.instance_count();
{
PCommandImpl pCom = buildTestCommand(registry);
checkExec (pCom);
checkUndo (pCom);
}
CHECK (cnt_inst == registry.instance_count());
}
/** create a command implementation frame usable for tests.
* This simulates what normally happens within a CommandDef.
* The created CommandImpl isn't registered, and thus will
@ -96,74 +96,74 @@ namespace test {
PCommandImpl
buildTestCommand (CommandRegistry& registry)
{
typedef void Sig_oper(int);
typedef long Sig_capt(int);
typedef void Sig_undo(int,long);
function<Sig_oper> o_Fun (command1::operate);
function<Sig_capt> c_Fun (command1::capture);
function<Sig_undo> u_Fun (command1::undoIt);
CHECK (o_Fun && c_Fun && u_Fun);
// when the CommandDef is complete, it issues the
// allocation call to the registry behind the scenes....
PCommandImpl pImpl = registry.newCommandImpl(o_Fun,c_Fun,u_Fun);
CHECK (pImpl);
CHECK (*pImpl);
return pImpl;
}
void
checkExec (PCommandImpl com)
{
CHECK (com);
CHECK (!com->canExec());
typedef Types<int> ArgType;
const int ARGU (1 + rand() % 1000);
Tuple<ArgType> tuple(ARGU);
TypedArguments<Tuple<ArgType> > arg(tuple);
com->setArguments(arg);
CHECK (com->canExec());
CHECK (!com->canUndo());
command1::check_ = 0;
HaPatt patt = HandlingPattern::get(TEST_PATTERN);
ExecResult res = patt.invoke(*com, TEST_CMD);
CHECK (res);
CHECK (ARGU == command1::check_);
CHECK (com->canUndo());
}
void
checkUndo (PCommandImpl com)
{
CHECK (com);
CHECK (com->canExec());
CHECK (com->canUndo());
CHECK (command1::check_ > 0);
HaPatt ePatt = HandlingPattern::get(TEST_PATTERN);
HaPatt uPatt = ePatt.howtoUNDO();
ExecResult res = uPatt.invoke(*com, TEST_CMD);
CHECK (res);
CHECK (command1::check_ == 0);
}
};
/** Register this test class... */
LAUNCHER (HandlingPatternBasics_test, "function controller");
}} // namespace control::test

View file

@ -99,7 +99,7 @@ namespace test {
MemHolder mementoHolder (undo_func,cap_func);
CHECK (sizeof(MemHolder) <= sizeof(int) // storage for the memento
CHECK (sizeof(MemHolder) <= sizeof(int) // storage for the memento
+ 2 * sizeof(function<void()>) // storage for the 2 undecorated functors
+ ALIGNMENT);
@ -160,10 +160,10 @@ namespace test {
CHECK (m22 != m11);
CHECK (m22 != m12);
CHECK (m22 != m21);
MemHolder m22x (m22); // clone copy
CHECK (!m22x);
CHECK (m22 == m22x); // same functions, no state --> equal
CHECK (m22 == m22x); // same functions, no state --> equal
testVal = 0;
m22x.tieCaptureFunc() (1 + (rand() % 9)); // produce a random memento value != 0

View file

@ -0,0 +1,113 @@
/*
FixtureChangeDetector(Test) - detecting segmentation differences
Copyright (C) Lumiera.org
2010, 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.
* *****************************************************/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "proc/mobject/builder/fixture-change-detector.hpp"
#include "proc/asset/timeline.hpp"
#include "proc/asset/pipe.hpp"
#include "lib/query.hpp"
#include "lib/util.hpp"
namespace mobject {
namespace builder {
namespace test {
// using util::isSameObject;
// using util::isnil;
//
using asset::Pipe;
using asset::PPipe;
using asset::Struct;
using asset::Timeline;
using asset::PTimeline;
using lumiera::Query;
//
typedef asset::ID<Pipe> PID;
typedef asset::ID<Struct> TID;
//
// typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor;
namespace { // test environment
inline PID
getPipe (string id)
{
return Pipe::query("id("+id+")");
}
inline TID
getTimeline (string id)
{
return asset::Struct::retrieve (Query<Timeline> ("id("+id+")"))->getID();
}
struct TestContext
{
/** setup */
TestContext()
{ }
/** tear-down */
~TestContext()
{
}
};
}
/*********************************************************************************
* @test TODO blubb
*
* @see mobject::builder::FixtureChangeDetector
*/
class FixtureChangeDetector_test : public Test
{
virtual void
run (Arg)
{
TestContext ctx;
}
void
fabricating_ModelPorts ()
{
}
};
/** Register this test class... */
LAUNCHER (FixtureChangeDetector_test, "unit builder");
}}} // namespace mobject::builder::test

View file

@ -0,0 +1,275 @@
/*
ModelPortRegistry(Test) - verify handling of model ports
Copyright (C) Lumiera.org
2010, 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.
* *****************************************************/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "proc/mobject/builder/model-port-registry.hpp"
#include "proc/asset/timeline.hpp"
#include "proc/asset/pipe.hpp"
#include "lib/query.hpp"
#include "lib/util.hpp"
namespace mobject {
namespace builder {
namespace test {
using util::isSameObject;
using util::isnil;
using asset::Pipe;
using asset::PPipe;
using asset::Struct;
using asset::Timeline;
using asset::PTimeline;
using lumiera::Query;
typedef asset::ID<Pipe> PID;
typedef asset::ID<Struct> TID;
typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor;
namespace { // test environment
inline PID
getPipe (string id)
{
return Pipe::query("id("+id+")");
}
inline TID
getTimeline (string id)
{
return asset::Struct::retrieve (Query<Timeline> ("id("+id+")"))->getID();
}
struct TestContext
{
ModelPortRegistry registry_;
ModelPortRegistry* previous_;
/** setup */
TestContext()
: registry_()
, previous_(ModelPortRegistry::setActiveInstance (registry_))
{ }
/** tear-down */
~TestContext()
{
if (previous_)
ModelPortRegistry::setActiveInstance (*previous_);
else
ModelPortRegistry::shutdown();
}
};
}
/*********************************************************************************
* @test create a standalone model port registry to verify the behaviour of
* model ports, accessed through reference handles. This test provides an
* example setup detached from the real usage situation within the builder.
* The ModelPortRegistry management interface is used to create and track a
* set of model ports, to be made visible by an atomic, transactional switch.
* The access for client code through the ModelPort frontend is then verified.
*
* @see mobject::ModelPort
* @see mobject::builder::ModelPortRegistry
*/
class ModelPortRegistry_test : public Test
{
virtual void
run (Arg)
{
TestContext ctx;
fabricating_ModelPorts (ctx.registry_);
accessing_ModelPorts();
transactionalSwitch (ctx.registry_);
}
void
fabricating_ModelPorts (ModelPortRegistry& registry)
{
/* == some Assets to play with == */
PID pipeA = getPipe ("pipeA");
PID pipeB = getPipe ("pipeB");
TID someTimeline = getTimeline ("some_test_Timeline");
// start out with defining some new model ports......
MPDescriptor p1 = registry.definePort (pipeA, someTimeline);
MPDescriptor p2 = registry.definePort (pipeB, someTimeline);
CHECK (registry.contains (pipeA));
CHECK (registry.contains (pipeB));
VERIFY_ERROR (DUPLICATE_MODEL_PORT, registry.definePort(pipeB, someTimeline) );
CHECK (registry.contains (pipeB));
CHECK (pipeA == p1.id());
CHECK (pipeB == p2.id());
CHECK (someTimeline == p1.holder());
CHECK (someTimeline == p2.holder());
registry.commit();
}
void
accessing_ModelPorts ()
{
PID pipeA = getPipe ("pipeA");
PID pipeB = getPipe ("pipeB");
PID pipeWC = getPipe ("WCpipe");
ModelPort mp1(pipeA);
ModelPort mp2(pipeB);
VERIFY_ERROR (INVALID_MODEL_PORT, ModelPort unbefitting(pipeWC) );
ModelPort mp1x(pipeA); // can be created multiple times
ModelPort mp2x(mp1x); // can be copied at will
ModelPort mpNull; // can be default constructed (->unconnected)
CHECK (mp1);
CHECK (mp2);
CHECK (mp1x);
CHECK (!mpNull); // bool check verifies setup and connected state
CHECK ( ModelPort::exists (pipeA)); // this is the same check, but invoked just with an pipe-ID
CHECK ( ModelPort::exists (pipeB));
CHECK (!ModelPort::exists (pipeWC));
CHECK (mp1 == mp1x);
CHECK (!isSameObject (mp1, mp1x));
CHECK (mp1 != mp2);
CHECK (mp2 != mp1);
CHECK (mp1 != mpNull);
CHECK (mp2 != mpNull);
CHECK (mp1.pipe() == pipeA);
CHECK (mp2.pipe() == pipeB);
CHECK (mp1x.pipe() == pipeA);
VERIFY_ERROR (UNCONNECTED_MODEL_PORT, mpNull.pipe()); // any further operations on an unconnected port will throw
VERIFY_ERROR (UNCONNECTED_MODEL_PORT, mpNull.holder());
CHECK (mp1.streamType() == pipeA.streamType());
}
void
transactionalSwitch (ModelPortRegistry& registry)
{
PID pipeA = getPipe ("pipeA");
PID pipeB = getPipe ("pipeB");
PID pipeWC = getPipe ("WCpipe");
CHECK ( ModelPort::exists (pipeB));
CHECK (!ModelPort::exists (pipeWC));
CHECK (ModelPort::exists (pipeA));
CHECK (registry.contains (pipeA));
registry.remove (pipeA);
CHECK (!registry.contains (pipeA)); // removed from the current (pending) transaction
CHECK ( ModelPort::exists (pipeA)); // but not yet publicly visible
// now create a new and differing definition of port A
TID anotherTimeline = getTimeline ("another_test_Timeline");
MPDescriptor p1 = registry.definePort (pipeA, anotherTimeline);
CHECK (registry.contains (pipeA));
CHECK (anotherTimeline == p1.holder());
CHECK (ModelPort(pipeA).holder() != anotherTimeline);
registry.remove (pipeB); // some more wired definitions
registry.definePort (pipeWC, anotherTimeline);
CHECK (!registry.contains (pipeB));
CHECK ( registry.contains (pipeWC));
CHECK ( ModelPort::exists (pipeB));
CHECK (!ModelPort::exists (pipeWC));
CHECK ( registry.isRegistered (pipeB)); // this is the same as ModelPort::exists
CHECK (!registry.isRegistered (pipeWC)); //
// Note: pending transaction not yet committed
ModelPort portA(pipeA); // ...... thus the changes aren't reflected to client code
ModelPort portB(pipeB);
VERIFY_ERROR (INVALID_MODEL_PORT, ModelPort ineptly(pipeWC));
CHECK (portA);
CHECK (portB);
CHECK (portA.pipe() == pipeA);
CHECK (portB.pipe() == pipeB);
CHECK (portA.holder() != anotherTimeline);
registry.commit();
CHECK ( ModelPort::exists (pipeA)); // now all our changes got publicly visible
CHECK (!ModelPort::exists (pipeB));
CHECK ( ModelPort::exists (pipeWC));
CHECK ( portA);
CHECK (!portB);
CHECK (portA.holder() == anotherTimeline);
CHECK (portA.pipe() == pipeA);
VERIFY_ERROR (INVALID_MODEL_PORT, portB.pipe());
ModelPort pwc(pipeWC); // now clients may also use the now officially promoted new port
CHECK (pwc);
CHECK (pwc.pipe() == pipeWC);
CHECK (pwc.holder() == anotherTimeline);
// Next: doing several changes,
// but finally *not* committing them...
CHECK ( registry.contains (pipeA));
CHECK (!registry.contains (pipeB));
CHECK ( registry.contains (pipeWC));
registry.remove (pipeA);
registry.clear(); // remove everything from the pending transaction
CHECK (!registry.contains (pipeA));
CHECK (!registry.contains (pipeB));
CHECK (!registry.contains (pipeWC));
registry.definePort (pipeB, anotherTimeline);
CHECK ( registry.contains (pipeB));
CHECK (!portB); // not committed and thus not visible
CHECK (portA);
CHECK (pwc);
registry.rollback();
CHECK ( registry.contains (pipeA)); // no effect to the officialy visible state
CHECK (!registry.contains (pipeB));
CHECK ( registry.contains (pipeWC));
VERIFY_ERROR(INVALID_MODEL_PORT, registry.get(pipeB) );
CHECK (!portB);
}
};
/** Register this test class... */
LAUNCHER (ModelPortRegistry_test, "unit session builder");
}}} // namespace mobject::builder::test

View file

@ -0,0 +1,113 @@
/*
SegmentationDatastructure(Test) - verify basic properties of the Segmentation
Copyright (C) Lumiera.org
2010, 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.
* *****************************************************/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "proc/mobject/builder/fixture-change-detector.hpp"
#include "proc/asset/timeline.hpp"
#include "proc/asset/pipe.hpp"
#include "lib/query.hpp"
#include "lib/util.hpp"
namespace mobject {
namespace builder {
namespace test {
// using util::isSameObject;
// using util::isnil;
//
using asset::Pipe;
using asset::PPipe;
using asset::Struct;
using asset::Timeline;
using asset::PTimeline;
using lumiera::Query;
//
typedef asset::ID<Pipe> PID;
typedef asset::ID<Struct> TID;
//
// typedef ModelPortRegistry::ModelPortDescriptor const& MPDescriptor;
namespace { // test environment
inline PID
getPipe (string id)
{
return Pipe::query("id("+id+")");
}
inline TID
getTimeline (string id)
{
return asset::Struct::retrieve (Query<Timeline> ("id("+id+")"))->getID();
}
struct TestContext
{
/** setup */
TestContext()
{ }
/** tear-down */
~TestContext()
{
}
};
}
/*********************************************************************************
* @test TODO blubb
*
* @see mobject::builder::FixtureChangeDetector
*/
class SegmentationDatastructure_test : public Test
{
virtual void
run (Arg)
{
TestContext ctx;
}
void
fabricating_ModelPorts ()
{
}
};
/** Register this test class... */
LAUNCHER (SegmentationDatastructure_test, "unit builder");
}}} // namespace mobject::builder::test

View file

@ -45,19 +45,19 @@ using std::endl;
namespace mobject {
namespace test {
namespace { // shortcut for checking use-counts
bool
checkUseCount (size_t cnt, uint additional=0)
{
static uint base_count=0;
if (!additional) // called for init
base_count = cnt;
return cnt == base_count + additional;
}
template<class REF>
bool
checkUseCount (REF const& ref, uint additional)
@ -65,17 +65,17 @@ namespace test {
return checkUseCount(ref.use_count(), additional);
}
}
using lumiera::Time;
using session::Clip;
using session::PMedia;
using session::SessionServiceMockIndex;
using session::PPIdx;
/***************************************************************************
* @test properties and behaviour of the external reference-mechanism for
* MObjects placed into the session. We create a simple, simulated
@ -88,49 +88,49 @@ namespace test {
*/
class MObjectRef_test : public Test
{
typedef Placement<MObject> PMObj;
typedef Placement<Clip> PClip;
typedef TestPlacement<TestSubMO1> PSub1;
virtual void
run (Arg)
{
// create data simulating a "Session"
PMObj testClip1 = asset::Media::create("test-1", asset::VIDEO)->createClip();
PMObj testClip2 = asset::Media::create("test-2", asset::VIDEO)->createClip();
// set up a tie to fixed start positions (i.e. "properties of placement")
testClip1.chain(Time(10));
testClip2.chain(Time(20));
CHECK (testClip1->isValid());
CHECK (testClip2->isValid());
CHECK (2 == testClip1.use_count()); // one by the placement and one by the clip-Asset
CHECK (2 == testClip2.use_count());
// Prepare an (test)Index
PPIdx index = SessionServiceMockIndex::install();
PMO& root = index->getRoot();
// Add the Clips to "session" (here: dummy index)
PMObj::ID clip1ID = index->insert (testClip1, root);
PMObj::ID clip2ID = index->insert (testClip2, root);
CHECK (2 == index->size());
// use the IDs returned on insertion to fetch
// language references to the placement instance within the index
// we'll use these language refs as base to create MObejctRef handles.
PMObj& pClip1 = index->find(clip1ID);
PMObj& pClip2 = index->find(clip2ID);
CHECK (3 == pClip1.use_count()); // clip-Asset, original placement, new placement in index
CHECK (3 == pClip1.use_count()); // clip-Asset, original placement, new placement in index
CHECK (3 == pClip2.use_count());
checkUseCount(pClip1.use_count()); // set ref point for later checks
// extract various kinds of IDs and refs
PMObj & rP1 (pClip1);
PMObj const& rP2 (pClip2);
@ -140,8 +140,8 @@ namespace test {
///////////////////////////////////////////TODO: check the C-API representation here
PlacementRef<Clip> ref1 (id1);
PlacementRef<MObject> ref2 (pClip2);
// -----Tests------------------
checkBuildMObjectRef (rP1, &pClip1);
checkBuildMObjectRef (rP2, &pClip2);
@ -151,12 +151,12 @@ namespace test {
///////////////////////////////////////////TODO: check the C-API representation here
checkBuildMObjectRef (ref1, &pClip1);
checkBuildMObjectRef (ref2, &pClip2);
checkComparison(rP1,rP2);
checkLifecycle (rP1,rP2);
checkTypeHandling (luid);
// -----Tests------------------
// verify clean state
index->remove (pClip1);
index->remove (pClip2); // note: this invalidates pClip1 and pClip2;
@ -168,24 +168,24 @@ namespace test {
CHECK (2 == testClip2.use_count());
index.reset();
}
template<typename REF>
void
checkBuildMObjectRef (REF& refObj, void* placementAdr)
{
MORef<Clip> rMO;
CHECK (!rMO); // still empty (not bound)
CHECK (!rMO); // still empty (not bound)
CHECK (0==rMO.use_count());
cout << string(rMO) << endl; /////////////////////TICKET #527
cout << showSizeof(rMO) << endl;
// activate by binding to provided ref
rMO.activate(refObj);
CHECK (rMO); // now bound
cout << string(rMO) << endl; /////////////////////TICKET #527
// access MObject (Clip API)
// cout << rMO->operator string() << endl; /////////////////////TICKET #428
PMedia media = rMO->getMedia();
@ -193,45 +193,45 @@ namespace test {
Time mediaLength = media->getLength();
CHECK (Time(0) < mediaLength);
CHECK (rMO->isValid());
// access the Placement-API
CHECK (checkUseCount(rMO, 1)); // now rMO shares ownership with the Placement --> use-count += 1
CHECK (Time(0) < rMO.getStartTime()); // (internally, this resolves to an ExplicitPlacement) /////////TICKET #332
CHECK (checkUseCount(rMO, 1)); // now rMO shares ownership with the Placement --> use-count += 1
CHECK (Time(0) < rMO.getStartTime()); // (internally, this resolves to an ExplicitPlacement) /////////TICKET #332
CHECK ( rMO.isCompatible<MObject>());
CHECK ( rMO.isCompatible<Clip>());
CHECK (!rMO.isCompatible<TestSubMO1>());
Time start = rMO.getStartTime();
// re-link to the Placement (note we get the Clip API!)
Placement<Clip> & refP = rMO.getPlacement();
CHECK (refP.isValid());
CHECK (refP.use_count() == rMO.use_count());
CHECK (checkUseCount(refP, 1)); // use count not changed
CHECK (&refP == placementAdr); // actually denotes the address of the original Placement in the "session"
CHECK (checkUseCount(refP, 1)); // use count not changed
CHECK (&refP == placementAdr); // actually denotes the address of the original Placement in the "session"
cout << string(refP) << endl;
ExplicitPlacement exPla = refP.resolve();
CHECK (exPla.time == start); // recovered Placement resolves to the same time as provided by the proxied API
CHECK (checkUseCount(refP, 2)); // but now we've indeed created an additional owner (exPla)
CHECK (exPla.time == start); // recovered Placement resolves to the same time as provided by the proxied API
CHECK (checkUseCount(refP, 2)); // but now we've indeed created an additional owner (exPla)
CHECK (checkUseCount(rMO, 2));
}
void
checkComparison (PMO const& p1, PMO const& p2)
{
PlacementRef<Clip> pRef1 (p1);
PlacementRef<MObject> pRef2 (p2);
MORef<MObject> rM;
MORef<Clip> rC;
rM.activate (p1);
rC.activate (p2);
CHECK (rM && rC);
CHECK (!(rM == rC) && !(rC == rM));
CHECK ( (rM != rC) && (rC != rM));
// mixed comparisons
CHECK ( (rM == pRef1) && (pRef1 == rM));
CHECK ( (rC == pRef2) && (pRef2 == rC));
@ -241,7 +241,7 @@ namespace test {
CHECK ( (rC != pRef1) && (pRef1 != rC));
CHECK (!(rM == pRef2) && !(pRef2 == rM));
CHECK (!(rC == pRef1) && !(pRef1 == rC));
CHECK ( (rM == p1.getID()) );
CHECK ( (rC == p2.getID()) );
CHECK (!(rM != p1.getID()) );
@ -250,125 +250,125 @@ namespace test {
CHECK ( (rC != p1.getID()) );
CHECK (!(rM == p2.getID()) );
CHECK (!(rC == p1.getID()) );
rC.activate (pRef1);
CHECK ( (rM == rC) && (rC == rM));
CHECK (!(rM != rC) && !(rC != rM));
CHECK ( (rC == pRef1) && (pRef1 == rC));
CHECK (!(rC != pRef1) && !(pRef1 != rC));
CHECK ( (rC != pRef2) && (pRef2 != rC));
CHECK (!(rC == pRef2) && !(pRef2 == rC));
CHECK ( (rC == p1.getID()) );
CHECK (!(rC != p1.getID()) );
CHECK ( (rC != p2.getID()) );
CHECK (!(rC == p2.getID()) );
rM.close();
CHECK (!rM);
CHECK (!(rM == rC) && !(rC == rM));
CHECK ( (rM != rC) && (rC != rM));
CHECK (!(rM == pRef1) && !(pRef1 == rM));
CHECK ( (rM != pRef1) && (pRef1 != rM));
CHECK ( (rM != pRef2) && (pRef2 != rM));
CHECK (!(rM == pRef2) && !(pRef2 == rM));
CHECK (!(rM == p1.getID()) );
CHECK ( (rM != p1.getID()) );
CHECK ( (rM != p2.getID()) );
CHECK (!(rM == p2.getID()) );
}
void
checkLifecycle (PMObj const& p1, PMObj const& p2)
{
CHECK (checkUseCount(p1, 0));
CHECK (checkUseCount(p2, 0));
MORef<Clip> rMO;
CHECK (!rMO);
CHECK (0 == rMO.use_count());
rMO.activate(p1);
CHECK (rMO);
CHECK (rMO->getMedia()->getFilename() == "test-1");
CHECK (checkUseCount(rMO, 1));
CHECK (checkUseCount(p1, 1)); // sharing ownership
CHECK (checkUseCount(p2, 0));
rMO.activate(p2);
CHECK (rMO);
CHECK (rMO->getMedia()->getFilename() == "test-2");
CHECK (checkUseCount(rMO, 1));
CHECK (checkUseCount(p1, 0)); // detached, use count dropped to previous value
CHECK (checkUseCount(p2, 1)); // sharing ownership
rMO.activate(p2);
CHECK (checkUseCount(rMO, 1)); // no change
rMO.close();
CHECK (!rMO);
CHECK (checkUseCount(p1, 0));
CHECK (checkUseCount(p2, 0));
VERIFY_ERROR (BOTTOM_MOBJECTREF, rMO.getPlacement() );
VERIFY_ERROR (BOTTOM_MOBJECTREF, rMO->getMedia() );
}
void
checkTypeHandling (LumieraUid luid)
{
MObjectRef rMObj;
MORef<Clip> rClip;
MORef<TestSubMO1> rSub1;
CHECK ( ! rMObj.use_count());
CHECK ( ! rClip.use_count());
CHECK ( ! rSub1.use_count());
rMObj.activate(luid);
CHECK (checkUseCount(rMObj, 1));
CHECK ( ! rClip.use_count());
CHECK ( ! rSub1.use_count());
rClip.activate(rMObj); // attach on existing MObjectRef
CHECK (checkUseCount(rMObj, 2));
CHECK (checkUseCount(rClip, 2));
CHECK ( ! rSub1.use_count());
// impossible, because Clip isn't a subclass of TestSubMO1:
VERIFY_ERROR (INVALID_PLACEMENTREF, rSub1.activate(luid) );
VERIFY_ERROR (INVALID_PLACEMENTREF, rSub1 = rMObj );
CHECK (rMObj->isValid());
CHECK (rClip->isValid());
CHECK (rMObj.getPlacement().getID() == rClip.getPlacement().getID());
// doesn't compile, because the function isn't on MObject API:
// rMObj->getMedia();
rClip.close();
CHECK (checkUseCount(rMObj, 1));
CHECK ( ! rClip.use_count());
// can assign, because the actual type is checked:
rClip = rMObj;
CHECK (checkUseCount(rMObj, 2));
CHECK (checkUseCount(rClip, 2));
cout << string(rClip) << endl; //////////TICKET #527
cout << string(rClip->getMedia()->ident) << endl; //////////TICKET #520
}
};
/** Register this test class... */
LAUNCHER (MObjectRef_test, "unit session");
}} // namespace mobject::test

View file

@ -76,7 +76,7 @@ namespace test {
{
PPipe srcP = Pipe::lookup (sourcePipeID);
format queryPattern ("id(master_%1%), stream(%1%), ord(%2%)");
return Query<Pipe> (queryPattern % srcP->getStreamID() % seqNr);
return Query<Pipe> (str(queryPattern % srcP->getStreamID().getSym() % seqNr));
}
};

View file

@ -44,10 +44,10 @@ using std::endl;
namespace mobject {
namespace session {
namespace test {
using namespace mobject::test;
typedef TestPlacement<TestSubMO21> PSub;
typedef PlacementMO::ID P_ID;
@ -65,68 +65,68 @@ namespace test {
*/
class PlacementRef_test : public Test
{
virtual void
run (Arg)
{
PSub testPlacement1(*new TestSubMO21);
PSub testPlacement2(*new TestSubMO21);
testPlacement2.chain(Time(2)); // define start time of Placement-2 to be at t=2
// Prepare an (test)Index backing the PlacementRefs
PPIdx index = SessionServiceMockIndex::install();
PMO& root = index->getRoot();
P_ID id1 = index->insert (testPlacement1, root);
P_ID tmpID = index->insert (testPlacement2, root);
CHECK (2 == index->size());
// References to the "live" placements within our test index
PMO& p1 = index->find(id1);
PMO& p2 = index->find(tmpID);
PlacementMO::Id<TestSubMO21> id2 = p2.recastID<TestSubMO21>();
CHECK (id2);
CHECK (id2 != p1.getID());
// create placement refs
PlacementRef<TestSubMO21> ref1 (p1);
PlacementRef<TestSubMO21> ref2 (id2);
PlacementRef<MObject> refX (ref2);
CHECK (ref1);
CHECK (ref2);
CHECK (refX);
CHECK (ref1 != ref2);
CHECK (ref2 == refX);
// indeed a "reference": resolves to the same memory location
CHECK (isSameObject (p1, *ref1));
CHECK (isSameObject (p2, *ref2));
CHECK (isSameObject (p2, *refX));
cout << string(*ref1) << endl;
cout << string(*ref2) << endl;
cout << string(*refX) << endl;
// PlacementRef mimics placement behaviour
ref1->specialAPI();
CHECK (2 == ref1.use_count());
CHECK (2 == ref2.use_count());
ExplicitPlacement exPla = refX.resolve();
CHECK (exPla.time == Time(2)); // indeed get back the time we set on p2 above
CHECK (3 == ref2.use_count()); // exPla shares ownership with p2
CHECK (index->contains(ref1)); // ref can stand-in for a placement-ID
CHECK (sizeof(id2) == sizeof(ref2)); // (and is actually implemented based on an ID)
CHECK (exPla.time == Time(2)); // indeed get back the time we set on p2 above
CHECK (3 == ref2.use_count()); // exPla shares ownership with p2
CHECK (index->contains(ref1)); // ref can stand-in for a placement-ID
CHECK (sizeof(id2) == sizeof(ref2)); // (and is actually implemented based on an ID)
// assignment on placement refs
refX = ref1;
CHECK (ref1 != ref2);
CHECK (ref1 == refX);
CHECK (ref2 != refX);
// re-assignment with a new placement
refX = p2;
CHECK (refX == ref2);
@ -135,7 +135,7 @@ namespace test {
CHECK (refX == ref1);
CHECK (refX != ref2);
CHECK (isSameObject (*refX, p1));
LumieraUid luid2 (p2.getID().get());
refX = luid2; // assignment works even based on a plain LUID
ref2 = ref1;
@ -151,40 +151,40 @@ namespace test {
CHECK (ref1 != ref2);
CHECK (ref1 == refX);
CHECK (ref2 != refX);
// resolution is indeed "live", we see changes to the referred placement
CHECK (refX.resolve().time == Time::MIN);
p1.chain = p2.chain; // do a change on the placement within index....
CHECK (refX.resolve().time == Time(2)); // now we get the time tie we originally set on p2
CHECK (p1.getID() != p2.getID()); // but the instance identities are still unaltered
CHECK (refX.resolve().time == Time(2)); // now we get the time tie we originally set on p2
CHECK (p1.getID() != p2.getID()); // but the instance identities are still unaltered
CHECK (2 == ref1.use_count());
CHECK (3 == ref2.use_count()); // one more because of shared ownership with exPla
CHECK (3 == ref2.use_count()); // one more because of shared ownership with exPla
// actively removing p2 invalidates the other refs to
index->remove (ref1);
CHECK (!ref1); // checks invalidity without throwing
CHECK (!ref1); // checks invalidity without throwing
CHECK (!refX);
VERIFY_ERROR(NOT_IN_SESSION, *ref1 );
// deliberately create an invalid PlacementRef
PlacementRef<TestSubMO21> bottom;
CHECK (!bottom);
VERIFY_ERROR(BOTTOM_PLACEMENTREF, *bottom );
VERIFY_ERROR(BOTTOM_PLACEMENTREF, bottom->specialAPI() );
VERIFY_ERROR(BOTTOM_PLACEMENTREF, bottom.resolve() );
//consistency check; then reset PlacementRef index to default
CHECK (1 == index->size());
CHECK (index->isValid());
index.reset();
}
};
/** Register this test class... */
LAUNCHER (PlacementRef_test, "unit session");
}}} // namespace mobject::session::test

View file

@ -31,6 +31,7 @@
#include "common/configrules.hpp"
#include "proc/assetmanager.hpp"
#include "proc/mobject/session.hpp"
#include "lib/streamtype.hpp"
#include <boost/format.hpp>
@ -49,9 +50,10 @@ namespace asset {
using lumiera::ConfigRules;
using lumiera::query::QueryHandler;
using lumiera::StreamType;
/** shortcut: query for given Pipe-ID */
bool
find (const string& pID)
@ -94,29 +96,30 @@ namespace asset {
define_and_search ()
{
string sID = newID ("stream");
// create Pipes explicitly
StreamType::ID stID (sID);
// create Pipes explicitly
// (without utilising default queries)
PPipe pipe1 = Struct::retrieve.newPipe (newID("pipe"), newID("stream"));
PPipe pipe2 = Struct::retrieve.newPipe (newID("pipe"), sID );
CHECK (pipe1 != pipe2);
CHECK (sID == pipe2->getStreamID());
CHECK (stID == pipe2->getStreamID());
CHECK (!find (pipe1->getPipeID()), "accidental clash of random test-IDs");
CHECK (!find (pipe2->getPipeID()), "accidental clash of random test-IDs");
// now declare that these objects should be considered "default"
lumiera::query::setFakeBypass(""); /////////////////////////////////////////////////TODO mock resolution
CHECK (Session::current->defaults.define (pipe1, Query<Pipe> (""))); // unrestricted default
lumiera::query::setFakeBypass(""); /////////////////////////////////////////////////TODO mock resolution
CHECK (Session::current->defaults.define (pipe1, Query<Pipe> (""))); // unrestricted default
lumiera::query::setFakeBypass("stream("+sID+")"); ///////////////////////////////////TODO mock resolution
lumiera::query::setFakeBypass("stream("+sID+")"); ///////////////////////////////////TODO mock resolution
CHECK (Session::current->defaults.define (pipe2, Query<Pipe> ("stream("+sID+")")));
CHECK ( find (pipe1->getPipeID()), "failure declaring object as default");
CHECK ( find (pipe2->getPipeID()), "failure declaring object as default");
CHECK (sID != pipe1->getStreamID(), "accidental clash");
CHECK (stID != pipe1->getStreamID(), "accidental clash");
CHECK (!Session::current->defaults.define (pipe1, Query<Pipe> ("stream("+sID+")")));
// can't be registered with this query, due to failure caused by wrong stream-ID
}

View file

@ -32,6 +32,7 @@
#include "proc/asset/pipe.hpp"
#include "proc/assetmanager.hpp"
#include "proc/mobject/session.hpp"
#include "lib/streamtype.hpp"
#include <boost/format.hpp>
@ -49,8 +50,9 @@ namespace test {
using mobject::Session;
using lumiera::Query;
using lumiera::query::normaliseID;
using lumiera::StreamType;
/** shortcut: run just a query
* without creating new instances
*/
@ -117,7 +119,7 @@ namespace test {
retrieveConstrainedDefault (string pID, string sID)
{
PPipe pipe1 = Pipe::query (""); // "the default pipe"
CHECK (sID != pipe1->getStreamID(),
CHECK ( pipe1->getStreamID() != StreamType::ID(sID),
"stream-ID \"%s\" not suitable for test, because "
"the default-pipe \"%s\" happens to have the same "
"stream-ID. We need it to be different",
@ -126,7 +128,7 @@ namespace test {
string query_for_sID ("stream("+sID+")");
PPipe pipe2 = Pipe::query (query_for_sID);
CHECK (sID == pipe2->getStreamID());
CHECK (pipe2->getStreamID() == StreamType::ID(sID));
CHECK (pipe2 != pipe1);
CHECK (pipe2 == Pipe::query (query_for_sID)); // reproducible
}

View file

@ -45,12 +45,12 @@ using std::endl;
namespace mobject {
namespace session {
namespace test {
using session::test::TestClip;
typedef PlacementIndex& Idx;
/***************************************************************************
* @test basic behaviour of the index mechanism used to keep track
* of individual Placements as added to the current Session.
@ -61,56 +61,56 @@ namespace test {
*/
class PlacementIndex_test : public Test
{
virtual void
run (Arg)
{
PlacementIndex index (make_dummyRoot());
CHECK (index.isValid());
checkSimpleInsertRemove (index);
has_size (0, index);
PMO::ID elmID = checkSimpleAccess (index);
CHECK (index.isValid());
has_size (2, index);
checkTypedAccess (index, elmID);
has_size (3, index);
checkScopeHandling (index);
has_size (9, index);
checkContentsEnumeration (index);
has_size (9, index);
CHECK (index.isValid());
index.clear();
has_size (0, index);
CHECK (index.isValid());
}
void
has_size(uint siz, Idx index)
{
CHECK (siz == index.size());
}
void
checkSimpleInsertRemove (Idx index)
{
PMO clip = TestClip::create();
PMO& root = index.getRoot();
CHECK (0 == index.size());
PMO::ID elmID = index.insert (clip, root);
CHECK (1 == index.size());
CHECK ( index.contains (elmID));
CHECK (!index.contains (clip)); // index stores copies
CHECK (!index.contains (clip)); // index stores copies
index.remove(clip); // has no effect
CHECK (1 == index.size());
index.remove(elmID);
@ -118,67 +118,67 @@ namespace test {
CHECK (!index.contains (elmID));
CHECK ( index.contains (root));
}
PMO::ID
checkSimpleAccess (Idx index)
{
PMO testObj = TestClip::create();
PMO& root = index.getRoot();
PMO::ID elmID = index.insert (testObj, root);
PMO& elm = index.find(elmID);
CHECK (elmID == elm.getID());
CHECK (!isSameObject (elm,testObj)); // note: placements are registered as copy
CHECK (isSameDef(elm,testObj)); // they are semantically equivalent ////////TICKET #511
CHECK (elmID != testObj.getID()); // but have a distinct identity
CHECK (!isSameObject (elm,testObj)); // note: placements are registered as copy
CHECK (isSameDef(elm,testObj)); // they are semantically equivalent ////////TICKET #511
CHECK (elmID != testObj.getID()); // but have a distinct identity
PMO::ID elmID2 = index.insert(testObj, root);
CHECK (elmID2 != elmID); // ...and each insert creates a new instance
CHECK (elmID2 != elmID); // ...and each insert creates a new instance
CHECK (testObj != index.find (elmID));
CHECK (testObj != index.find (elmID2));
CHECK (isSameDef(testObj, index.find(elmID)));
CHECK (isSameDef(testObj, index.find(elmID2)));
CHECK (!isSameObject (testObj, index.find(elmID2)));
CHECK (!isSameObject (elm, index.find(elmID2)));
// can repeatedly retrieve a reference to the same object
CHECK ( isSameObject (elm, index.find(elmID )));
CHECK ( isSameObject (elm, index.find(elmID )));
// can also re-access objects by previous ref
CHECK ( isSameObject (elm, index.find(elm)));
return elmID;
}
void
checkTypedAccess (Idx index, PMO::ID elmID)
{
PMO& elm = index.find(elmID);
CHECK (elmID == elm.getID());
typedef Placement<Clip> PClip;
PClip anotherTestClip = TestClip::create();
typedef PlacementMO::Id<Clip> IDClip;
IDClip clipID = index.insert(anotherTestClip, elmID);
// retaining the more specific type info
// access as MObject...
PMO::ID mID = clipID;
PMO& asMO = index.find(mID);
// access as Clip
PClip& asClip = index.find(clipID);
CHECK (LENGTH_TestClip == asClip->getMedia()->getLength()); // using the Clip API
CHECK ( isSameObject(asMO,asClip));
CHECK (!isSameObject(asClip, anotherTestClip)); // always inserting a copy into the PlacementIndex
}
void
checkInvalidRef (Idx index)
{
@ -186,26 +186,26 @@ namespace test {
PlacementMO::ID invalidID (invalid);
CHECK (!bool(invalidID));
CHECK (!bool(invalid));
VERIFY_ERROR(BOTTOM_PLACEMENTREF, index.find(invalid) );
VERIFY_ERROR(BOTTOM_PLACEMENTREF, index.find(invalidID) );
VERIFY_ERROR(BOTTOM_PLACEMENTREF, index.getScope(invalidID) );
CHECK (!index.contains(invalidID));
PMO testObj = TestClip::create();
VERIFY_ERROR(INVALID_SCOPE, index.insert(testObj, invalidID) );
CHECK (false == index.remove(invalidID));
}
void
checkScopeHandling (Idx index)
{
PMO testObj = TestClip::create();
PMO& root = index.getRoot();
typedef PMO::ID ID;
ID e1 = index.insert (testObj, root);
ID e11 = index.insert (testObj, e1);
@ -215,7 +215,7 @@ namespace test {
ID e132 = index.insert (testObj, e13);
ID e133 = index.insert (testObj, e13);
ID e1331 = index.insert (testObj, e133);
CHECK (index.isValid());
CHECK (root == index.getScope(e1));
CHECK (e1 == index.getScope(e11).getID());
@ -227,22 +227,22 @@ namespace test {
CHECK (e133 == index.getScope(e1331).getID());
CHECK (e1 != e13);
CHECK (e13 != e133);
CHECK (index.getScope(e11) == index.getScope(index.find(e11)));
CHECK (index.getScope(e131) == index.getScope(index.find(e131)));
VERIFY_ERROR(NONEMPTY_SCOPE, index.remove(e13) ); // can't remove a scope-constituting element
VERIFY_ERROR(NONEMPTY_SCOPE, index.remove(e133) );
CHECK (index.contains(e1331));
CHECK (index.remove(e1331));
CHECK (!index.contains(e1331));
CHECK (!index.remove(e1331));
CHECK (index.remove(e133)); // but can remove an scope, after emptying it
CHECK (index.remove(e133)); // but can remove an scope, after emptying it
CHECK (!index.contains(e133));
CHECK (index.isValid());
// build a complete new subtree
uint siz = index.size();
ID e1321 = index.insert (testObj, e132);
@ -258,7 +258,7 @@ namespace test {
ID e132142 = index.insert (testObj, e13214);
ID e132143 = index.insert (testObj, e13214);
ID e132144 = index.insert (testObj, e13214);
// ...and kill it recursively in one sway
index.clear (e1321);
CHECK (!index.contains (e1321));
@ -269,11 +269,11 @@ namespace test {
CHECK (siz == index.size());
CHECK (index.isValid());
}
typedef PlacementIndex::iterator Iter;
/** @test drill down into the tree-like structure
* and enumerate the contents of each element, if any
*/
@ -281,14 +281,14 @@ namespace test {
checkContentsEnumeration (Idx index)
{
PMO& root = index.getRoot();
Iter rootContents = index.getReferrers (root.getID());
CHECK (rootContents);
discover (index, rootContents, 0);
}
void
discover (Idx index, Iter iter, uint level)
{
@ -296,30 +296,30 @@ namespace test {
for ( ; iter; ++iter )
{
cout << indent(level) << "::" << string(*iter) << endl;
++count;
Iter scopeContents = index.getReferrers (iter->getID());
if (scopeContents)
discover (index, scopeContents, level+1);
}
static format summary ("...%i elements at Level %i");
cout << indent(level) << summary % count % level << endl;
CHECK (!iter);
CHECK (0 < count);
}
static string
indent (uint level)
{
return string (level, ' ');
}
};
/** Register this test class... */
LAUNCHER (PlacementIndex_test, "unit session");
}}} // namespace mobject::session::test

View file

@ -134,7 +134,7 @@ namespace test {
{
QueryFocus original; // automatically attaches to current stack top
uint num_refs = refs (original);
CHECK (num_refs > 1); // because the run() function also holds a ref
CHECK (num_refs > 1); // because the run() function also holds a ref
QueryFocus subF = QueryFocus::push();
cout << string(subF) << endl;
@ -161,7 +161,7 @@ namespace test {
subF2.pop(); // releasing this focus and re-attaching to what's on stack top
cout << string(subF2) << "<<<--after pop()" << endl;
CHECK (subF2 == subF);
CHECK (2 == refs(subF2)); // both are now attached to the same path
CHECK (2 == refs(subF2)); // both are now attached to the same path
CHECK (2 == refs(subF));
}
// subF2 went out of scope, but no auto-pop happens (because subF is still there)

View file

@ -84,7 +84,7 @@ test_lib_SOURCES = \
$(testlib_srcdir)/sanitised-identifier-test.cpp \
$(testlib_srcdir)/scoped-holder-test.cpp \
$(testlib_srcdir)/scoped-ptrvect-test.cpp \
$(testlib_srcdir)/scopedholdertransfertest.cpp \
$(testlib_srcdir)/scoped-holder-transfer-test.cpp \
$(testlib_srcdir)/singleton-subclass-test.cpp \
$(testlib_srcdir)/singleton-test.cpp \
$(testlib_srcdir)/singletontestmocktest.cpp \
@ -106,7 +106,7 @@ test_lib_SOURCES = \
$(testlib_srcdir)/typed-allocation-manager-test.cpp \
$(testlib_srcdir)/typed-counter-test.cpp \
$(testlib_srcdir)/util-foreach-test.cpp \
$(testlib_srcdir)/vectortransfertest.cpp \
$(testlib_srcdir)/vector-transfer-test.cpp \
$(testlib_srcdir)/visitingtoolconcept.cpp \
$(testlib_srcdir)/visitingtoolextendedtest.cpp \
$(testlib_srcdir)/visitingtooltest.cpp

View file

@ -33,37 +33,37 @@
namespace lib {
namespace test{
using lumiera::P;
using std::string;
using std::tr1::shared_ptr;
using std::tr1::weak_ptr;
using lumiera::error::LUMIERA_ERROR_ASSERTION;
struct X
: boost::totally_ordered<X>
{
long x_;
X(long x=0) : x_(x) {}
operator long () { return x_; }
bool operator< (const X& ox) const { return x_ < ox.x_; }
bool operator== (const X& ox) const { return x_ == ox.x_; }
virtual ~X() {} // using RTTI
};
struct XX : public X
{
long xx_;
XX(long x=0) : X(x), xx_(x+1) {}
};
/****************************************************************
* @test assure correct behaviour of lumiera's custom shared-ptr,
* including ADL of operators, shared ownership, typing
@ -80,8 +80,8 @@ namespace test{
check_type_relations ();
check_ordering ();
}
/** @test smart-ptr basic behaviour */
void
check_refcounting ()
@ -90,84 +90,84 @@ namespace test{
CHECK (p1);
CHECK (1 == p1.use_count());
CHECK (7 == p1->x_);
{
P<X> p2 (new X(9));
CHECK (1 == p2.use_count());
p2.swap (p1);
CHECK (1 == p1.use_count());
CHECK (1 == p2.use_count());
p2 = p1;
CHECK (2 == p1.use_count());
CHECK (2 == p2.use_count());
}
CHECK (1 == p1.use_count());
CHECK (9 == p1->x_);
p1.reset();
CHECK (0 == p1.use_count());
CHECK (!p1);
}
/** @test cooperation with other shared-ptr types */
void
check_shared_ownership ()
{
std::auto_ptr<X> au (new X(22));
CHECK (au.get());
P<X> pX (au);
CHECK (!au.get());
CHECK (pX);
CHECK (1 == pX.use_count());
CHECK (22 == pX->x_);
weak_ptr<X> wX (pX);
CHECK (wX.lock());
CHECK (1 == pX.use_count());
shared_ptr<X> sp1 (wX);
shared_ptr<X> sp2 (pX);
shared_ptr<X> sp3; sp3 = pX;
CHECK (22 == sp3->x_);
CHECK (4 == pX.use_count());
CHECK (*pX == *sp1);
CHECK (*sp1 == *sp2);
CHECK (*sp2 == *sp3);
P<X> pX2;
pX2.swap(pX);
CHECK (!pX);
CHECK (0 == pX.use_count());
CHECK (4 == pX2.use_count());
P<X, P<X> > pXX (pX2); // a different type, but compatible pointers
pX2 = pX;
CHECK (!pX2);
CHECK (0 == pX2.use_count());
CHECK (4 == pXX.use_count());
sp3 = sp2 = sp1 = pX;
CHECK (22 == pXX->x_);
CHECK (1 == pXX.use_count());
CHECK (!sp1);
CHECK (!sp2);
CHECK (!sp3);
CHECK (22 == wX.lock()->x_);
CHECK (1 == pXX.use_count());
pXX.reset();
CHECK (!pXX);
CHECK (!wX.lock());
}
/** @test building type relationships on smart-ptrs */
void
check_type_relations ()
@ -179,34 +179,34 @@ namespace test{
P<XX,shared_ptr<long> > pLo;// Base: shared_ptr<long> (rather nonsense, but well...)
P<X,string> pLoL; // Base: std::string
P<string> pLoLoL; // Base: shared_ptr<string>
CHECK (INSTANCEOF (shared_ptr<X>, &pX));
CHECK ( INSTANCEOF (shared_ptr<XX>, &pX1));
// CHECK (!INSTANCEOF (shared_ptr<X>, &pX1)); // doesn't compile (no RTTI) -- that's correct
// CHECK (!INSTANCEOF (P<X>, &pX1)); // similar, type mismatch detected by compiler
// CHECK (!INSTANCEOF (shared_ptr<X>, &pX1)); // doesn't compile (no RTTI) -- that's correct
// CHECK (!INSTANCEOF (P<X>, &pX1)); // similar, type mismatch detected by compiler
CHECK ( INSTANCEOF (shared_ptr<X>, &pX2));
// CHECK (!INSTANCEOF (shared_ptr<XX>, &pX2));
CHECK ( INSTANCEOF (P<X>, &pX2));
CHECK ( INSTANCEOF (shared_ptr<X>, &pX3));
// CHECK (!INSTANCEOF (shared_ptr<XX>, &pX3));
// CHECK (!INSTANCEOF (P<X>, &pX3));
CHECK ( INSTANCEOF (shared_ptr<long>, &pLo));
// CHECK (!INSTANCEOF (shared_ptr<X>, &pLo));
// CHECK (!INSTANCEOF (P<X>, &pLo));
// CHECK (!INSTANCEOF (shared_ptr<long>, &pLoL));
// CHECK (!INSTANCEOF (shared_ptr<X>, &pLoL));
// CHECK (!INSTANCEOF (P<X>, &pLoL));
CHECK ( INSTANCEOF (string, &pLoL));
CHECK ( INSTANCEOF (shared_ptr<string>, &pLoLoL));
// CHECK (!INSTANCEOF (string, &pLoLoL));
// CHECK (!INSTANCEOF (shared_ptr<X>, &pLoLoL));
pX = pX1; // OK: pointee subtype...
pX = pX2; // invokes shared_ptr<X>::operator= (shared_ptr<X> const&)
pX = pX3;
@ -215,99 +215,99 @@ namespace test{
// pX = pLoLoL; // same...
// you won't be able to do much with the "LoLo"-Types,
// as their types and pointee types's relations don't match
pX.reset (new XX(5));
CHECK (5 == *pX); // implicit conversion from X to long
CHECK (5 == *pX); // implicit conversion from X to long
pX2 = pX; // works, because both are implemented in terms of shared_ptr<X>
CHECK (5 == pX2->x_);
CHECK (6 == pX2->xx_); // using the XX interface (performing dynamic downcast)
CHECK (6 == pX2->xx_); // using the XX interface (performing dynamic downcast)
pX3.reset (new X(7)); // again works because implemented in terms of shared_ptr<X>
pX2 = pX3; // same
CHECK (pX2); // both contain indeed a valid pointer....
CHECK (pX2); // both contain indeed a valid pointer....
CHECK (pX3);
CHECK (! pX2.get()); // but dynamic cast to XX at access fails
CHECK (! pX2.get()); // but dynamic cast to XX at access fails
CHECK (! pX3.get());
}
/** @test equality and ordering operators forwarded to pointee */
void
check_ordering ()
{
typedef P<X> PX;
typedef P<XX,PX> PXX;
PX pX1 (new X(3));
PX pX2 (new XX(5));
PX pX3, pX4, pX5, pX6;
PXX pXX (new XX(7));
pX3 = pXX;
pX4.reset(new X(*pXX));
CHECK ( (pX1 == pX1)); // reflexivity
CHECK (!(pX1 != pX1));
CHECK (!(pX1 < pX1));
CHECK (!(pX1 > pX1));
CHECK ( (pX1 <= pX1));
CHECK ( (pX1 >= pX1));
CHECK (!(pX1 == pX2)); // compare to same ptr type with larger pointee of subtype
CHECK ( (pX1 != pX2));
CHECK ( (pX1 < pX2));
CHECK (!(pX1 > pX2));
CHECK ( (pX1 <= pX2));
CHECK (!(pX1 >= pX2));
CHECK (!(pX2 == pXX)); // compare to ptr subtype with larger pointee of same subtype
CHECK ( (pX2 != pXX));
CHECK ( (pX2 < pXX));
CHECK (!(pX2 > pXX));
CHECK ( (pX2 <= pXX));
CHECK (!(pX2 >= pXX));
CHECK (!(pX1 == pXX)); // transitively compare to ptr subtype with larger pointee of subtype
CHECK ( (pX1 != pXX));
CHECK ( (pX1 < pXX));
CHECK (!(pX1 > pXX));
CHECK ( (pX1 <= pXX));
CHECK (!(pX1 >= pXX));
CHECK ( (pX3 == pXX)); // compare ptr to subtype ptr both referring to same pointee
CHECK (!(pX3 != pXX));
CHECK (!(pX3 < pXX));
CHECK (!(pX3 > pXX));
CHECK ( (pX3 <= pXX));
CHECK ( (pX3 >= pXX));
CHECK ( (pX4 == pXX)); // compare ptr to subtype ptr both referring to different but equal pointees
CHECK (!(pX4 != pXX));
CHECK (!(pX4 < pXX));
CHECK (!(pX4 > pXX));
CHECK ( (pX4 <= pXX));
CHECK ( (pX4 >= pXX));
CHECK (!(pXX == pX5)); // compare subtype ptr to empty ptr: "unequal but not orderable"
CHECK ( (pXX != pX5));
CHECK ( (pX5 == pX6)); // compare two empty ptrs: "equal, equivalent but not orderable"
CHECK (!(pX5 != pX6));
// order relations on NIL pointers disallowed
#if false ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT
VERIFY_ERROR (ASSERTION, pXX < pX5 );
VERIFY_ERROR (ASSERTION, pXX > pX5 );
VERIFY_ERROR (ASSERTION, pXX <= pX5 );
VERIFY_ERROR (ASSERTION, pXX >= pX5 );
VERIFY_ERROR (ASSERTION, pX5 < pXX );
VERIFY_ERROR (ASSERTION, pX5 > pXX );
VERIFY_ERROR (ASSERTION, pX5 <= pXX );
VERIFY_ERROR (ASSERTION, pX5 >= pXX );
VERIFY_ERROR (ASSERTION, pX5 < pX6 );
VERIFY_ERROR (ASSERTION, pX5 > pX6 );
VERIFY_ERROR (ASSERTION, pX5 <= pX6 );
@ -315,11 +315,11 @@ namespace test{
#endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT
}
};
/** Register this test class... */
LAUNCHER (CustomSharedPtr_test, "unit common");
}} // namespace lib::test

View file

@ -39,12 +39,12 @@
namespace lib {
namespace wrapper {
namespace test{
using ::Test;
using lib::test::randStr;
using lib::test::showSizeof;
using util::isSameObject;
using std::tr1::placeholders::_1;
using std::tr1::ref;
using std::vector;
@ -52,59 +52,57 @@ namespace test{
using std::rand;
using std::cout;
using std::endl;
namespace { // Test helper: yet another ctor/dtor counting class
long cntTracker = 0;
struct Tracker
{
uint i_;
Tracker() : i_(rand() % 500) { ++cntTracker; }
Tracker(Tracker const& ot) : i_(ot.i_) { ++cntTracker; }
~Tracker() { --cntTracker; }
};
bool operator== (Tracker const& t1, Tracker const& t2) { return t1.i_ == t2.i_; }
bool operator!= (Tracker const& t1, Tracker const& t2) { return t1.i_ != t2.i_; }
/// to be bound as test function....
int&
pickElement (vector<int>& vec, size_t idx)
{
return vec[idx];
}
function<int&(size_t)>
pickElement_ofVector (vector<int>& vec)
{
return std::tr1::bind (pickElement, ref(vec), _1 );
}
} // (END) Test helpers
/*******************************************************************************
* @test use the ItemWrapper to define inline-storage holding values,
* pointers and references. Verify correct behaviour in each case,
* including (self)assignment, empty check, invalid dereferentiation.
*
*
* @see wrapper.hpp
*/
class ItemWrapper_test : public Test
{
virtual void
run (Arg)
{
@ -113,83 +111,83 @@ namespace test{
string s1 (randStr(50));
string s2 (randStr(50));
const char* cp (s1.c_str());
verifyWrapper<ulong> (l1, l2);
verifyWrapper<ulong&> (l1, l2);
verifyWrapper<ulong*> (&l1, &l2);
verifyWrapper<ulong*> ((0), &l2);
verifyWrapper<ulong*> (&l1, (0));
verifyWrapper<ulong const&> (l1, l2);
verifyWrapper<string> (s1, s2);
verifyWrapper<string&> (s1, s2);
verifyWrapper<string*> (&s1, &s2);
verifyWrapper<const char*> (cp, "Lumiera");
verifySaneInstanceHandling();
verifyWrappedRef ();
verifyFunctionResult ();
verifyFunctionRefResult ();
}
template<typename X>
void
verifyWrapper (X val, X otherVal)
{
const ItemWrapper<X> wrap(val);
CHECK (wrap);
cout << "ItemWrapper: " << showSizeof(wrap) << endl;
ItemWrapper<X> copy1 (wrap);
ItemWrapper<X> copy2;
ItemWrapper<X> empty;
CHECK (copy1);
CHECK (!copy2);
CHECK (false == bool(empty));
CHECK (wrap == copy1);
CHECK (wrap != copy2);
CHECK (wrap != empty);
copy2 = copy1;
CHECK (copy2);
CHECK (wrap == copy2);
CHECK (wrap != empty);
copy2 = otherVal;
CHECK (copy2);
CHECK (wrap != copy2);
CHECK (wrap != empty);
CHECK (val == *wrap);
CHECK (val == *copy1);
CHECK (val != *copy2);
VERIFY_ERROR (BOTTOM_VALUE, *empty );
CHECK (otherVal == *copy2);
copy1 = copy2;
CHECK (otherVal == *copy1);
CHECK (otherVal == *copy2);
CHECK (wrap != copy1);
CHECK (wrap != copy2);
copy1 = empty; // assign empty to discard value
copy1 = copy1; // self-assign empty value
CHECK (!copy1);
copy1 = copy2;
CHECK (otherVal == *copy1);
copy1 = copy1; // self-assign (will be suppressed)
CHECK (otherVal == *copy1);
copy1 = *copy1; // self-assign also detected in this case
CHECK (otherVal == *copy2);
CHECK (copy1);
copy1.reset();
CHECK (!copy1);
@ -197,8 +195,8 @@ namespace test{
CHECK (copy2 != copy1);
VERIFY_ERROR (BOTTOM_VALUE, *copy1 );
};
/** @test verify that ctor and dtor calls are balanced,
* even when assigning and self-assigning.
*/
@ -209,16 +207,16 @@ namespace test{
{
Tracker t1;
Tracker t2;
verifyWrapper<Tracker> (t1, t2);
verifyWrapper<Tracker&> (t1, t2);
verifyWrapper<Tracker*> (&t1, &t2);
}
CHECK (0 == cntTracker);
}
/** @test verify especially that we can wrap and handle
* a reference "value" in a pointer-like manner
*/
@ -228,23 +226,23 @@ namespace test{
int x = 5;
ItemWrapper<int&> refWrap;
CHECK (!refWrap);
refWrap = x;
CHECK (refWrap);
CHECK (5 == *refWrap);
CHECK (x == *refWrap);
*refWrap += 5;
CHECK (x == 10);
ItemWrapper<int*> ptrWrap (& *refWrap);
CHECK ( isSameObject (**ptrWrap, x));
CHECK (!isSameObject ( *ptrWrap, &x));
**ptrWrap += 13;
CHECK (x == 23);
}
/** @test verify an extension built on top of the ItemWrapper:
* a function which remembers the last result. As a simple test,
* we bind the \c rand() standard lib function and remember the
@ -254,17 +252,17 @@ namespace test{
verifyFunctionResult()
{
FunctionResult<int(void)> randomVal (std::rand);
// function was never invoked, thus the remembered result is NIL
CHECK (!randomVal);
VERIFY_ERROR (BOTTOM_VALUE, *randomVal );
int v1 = randomVal();
CHECK (v1 == *randomVal);
CHECK (v1 == *randomVal);
CHECK (v1 == *randomVal);
CHECK (randomVal);
int v2;
do v2 = randomVal();
while (v1 == v2);
@ -272,8 +270,8 @@ namespace test{
CHECK (v2 == *randomVal);
CHECK (v1 != *randomVal);
}
/** @test verify an extension built on top of the ItemWrapper:
* a function which remembers the last result. Here we use
* a test function, which picks a member of an vector and
@ -288,27 +286,27 @@ namespace test{
vector<int> testVec;
for (uint i=0; i<10; ++i)
testVec.push_back(i);
FunctionResult<int&(size_t)> funRes (pickElement_ofVector(testVec));
// function was never invoked, thus the remembered result is NIL
CHECK (!funRes);
VERIFY_ERROR (BOTTOM_VALUE, *funRes );
int& r5 = funRes (5);
CHECK (funRes); // indicates existence of cached result
CHECK (funRes); // indicates existence of cached result
CHECK (5 == r5);
CHECK (isSameObject (r5, testVec[5]));
int& r5x = *funRes;
CHECK (isSameObject (r5, r5x));
CHECK ( isSameObject (r5, *funRes));
int& r7 = funRes (7);
CHECK (!isSameObject (r5, *funRes));
CHECK ( isSameObject (r7, *funRes));
-- r5x;
++ *funRes;
CHECK (5-1 == testVec[5]);
@ -316,9 +314,9 @@ namespace test{
CHECK (7+1 == r7);
}
};
LAUNCHER (ItemWrapper_test, "unit common");
}}} // namespace lib::wrapper::test

View file

@ -37,7 +37,7 @@
namespace lib {
namespace test{
using ::Test;
using boost::lexical_cast;
using util::for_each;
@ -46,74 +46,74 @@ namespace test{
using std::cout;
using std::endl;
using std::rand;
namespace { // Test data
uint NUM_ELMS = 10;
struct TestSource
{
vector<int> data_;
TestSource(uint num)
{
while (num)
data_.push_back(num--);
}
typedef vector<int>::iterator sourceIter;
typedef RangeIter<sourceIter> iterator;
iterator begin() { return iterator(data_.begin(),data_.end()); }
iterator end() { return iterator(); }
};
} // (END) Test data
/*******************************************************************************
* @test build combined and filtering iterators with the help of lib::IterTool.
* Check correct behaviour of the resulting iterators and
* verify they fulfil the Lumiera Forward Iterator concept
*
*
* @todo implement more iterator tools.... see Ticket #347
*/
class IterTools_test : public Test
{
typedef TestSource::iterator Iter;
virtual void
run (Arg arg)
{
if (0 < arg.size()) NUM_ELMS = lexical_cast<uint> (arg[0]);
TestSource source(NUM_ELMS);
pullOut (source.begin());
verifyComparisons (source.begin());
buildFilterIterator (source.begin());
Iter ii (source.begin());
++++++ii;
buildFilterIterator (ii);
verify_filterRepetitions();
buildTransformingIterator (source.begin());
}
template<class IT>
void
pullOut (IT const& ii)
@ -122,34 +122,34 @@ namespace test{
cout << "::" << *iter;
cout << endl;
}
static bool takeAll (int) { return true; }
static bool takeOdd (int i) { return 0 != i % 2; }
static bool takeEve (int i) { return 0 == i % 2; }
void
buildFilterIterator (Iter const& ii)
{
pullOut (filterIterator (ii, takeAll)); // note: using the convenient builder function
pullOut (filterIterator (ii, takeEve));
pullOut (filterIterator (ii, takeOdd));
FilterIter<Iter> all (ii, takeAll);
FilterIter<Iter> odd (ii, takeOdd);
verifyComparisons (all);
verifyComparisons (odd);
while (++all && ++odd)
CHECK (all != odd);
while (++all) { }
CHECK (isnil (odd));
CHECK (all == odd);
}
/** @test verify the helper to filter duplicate elements
* emitted by an source iterator. This test creates
* a sequence of numbers with random repetitions.
@ -165,56 +165,56 @@ namespace test{
while (--n);
}
CHECK (NUM_ELMS < numberz.size(), "no repetition in test data??");
typedef vector<uint>::iterator SrcIter;
typedef RangeIter<SrcIter> SeqIter;
typedef FilterIter<SeqIter> FilteredSeq;
SeqIter completeSequence (numberz.begin(), numberz.end());
FilteredSeq filtered = filterRepetitions (completeSequence);
uint num=0;
for (; num<NUM_ELMS && !isnil(filtered);
++num,
++filtered
)
CHECK (num == *filtered);
CHECK (num == NUM_ELMS && isnil(filtered));
}
static ulong addTwo (int i) { return i+2; }
static int negate (int i) { return -i; }
static int idFunc (int i) { return i; }
void
buildTransformingIterator (Iter const& ii)
{
pullOut (transformIterator(ii, idFunc));
pullOut (transformIterator(ii, negate));
pullOut (transformIterator(ii, addTwo)); // note: changing output type to unsigned
TransformIter<Iter, int> idi (ii, idFunc);
TransformIter<Iter, int> neg (ii, negate);
verifyComparisons (idi);
verifyComparisons (neg);
CHECK (idi);
CHECK (neg);
for ( ;idi&&neg;
++idi,++neg)
CHECK (idi != neg);
CHECK (!idi && !neg);
CHECK (idi == neg);
}
/** @test verify equality handling and NIL detection
* for the given iterator/wrapper handed in */
template<class IT>
@ -227,36 +227,36 @@ namespace test{
CHECK ( isnil (iN));
CHECK (!isnil (i1));
CHECK (!isnil (i2));
CHECK (i1 == i2); CHECK (i2 == i1);
CHECK (i1 != iN); CHECK (iN != i1);
CHECK (i2 != iN); CHECK (iN != i2);
++i1;
CHECK (i1 != i2);
CHECK (i1 != iN);
++i2;
CHECK (i1 == i2);
CHECK (i1 != iN);
CHECK (i2 != iN);
while (++i1) { }
CHECK (isnil(i1));
CHECK (i1 != i2);
CHECK (i1 == iN);
while (++i2) { }
CHECK (isnil(i2));
CHECK (i2 == i1);
CHECK (i2 == iN);
}
};
LAUNCHER (IterTools_test, "unit common");
}} // namespace lib::test

View file

@ -32,98 +32,98 @@
namespace lumiera {
namespace typelist{
namespace test {
using ::test::Test;
using func::applyFirst;
using func::applyLast;
using func::bindLast;
using func::PApply;
using func::BindToArgument;
namespace { // test functions
Num<1> _1_;
Num<2> _2_;
Num<3> _3_;
Num<4> _4_;
Num<5> _5_;
Num<6> _6_;
Num<7> _7_;
Num<8> _8_;
Num<9> _9_;
/** "Function-1" will be used at the front side, accepting a tuple of values */
template<uint i>
Num<i>
fun11 ( Num<i> val1
)
{
return val1;
}
template<uint i, uint ii>
Num<i>
fun12 ( Num<i> val1
, Num<ii> val2
)
{
val1.o_ += val2.o_;
return val1;
}
template<uint i, uint ii, uint iii>
Num<i>
fun13 ( Num<i> val1
, Num<ii> val2
, Num<iii> val3
)
{
val1.o_ += val2.o_ + val3.o_;
return val1;
}
template<uint i, uint ii, uint iii, uint iv>
Num<i>
fun14 ( Num<i> val1
, Num<ii> val2
, Num<iii> val3
, Num<iv> val4
)
{
val1.o_ += val2.o_ + val3.o_ + val4.o_;
return val1;
}
template<uint i, uint ii, uint iii, uint iv, uint v>
Num<i>
fun15 ( Num<i> val1
, Num<ii> val2
, Num<iii> val3
, Num<iv> val4
, Num<v> val5
)
{
val1.o_ += val2.o_ + val3.o_ + val4.o_ + val5.o_;
return val1;
}
/** "Function-2" can be chained behind fun1 */
template<class II>
int
fun2 (II val)
{
return val.o_;
}
} // (End) test data
namespace { // test functions
Num<1> _1_;
Num<2> _2_;
Num<3> _3_;
Num<4> _4_;
Num<5> _5_;
Num<6> _6_;
Num<7> _7_;
Num<8> _8_;
Num<9> _9_;
/** "Function-1" will be used at the front side, accepting a tuple of values */
template<uint i>
Num<i>
fun11 ( Num<i> val1
)
{
return val1;
}
template<uint i, uint ii>
Num<i>
fun12 ( Num<i> val1
, Num<ii> val2
)
{
val1.o_ += val2.o_;
return val1;
}
template<uint i, uint ii, uint iii>
Num<i>
fun13 ( Num<i> val1
, Num<ii> val2
, Num<iii> val3
)
{
val1.o_ += val2.o_ + val3.o_;
return val1;
}
template<uint i, uint ii, uint iii, uint iv>
Num<i>
fun14 ( Num<i> val1
, Num<ii> val2
, Num<iii> val3
, Num<iv> val4
)
{
val1.o_ += val2.o_ + val3.o_ + val4.o_;
return val1;
}
template<uint i, uint ii, uint iii, uint iv, uint v>
Num<i>
fun15 ( Num<i> val1
, Num<ii> val2
, Num<iii> val3
, Num<iv> val4
, Num<v> val5
)
{
val1.o_ += val2.o_ + val3.o_ + val4.o_ + val5.o_;
return val1;
}
/** "Function-2" can be chained behind fun1 */
template<class II>
int
fun2 (II val)
{
return val.o_;
}
} // (End) test data
/******************************************************************************
* @test this test covers some extensions and variations on function closures:
* - partial application of a function, returning a partial closure
@ -140,8 +140,8 @@ namespace test {
check_functionalComposition ();
check_bindToArbitraryParameter ();
}
/** verify the test input data
* @see TypeListManipl_test#check_diagnostics()
* for an explanation of the DISPLAY macro
@ -151,59 +151,59 @@ namespace test {
{
CHECK (6 == (fun13<1,2,3> (_1_, _2_, _3_)).o_ );
CHECK (6 == (fun13<1,1,1> (Num<1>(3), Num<1>(2), Num<1>(1))).o_ );
CHECK ( 1 == fun2 (fun11<1> (_1_)) );
CHECK ( 3 == fun2 (fun12<1,2> (_1_, _2_)) );
CHECK ( 6 == fun2 (fun13<1,2,3> (_1_, _2_, _3_)) );
CHECK (10 == fun2 (fun14<1,2,3,4> (_1_, _2_, _3_, _4_)) );
CHECK (15 == fun2 (fun15<1,2,3,4,5> (_1_, _2_, _3_, _4_, _5_)) );
CHECK ( 9 == fun2 (fun13<2,3,4> (_2_, _3_, _4_)) );
CHECK (18 == fun2 (fun13<5,6,7> (_5_, _6_, _7_)) );
CHECK (24 == fun2 (fun13<9,8,7> (_9_, _8_, _7_)) );
}
void
check_partialApplication ()
{
// Because the code of the partial function application is very technical,
// the following might serve as explanation what actually happens....
// (and actually it's a leftover from initial debugging)
typedef Num<1> Sig123(Num<1>, Num<2>, Num<3>); // signature of the original function
typedef Num<1> Sig23(Num<2>, Num<3>); // signature after having closed over the first argument
typedef function<Sig23> F23; // and a tr1::function object to hold such a function
Sig123& f =fun13<1,2,3>; // the actual input: a reference to the bare function
// Version1: do a direct argument binding----------------- //
typedef std::tr1::_Placeholder<1> PH1; // tr1::function argument placeholders
typedef std::tr1::_Placeholder<2> PH2;
PH1 ph1; // these empty structs are used to mark the arguments to be kept "open"
PH2 ph2;
Num<1> num18 (18); // ...and this value is for closing the first function argument
F23 fun_23 = std::tr1::bind (f, num18 // do the actual binding (i.e. close the first argument with a constant value)
, ph1
, ph2
);
int res = 0;
res = fun_23 (_2_,_3_).o_; // and invoke the resulting functor ("closure"), providing the remaining arguments
CHECK (23 == res);
// Version2: extract the binding arguments from a tuple--- //
typedef Tuple<Types<Num<1>, PH1, PH2> > PartialArg; // Tuple type to hold the binding values. Note the placeholder types
PartialArg arg(num18); // Value for partial application (the placeholders are default constructed)
fun_23 = std::tr1::bind (f, tuple::element<0>(arg) // now extract the values to bind from this tuple
, tuple::element<1>(arg)
, tuple::element<2>(arg)
@ -211,90 +211,90 @@ namespace test {
res = 0;
res = fun_23 (_2_,_3_).o_; // and invoke the resulting functor....
CHECK (23 == res);
// Version3: let the PApply-template do the work for us--- //
typedef Types<Num<1> > ArgTypes; // now package just the argument(s) to be applied into a tuple
Tuple<ArgTypes> args_to_bind (Num<1>(18));
fun_23 = PApply<Sig123, ArgTypes>::bindFront (f , args_to_bind);
// "bindFront" will close the parameters starting from left....
res = 0;
res = fun_23 (_2_,_3_).o_; // invoke the resulting functor...
CHECK (23 == res);
// Version4: as you'd typically do it in real life-------- //
fun_23 = func::applyFirst (f, Num<1>(18)); // use the convenience function API to close over a single value
res = 0;
res = fun_23 (_2_,_3_).o_; // invoke the resulting functor...
CHECK (23 == res);
// what follows is the real unit test...
function<Sig123> func123 (f); // alternatively do it with an tr1::function object
fun_23 = func::applyFirst (func123, Num<1>(19));
res = fun_23 (_2_,_3_).o_;
CHECK (24 == res);
typedef function<Num<1>(Num<1>, Num<2>)> F12;
F12 fun_12 = func::applyLast(f, Num<3>(20)); // close the *last* argument of a function
res = fun_12 (_1_,_2_).o_;
CHECK (23 == res);
fun_12 = func::applyLast(func123, Num<3>(21)); // alternatively use a function object
res = fun_12 (_1_,_2_).o_;
CHECK (24 == res);
Sig123 *fP = &f; // a function pointer works too
fun_12 = func::applyLast( fP, Num<3>(22));
res = fun_12 (_1_,_2_).o_;
CHECK (25 == res);
// cover more cases....
CHECK (1 == (func::applyLast (fun11<1> , _1_) ( ) ).o_);
CHECK (1+3 == (func::applyLast (fun12<1,3> , _3_) (_1_) ).o_);
CHECK (1+3+5 == (func::applyLast (fun13<1,3,5> , _5_) (_1_,_3_) ).o_);
CHECK (1+3+5+7 == (func::applyLast (fun14<1,3,5,7> , _7_) (_1_,_3_,_5_) ).o_);
CHECK (1+3+5+7+9 == (func::applyLast (fun15<1,3,5,7,9>, _9_) (_1_,_3_,_5_,_7_)).o_);
CHECK (9+8+7+6+5 == (func::applyFirst(fun15<9,8,7,6,5>, _9_) (_8_,_7_,_6_,_5_)).o_);
CHECK ( 8+7+6+5 == (func::applyFirst( fun14<8,7,6,5>, _8_) (_7_,_6_,_5_)).o_);
CHECK ( 7+6+5 == (func::applyFirst( fun13<7,6,5>, _7_) (_6_,_5_)).o_);
CHECK ( 6+5 == (func::applyFirst( fun12<6,5>, _6_) (_5_)).o_);
CHECK ( 5 == (func::applyFirst( fun11<5>, _5_) ( )).o_);
CHECK (1 == (func::applyLast (fun11<1> , _1_ ) ( ) ).o_);
CHECK (1+3 == (func::applyLast (fun12<1,3> , _3_ ) (_1_) ).o_);
CHECK (1+3+5 == (func::applyLast (fun13<1,3,5> , _5_ ) (_1_,_3_) ).o_);
CHECK (1+3+5+7 == (func::applyLast (fun14<1,3,5,7> , _7_ ) (_1_,_3_,_5_) ).o_);
CHECK (1+3+5+7+9 == (func::applyLast (fun15<1,3,5,7,9>, _9_ ) (_1_,_3_,_5_,_7_)).o_);
CHECK (9+8+7+6+5 == (func::applyFirst(fun15<9,8,7,6,5>, _9_ ) (_8_,_7_,_6_,_5_)).o_);
CHECK ( 8+7+6+5 == (func::applyFirst( fun14<8,7,6,5>, _8_ ) (_7_,_6_,_5_)).o_);
CHECK ( 7+6+5 == (func::applyFirst( fun13<7,6,5>, _7_ ) (_6_,_5_)).o_);
CHECK ( 6+5 == (func::applyFirst( fun12<6,5>, _6_ ) (_5_)).o_);
CHECK ( 5 == (func::applyFirst( fun11<5>, _5_ ) ( )).o_);
// Finally a more convoluted example
// covering the general case of partial function closure:
typedef Num<5> Sig54321(Num<5>, Num<4>, Num<3>, Num<2>, Num<1>); // Signature of the 5-argument function
typedef Num<5> Sig54 (Num<5>, Num<4>); // ...closing the last 3 arguments should yield this 2-argument function
typedef Types<Num<3>,Num<2>,Num<1> > Args2Close; // Tuple type to hold the 3 argument values used for the closure
// Close the trailing 3 arguments of the 5-argument function...
function<Sig54> fun_54 = PApply<Sig54321, Args2Close>::bindBack(fun15<5,4,3,2,1>,
tuple::make(_3_,_2_,_1_)
);
// apply the remaining argument values
Num<5> resN5 = fun_54 (_5_,_4_);
CHECK (5+4+3+2+1 == resN5.o_);
}
void
check_functionalComposition ()
{
@ -304,87 +304,87 @@ namespace test {
typedef Num<1> Sig13(Num<1>,Num<2>,Num<3>);
typedef Num<1> Sig14(Num<1>,Num<2>,Num<3>,Num<4>);
typedef Num<1> Sig15(Num<1>,Num<2>,Num<3>,Num<4>,Num<5>);
Sig2 & ff = fun2< Num<1> >;
Sig11& f1 = fun11<1>;
Sig12& f2 = fun12<1,2>;
Sig13& f3 = fun13<1,2,3>;
Sig14& f4 = fun14<1,2,3,4>;
Sig15& f5 = fun15<1,2,3,4,5>;
CHECK (1 == func::chained(f1, ff) (_1_) );
CHECK (1+2 == func::chained(f2, ff) (_1_,_2_) );
CHECK (1+2+3 == func::chained(f3, ff) (_1_,_2_,_3_) );
CHECK (1+2+3+4 == func::chained(f4, ff) (_1_,_2_,_3_,_4_) );
CHECK (1+2+3+4+5 == func::chained(f5, ff) (_1_,_2_,_3_,_4_,_5_) );
function<Sig15> f5_fun = f5; // also works with function objects...
function<Sig2> ff_fun = ff;
CHECK (1+2+3+4+5 == func::chained(f5_fun, ff ) (_1_,_2_,_3_,_4_,_5_) );
CHECK (1+2+3+4+5 == func::chained(f5, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
CHECK (1+2+3+4+5 == func::chained(f5_fun, ff_fun) (_1_,_2_,_3_,_4_,_5_) );
}
void
check_bindToArbitraryParameter ()
{
typedef Num<1> Sig15(Num<1>,Num<2>,Num<3>,Num<4>,Num<5>);
typedef Num<1> SigR1( Num<2>,Num<3>,Num<4>,Num<5>);
typedef Num<1> SigR2(Num<1>, Num<3>,Num<4>,Num<5>);
typedef Num<1> SigR3(Num<1>,Num<2>, Num<4>,Num<5>);
typedef Num<1> SigR4(Num<1>,Num<2>,Num<3>, Num<5>);
typedef Num<1> SigR5(Num<1>,Num<2>,Num<3>,Num<4> );
typedef Num<5> SigA5(Num<5>);
Sig15& f = fun15<1,2,3,4,5>;
SigA5& f5 = fun11<5>;
Tuple<Types<char> > argT(55);
function<SigR1> f_bound_1 = BindToArgument<Sig15,char,0>::reduced (f, argT);
function<SigR2> f_bound_2 = BindToArgument<Sig15,char,1>::reduced (f, argT);
function<SigR3> f_bound_3 = BindToArgument<Sig15,char,2>::reduced (f, argT);
function<SigR4> f_bound_4 = BindToArgument<Sig15,char,3>::reduced (f, argT);
function<SigR5> f_bound_5 = BindToArgument<Sig15,char,4>::reduced (f, argT);
CHECK (55+2+3+4+5 == f_bound_1 ( _2_,_3_,_4_,_5_) );
CHECK (1+55+3+4+5 == f_bound_2 (_1_, _3_,_4_,_5_) );
CHECK (1+2+55+4+5 == f_bound_3 (_1_,_2_, _4_,_5_) );
CHECK (1+2+3+55+5 == f_bound_4 (_1_,_2_,_3_, _5_) );
CHECK (1+2+3+4+55 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
// degenerate case: specify wrong argument position (behind end of argument list)
// causes the argument to be simply ignored and no binding to happen
function<Sig15> f_bound_X = BindToArgument<Sig15,char,5>::reduced (f, argT);
CHECK (1+2+3+4+5 == f_bound_X (_1_,_2_,_3_,_4_,_5_) );
/* check the convenient function-style API */
using std::tr1::bind;
f_bound_5 = bindLast (f, bind(f5, Num<5>(99)));
CHECK (1+2+3+4+99 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
f_bound_5 = bindLast (f, bind(&f5, Num<5>(99))); // can bind function pointer
CHECK (1+2+3+4+99 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
function<Sig15> asFunctor(f);
f_bound_5 = bindLast (asFunctor, bind(f5, Num<5>(88))); // use functor instead of direct ref
CHECK (1+2+3+4+88 == f_bound_5 (_1_,_2_,_3_,_4_ ) );
}
};
/** Register this test class... */
LAUNCHER (FunctionComposition_test, "unit common");
}}} // namespace lumiera::typelist::test

View file

@ -23,13 +23,13 @@
/** @file generator-test.cpp
** \par what are we doing here??
**
**
** the following test composes both an interface and the corresponding implementation
** by instantiating "building block" templates over a collection of types. The resulting
** class ends up inheriting a \e virtual function instantiated for each of the types
** in the list. (remember: normally the number and signature of all virtual functions
** need to be absolutely fixed in the class definition)
**
**
** @see typelist-test.cpp
** @see generator.hpp
** @see lumiera::query::ConfigRules a real world usage example
@ -50,99 +50,98 @@ using std::cout;
namespace lumiera {
namespace typelist{
namespace test {
/** template for generating lots of different test types */
template<int I>
struct Block
/** template for generating lots of different test types */
template<int I>
struct Block
{
static string name;
string talk() { return "__"+name+"__"; }
};
boost::format fmt ("Block<%2i>");
template<int I>
string Block<I>::name = str (fmt % I);
/** Use this building block for assembling an abstract interface */
template<class X>
class TakeIt
{
public:
virtual void eat (X& x) = 0;
virtual ~TakeIt() { }
};
/** Use this building block for chaining corresponding implementation classes. */
template<class X, class BASE>
class DoIt
: public BASE
{
protected:
DoIt () { cout << "ctor DoIt<"<< X::name << " >\n";}
virtual ~DoIt() { cout << "dtor DoIt<"<< X::name << " >\n";}
public:
void eat (X& x) { cout << "devouring" << x.talk() << "\n";}
using BASE::eat; // prevent shadowing
};
typedef Types< Block<1>
, Block<2>
, Block<3>
, Block<5>
, Block<8>
, Block<13>
>::List TheTypes;
typedef InstantiateForEach<TheTypes,TakeIt> TheInterface;
struct BaseImpl : public TheInterface
{
void eat() { cout << "gulp!\n"; }
};
typedef InstantiateChained<TheTypes,DoIt, BaseImpl> NumberBabbler;
/*************************************************************************
* @test check the helpers for dealing with lists-of-types.
* Build an interface and an implementation class
* by inheriting template instantiations
* for a collection of classes</li>
*/
class TypeListGenerator_test : public Test
{
virtual void run(Arg)
{
static string name;
string talk() { return "__"+name+"__"; }
};
boost::format fmt ("Block<%2i>");
template<int I>
string Block<I>::name = str (fmt % I);
/** Use this building block for assembling an abstract interface */
template<class X>
class TakeIt
{
public:
virtual void eat (X& x) = 0;
virtual ~TakeIt() { }
};
/** Use this building block for chaining corresponding implementation classes. */
template<class X, class BASE>
class DoIt
: public BASE
{
protected:
DoIt () { cout << "ctor DoIt<"<< X::name << " >\n";}
virtual ~DoIt() { cout << "dtor DoIt<"<< X::name << " >\n";}
public:
void eat (X& x) { cout << "devouring" << x.talk() << "\n";}
using BASE::eat; // prevent shadowing
};
typedef Types< Block<1>
, Block<2>
, Block<3>
, Block<5>
, Block<8>
, Block<13>
>::List TheTypes;
typedef InstantiateForEach<TheTypes,TakeIt> TheInterface;
struct BaseImpl : public TheInterface
{
void eat() { cout << "gulp!\n"; }
};
typedef InstantiateChained<TheTypes,DoIt, BaseImpl> NumberBabbler;
/*************************************************************************
* @test check the helpers for dealing with lists-of-types.
* <ul><li>build an interface and an implementation class
* by inheriting template instantiations
* for a collection of classes</li>
* </ul>
*/
class TypeListGenerator_test : public Test
{
virtual void run(Arg)
{
NumberBabbler me_can_has_more_numberz;
CHECK (INSTANCEOF (TheInterface, &me_can_has_more_numberz));
TheTypes::Tail::Head b2; // Block<2>
TheTypes::Tail::Tail::Tail::Head b5; // Block<5>
TheTypes::Tail::Tail::Tail::Tail::Tail::Head b13; // Block<13>
me_can_has_more_numberz.eat (b2);
me_can_has_more_numberz.eat (b5);
TakeIt<Block<13> >& subInterface = me_can_has_more_numberz;
subInterface.eat (b13);
me_can_has_more_numberz.eat();
INFO (test, "SizeOf = %u", sizeof(me_can_has_more_numberz));
}
};
/** Register this test class... */
LAUNCHER (TypeListGenerator_test, "unit common");
NumberBabbler me_can_has_more_numberz;
CHECK (INSTANCEOF (TheInterface, &me_can_has_more_numberz));
TheTypes::Tail::Head b2; // Block<2>
TheTypes::Tail::Tail::Tail::Head b5; // Block<5>
TheTypes::Tail::Tail::Tail::Tail::Tail::Head b13; // Block<13>
me_can_has_more_numberz.eat (b2);
me_can_has_more_numberz.eat (b5);
TakeIt<Block<13> >& subInterface = me_can_has_more_numberz;
subInterface.eat (b13);
me_can_has_more_numberz.eat();
INFO (test, "SizeOf = %u", sizeof(me_can_has_more_numberz));
}
};
/** Register this test class... */
LAUNCHER (TypeListGenerator_test, "unit common");
}}} // namespace lumiera::typelist::test

View file

@ -24,7 +24,7 @@
/** @file type-tuple-test.cpp
** Interplay of typelists, type tuples and simple record
** data types build on top of them.
**
**
** @see lumiera::typelist::Tuple
** @see tuple.hpp
** @see function-closure.hpp
@ -48,28 +48,28 @@ using std::endl;
namespace lumiera {
namespace typelist{
namespace test {
namespace { // test data
typedef Types< Num<1>
, Num<3>
, Num<5>
> Types1;
typedef Types< Num<2>
, Num<4>
> Types2;
typedef Types< Num<7> > Types3;
} // (End) test data
namespace { // test data
typedef Types< Num<1>
, Num<3>
, Num<5>
> Types1;
typedef Types< Num<2>
, Num<4>
> Types2;
typedef Types< Num<7> > Types3;
} // (End) test data
/*************************************************************************
* @test Cover various aspects of the type tuple.
* Check the metaprogramming behaviour...
@ -99,8 +99,8 @@ namespace test {
check_tuple_copy();
check_value_access();
}
/** verify the test input data
* @see TypeListManipl_test#check_diagnostics()
* for an explanation of the DISPLAY macro
@ -111,156 +111,156 @@ namespace test {
typedef Types1::List L1;
typedef Types2::List L2;
typedef Types3::List L3;
DISPLAY (L1);
DISPLAY (L2);
DISPLAY (L3);
typedef Tuple<Types1> Tup1;
Tup1 tup1x (Num<1>(11));
DISPLAY (Tup1); // prints the type
DUMPVAL (Tup1()); // prints the contents
DUMPVAL (tup1x);
}
void
check_tuple_from_Typelist()
{
typedef Types1::List L1; // starting from an existing Typelist...
typedef Tuple<L1> T_L1; // ListType based tuple type
typedef Tuple<L1>::TupleType T1; // corresponding plain tuple type
typedef Tuple<L1>::Type Type1; // extract the underlying type sequence
DISPLAY (Type1);
DISPLAY (T_L1);
DISPLAY (T1);
T_L1 tup1 (Num<1>(8)); // both flavours can be created at runtime
T1 tup2 (Num<1>(9)); // (and we provide an explicit value for the 1st element)
DUMPVAL (tup1);
DUMPVAL (tup2);
typedef Tuple<Node<int, L1> > Prepend;
DISPLAY (Prepend); // another ListType based tuple created by prepending
Prepend prepend (22, tup2); // but note: the ListType based tuple has an "(head,tail)" style ctor
DUMPVAL (prepend); // ... and in construction, tup2 has been copied and coerced to ListType style
typedef Tuple<Types<> > NulT; // plain-flat empty Tuple
typedef Tuple<NullType> NulL; // list-style empty Tuple
CHECK ( is_Tuple<T1>::value);
CHECK ( is_TuplePlain<T1>::value);
CHECK (! is_TupleListType<T1>::value);
CHECK (! is_NullTuple<T1>::value);
CHECK ( is_Tuple<T_L1>::value);
CHECK (! is_TuplePlain<T_L1>::value);
CHECK ( is_TupleListType<T_L1>::value);
CHECK (! is_NullTuple<T_L1>::value);
CHECK ( is_Tuple<NulT>::value);
CHECK ( is_TuplePlain<NulT>::value);
CHECK (! is_TupleListType<NulT>::value);
CHECK ( is_NullTuple<NulT>::value);
CHECK ( is_Tuple<NulL>::value);
CHECK (! is_TuplePlain<NulL>::value);
CHECK ( is_TupleListType<NulL>::value);
CHECK ( is_NullTuple<NulL>::value);
CHECK (! is_Tuple<Type1>::value);
CHECK (! is_TuplePlain<Type1>::value);
CHECK (!is_TupleListType<Type1>::value);
CHECK (! is_NullTuple<Type1>::value);
CHECK (! is_Tuple<Types1::List>::value);
CHECK (! is_TuplePlain<Types1::List>::value);
CHECK (!is_TupleListType<Types1::List>::value);
CHECK (! is_NullTuple<Types1::List>::value);
}
void
check_sub_tuple_types()
{
cout << "\t:\n\t: ---Sub-Tuple-Types----\n";
typedef Append<Types2::List, Types1::List>::List L2;
typedef Tuple<L2> T_L2; // list-style Tuple
typedef Types<T_L2::HeadType> Head;
typedef T_L2::TailType Tail;
DISPLAY (T_L2);
DISPLAY (Head);
DISPLAY (Tail);
typedef T_L2::TupleType T2; // plain-flat Tuple
typedef Types<T2::HeadType> Head2;
typedef T2::TailType Tail2;
DISPLAY (T2);
DISPLAY (Head2);
DISPLAY (Tail2);
typedef Tuple<Types<> > NulT; // plain-flat empty Tuple
typedef Tuple<NullType> NulL; // list-style empty Tuple
DISPLAY (T2::Type); // irrespective of the flavour,
DISPLAY (T2::TailType); // a basic set of typedefs is
DISPLAY (T2::TupleType); // always available
DISPLAY (T2::ThisType);
DISPLAY (T2::Tail);
DISPLAY (T2::ArgList);
DISPLAY (T_L2::Type); // the element types as type sequence
DISPLAY (T_L2::TailType); // the element types of the "tail" tuple
DISPLAY (T_L2::TupleType); // corresponding plain-flat tuple type
DISPLAY (T_L2::ThisType); // "type_of(this)"
DISPLAY (T_L2::Tail); // tail tuple
DISPLAY (T_L2::ArgList); // typelist comprised of the element types
DISPLAY (NulT::Type);
DISPLAY (NulT::TailType);
DISPLAY (NulT::TupleType);
DISPLAY (NulT::ThisType);
DISPLAY (NulT::Tail);
DISPLAY (NulT::ArgList);
DISPLAY (NulL::Type);
DISPLAY (NulL::TailType);
DISPLAY (NulL::TupleType);
DISPLAY (NulL::ThisType);
DISPLAY (NulL::Tail);
DISPLAY (NulL::ArgList);
}
void
check_shiftedTuple()
{
cout << "\t:\n\t: ---Shifted-Tuple---\n";
typedef Append<Types2::List, Types3::List>::List L3;
typedef Tuple<L3>::Type Ty3;
typedef Tuple<Ty3> T3;
typedef Shifted<Ty3,0>::Type Ty_0; DISPLAY (Ty_0);
typedef Shifted<Ty3,1>::Type Ty_1; DISPLAY (Ty_1);
typedef Shifted<Ty3,2>::Type Ty_2; DISPLAY (Ty_2);
typedef Shifted<Ty3,3>::Type Ty_3; DISPLAY (Ty_3);
typedef Shifted<Ty3,4>::Type Ty_4; DISPLAY (Ty_4);
typedef Shifted<Ty3,0>::Type Ty_0; DISPLAY (Ty_0);
typedef Shifted<Ty3,1>::Type Ty_1; DISPLAY (Ty_1);
typedef Shifted<Ty3,2>::Type Ty_2; DISPLAY (Ty_2);
typedef Shifted<Ty3,3>::Type Ty_3; DISPLAY (Ty_3);
typedef Shifted<Ty3,4>::Type Ty_4; DISPLAY (Ty_4);
typedef T3::ShiftedTuple<0>::Type T_0; DISPLAY (T_0);
typedef T3::ShiftedTuple<1>::Type T_1; DISPLAY (T_1);
typedef T3::ShiftedTuple<2>::Type T_2; DISPLAY (T_2);
typedef T3::ShiftedTuple<3>::Type T_3; DISPLAY (T_3);
typedef T3::ShiftedTuple<4>::Type T_4; DISPLAY (T_4);
T3 tu3; DUMPVAL (tu3);
T_0 tu3_0 = tu3.getShifted<0>(); DUMPVAL (tu3_0);
T_1 tu3_1 = tu3.getShifted<1>(); DUMPVAL (tu3_1);
@ -268,13 +268,13 @@ namespace test {
T_3 tu3_3 = tu3.getShifted<3>(); DUMPVAL (tu3_3);
T_4 tu3_4 = tu3.getShifted<4>(); DUMPVAL (tu3_4);
}
void
check_tuple_creation()
{
cout << "\t:\n\t: ---creating-Tuples---\n";
Tuple<Types1> tup1 ;
Tuple<Types1> tup11 (Num<1>(11) );
Tuple<Types1> tup12 (Num<1>(), Num<3>(33) );
@ -283,22 +283,22 @@ namespace test {
DUMPVAL (tup11);
DUMPVAL (tup12);
DUMPVAL (tup13);
typedef Tuple<Types<int,int,Num<11> > > Tup2;
Tup2 tup2 = tuple::make(41,42, Num<11>(43)); // build tuple from given values
DISPLAY (Tup2);
DUMPVAL (tup2);
typedef Tup2::Tail Tup22;
Tup22 tup22 = tup2.getTail();
DISPLAY (Tup22);
DUMPVAL (tup22);
typedef Tup2::Tail::Tail Tup222;
Tup222 tup222 = tup22.getTail();
DISPLAY (Tup222);
DUMPVAL (tup222);
typedef Tuple<Types<> > T0T;
typedef Tuple<NullType> T0L;
T0T nullT = tuple::makeNullTuple();
@ -312,31 +312,31 @@ namespace test {
DUMPVAL (nulTcpy);
DUMPVAL (nulTref);
}
void
check_build_from_subTuple()
{
cout << "\t:\n\t: ---build-from-sub-Tuples---\n";
typedef Append<Types1::List, Types3::List>::List TL;
typedef Tuple<TL>::Type TT;
typedef Tuple<TL> T1357L;
typedef Tuple<TT> T1357T;
DISPLAY (T1357L);
DISPLAY (T1357T);
typedef Tuple<Types1::List> T135L;
typedef Tuple<Types<Num<5>,Num<7> > > T57T;
typedef Tuple<Types<Num<3>,Num<5> > > T35T;
DISPLAY (T135L);
DISPLAY (T57T);
DISPLAY (T35T);
T135L sub135;
T57T sub57;
T35T sub35 (Num<3>(8),Num<5>(8));
DUMPVAL (sub135);
T1357T b_135 = tuple::BuildTuple<T1357T,T135L>::create(sub135);
DUMPVAL (b_135);
@ -346,113 +346,113 @@ namespace test {
DUMPVAL (b_135);
b_135 = tuple::BuildTuple<TT,Types1::List>::create(sub135);
DUMPVAL (b_135); // all variations of type specification lead to the same result
DUMPVAL (sub57);
T1357T b_57 = tuple::BuildTuple<T1357T,T57T,2>::create(sub57);
DUMPVAL (b_57);
DUMPVAL (sub35);
T1357T b_35 = tuple::BuildTuple<T1357T,T35T,1>::create(sub35);
DUMPVAL (b_35);
b_35 = tuple::BuildTuple<T1357T,T35T,2>::create(sub35);
DUMPVAL (b_35); // note: wrong start position, argument tuple ignored completely
b_35 = tuple::BuildTuple<T1357T,T35T,4>::create(sub35);
DUMPVAL (b_35);
// use an argument tuple beyond the last argument of the target tuple...
typedef Tuple<Types<Num<7>,Num<8> > > T78T;
T78T sub78 (Num<7>(77),Num<8>(88));
DUMPVAL (sub78);
T1357T b_78 = tuple::BuildTuple<T1357T,T78T,3>::create(sub78);
DUMPVAL (b_78); // note: superfluous arguments ignored
typedef Tuple<Types<> > NulT;
NulT nult;
T1357T b_nul = tuple::BuildTuple<T1357T,NulT,1>::create(nult);
DUMPVAL (b_nul);
b_nul = tuple::BuildTuple<T1357T,NulT,4>::create(nult);
DUMPVAL (b_nul);
NulT b_nul2 = tuple::BuildTuple<NulT,T78T>::create(sub78);
DUMPVAL (b_nul2)
b_nul2 = tuple::BuildTuple<NulT,T78T,1>::create(sub78);
DUMPVAL (b_nul2)
}
void
check_tuple_copy()
{
cout << "\t:\n\t: ---copy-operations---\n";
Tuple<Types1> tup1 (Num<1>(11), Num<3>(33), Num<5>() );
Tuple<Types1> tup11 (tup1);
tup11.getAt<2>().o_ = 44;
DUMPVAL (tup1);
DUMPVAL (tup11);
tup1 = tup11;
DUMPVAL (tup1);
Tuple<Types1::List> tupL = tup11.getShifted<0>();
Tuple<Types1> tup1L (tupL); // create plain tuple from list-style tuple
DUMPVAL (tupL);
DUMPVAL (tup1L);
}
void
check_value_access()
{
cout << "\t:\n\t: ---value-access---\n";
typedef Append<Types2::List, Types2::List>::List T2424;
typedef Tuple<T2424> TupX;
TupX tupX;
DISPLAY (TupX);
DUMPVAL (tupX);
Tuple<Types2> tu2;
DUMPVAL (tu2);
tuple::element<1>(tu2).o_ = 5;
tu2.getHead() = Num<2> (tu2.getAt<1>().o_);
DUMPVAL (tu2);
tupX.getShifted<2>() = tu2;
DUMPVAL (tupX);
typedef Shifted<TupX::TupleType,2>::TupleType T4;
T4 t4 (tupX.getShifted<2>());
DISPLAY (T4);
DUMPVAL (t4);
DISPLAY (TupX::Type)
DISPLAY (TupX::TailType)
DISPLAY (TupX::ThisType)
DISPLAY (TupX::TupleType)
typedef TupX::TupleType TupT;
DISPLAY (TupT::Type)
DISPLAY (TupT::TailType)
DISPLAY (TupT::ThisType)
DISPLAY (TupT::TupleType)
TupT tupXcopy (tupX);
DUMPVAL (tupXcopy);
TupT& tupXcast (tupX.tupleCast()); // (down)cast list-style to plain tuple
DUMPVAL (tupXcast);
}
};
/** Register this test class... */
LAUNCHER (TypeTuple_test, "unit common");
}}} // namespace lumiera::typelist::test

View file

@ -23,7 +23,9 @@
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/util.hpp"
#include "lib/error.hpp"
#include "lib/scoped-holder.hpp"
#include "testdummy.hpp"
@ -38,6 +40,7 @@ namespace test{
using ::Test;
using util::isnil;
using lumiera::error::LUMIERA_ERROR_LOGIC;
using std::map;
using std::cout;
@ -47,7 +50,7 @@ namespace test{
/**********************************************************************************
* @test ScopedHolder and ScopedPtrHolder are initially empty and copyable.
* @test ScopedHolder and ScopedPtrHolder are initially empty and copyable.
* After taking ownership, they prohibit copy operations, manage the
* lifecycle of the contained object and provide smart-ptr like access.
* A series of identical tests is conducted both with the ScopedPtrHolder
@ -105,8 +108,8 @@ namespace test{
TRACE (test, "holder at %p", &holder);
TRACE (test, "object at %p", holder.get() );
TRACE (test, "size(object) = %u", sizeof(*holder));
TRACE (test, "size(holder) = %u", sizeof(holder));
TRACE (test, "size(object) = %lu", sizeof(*holder));
TRACE (test, "size(holder) = %lu", sizeof(holder));
}
CHECK (0==checksum);
}
@ -132,7 +135,7 @@ namespace test{
checksum -= val;
CHECK (0==checksum);
}
CHECK (!holder); /* because the exception happens in ctor
CHECK (!holder); /* because the exception happens in ctor
object doesn't count as "created" */
throw_in_ctor = false;
}
@ -157,60 +160,34 @@ namespace test{
CHECK (holder);
long currSum = checksum;
void* adr = holder.get();
try
{
holder2 = holder;
NOTREACHED ();
}
catch (lumiera::error::Logic&)
{
CHECK (holder);
CHECK (!holder2);
CHECK (holder.get()==adr);
CHECK (checksum==currSum);
}
try
{
holder = holder2;
NOTREACHED ();
}
catch (lumiera::error::Logic&)
{
CHECK (holder);
CHECK (!holder2);
CHECK (holder.get()==adr);
CHECK (checksum==currSum);
}
VERIFY_ERROR(LOGIC, holder2 = holder );
CHECK (holder);
CHECK (!holder2);
CHECK (holder.get()==adr);
CHECK (checksum==currSum);
VERIFY_ERROR(LOGIC, holder = holder2 );
CHECK (holder);
CHECK (!holder2);
CHECK (holder.get()==adr);
CHECK (checksum==currSum);
create_contained_object (holder2);
CHECK (holder2);
CHECK (checksum != currSum);
currSum = checksum;
try
{
holder = holder2;
NOTREACHED ();
}
catch (lumiera::error::Logic&)
{
CHECK (holder);
CHECK (holder2);
CHECK (holder.get()==adr);
CHECK (checksum==currSum);
}
try
{
HO holder3 (holder2);
NOTREACHED ();
}
catch (lumiera::error::Logic&)
{
CHECK (holder);
CHECK (holder2);
CHECK (checksum==currSum);
}
VERIFY_ERROR(LOGIC, holder = holder2 );
CHECK (holder);
CHECK (holder2);
CHECK (holder.get()==adr);
CHECK (checksum==currSum);
VERIFY_ERROR(LOGIC, HO holder3 (holder2) );
CHECK (holder);
CHECK (holder2);
CHECK (checksum==currSum);
}
CHECK (0==checksum);
}
@ -236,7 +213,7 @@ namespace test{
CHECK (!contained);
} // 100 holder objects created by sideeffect
CHECK (0==checksum); // ..... without creating any contained object!
CHECK (0==checksum); // ..... without creating any contained object!
CHECK (!isnil (maph));
CHECK (100==maph.size());
@ -257,7 +234,7 @@ namespace test{
CHECK (checksum == currSum - value55); // proves object#55's dtor has been invoked
CHECK (maph.size() == 99);
maph[55]; // create new empty holder by sideeffect...
maph[55]; // create new empty holder by sideeffect...
CHECK (&maph[55]);
CHECK (!maph[55]);
CHECK (maph.size() == 100);
@ -272,4 +249,3 @@ namespace test{
}} // namespace lib::test

View file

@ -26,7 +26,7 @@
#include "lib/util.hpp"
#include "lib/scoped-holder.hpp"
#include "lib/scopedholdertransfer.hpp"
#include "lib/scoped-holder-transfer.hpp"
#include "testdummy.hpp"
#include <iostream>
@ -67,9 +67,9 @@ namespace lib {
if (throw_in_transfer)
throw to.getVal();
to.setVal (from.getVal());
from.setVal(0);
swap (from,to);
from.setVal(0); // remove the old Dummy from accounting (checksum)
}
};

View file

@ -41,9 +41,9 @@ using std::cout;
namespace lib {
namespace test{
using lumiera::error::LUMIERA_ERROR_ASSERTION;
/**
* Target object to be instantiated as Singleton
* Allocates a variable amount of additional heap memory
@ -54,34 +54,34 @@ namespace test{
public:
static int cnt;
static void setCountParam (uint c) { Interface::cnt = c; }
virtual string identify() { return "Interface"; }
protected:
Interface () : TestTargetObj(cnt) {}
virtual ~Interface() {}
friend class singleton::StaticCreate<Interface>;
friend class singleton::HeapCreate<Interface>;
};
int Interface::cnt = 0;
class Impl : public Interface
{
public:
virtual string identify() { return "Implementation"; }
};
// for checking the safety.....
class Impl_XXX : public Impl { };
class Unrelated { };
/*******************************************************************
* @test specialised variant of the Singleton Factory, for creating
* subclasses (implementation classes) without coupling the
@ -93,33 +93,34 @@ namespace test{
*/
class SingletonSubclass_test : public Test
{
virtual void run(Arg arg)
virtual void
run(Arg arg)
{
uint num= isnil(arg)? 1 : lexical_cast<uint>(arg[1]);
cout << format("using the Singleton should create TargetObj(%d)...\n") % num;
Interface::setCountParam(num);
// marker to declare the concrete type to be created
singleton::UseSubclass<Impl> typeinfo;
// define an instance of the Singleton factory,
// Specialised to create the concrete Type passed in
SingletonSubclassFactory<Interface> instance (typeinfo);
// Now use the Singleton factory...
// Note: we get the Base type
Interface& t1 = instance();
Interface& t2 = instance();
CHECK ( &t1 == &t2, "not a Singleton, got two different instances." );
cout << "calling a non-static method on the Singleton-"
<< t1.identify() << "\n"
<< string (t1) << "\n";
///////////////////////////////////////////////////////////////////////////////TODO: find a way to configure NoBug to throw in case of assertion
///////////////////////////////////////////////////////////////////////////////TODO: just for the proc tests. Also find a better way to configure
///////////////////////////////////////////////////////////////////////////////TODO: the non-release check. Then re-enable these checks...
@ -127,32 +128,33 @@ namespace test{
// verify_error_detection ();
//#endif
}
void verify_error_detection ()
void
verify_error_detection ()
{
singleton::UseSubclass<Impl_XXX> more_special_type;
VERIFY_ERROR (ASSERTION, SingletonSubclassFactory<Interface> instance (more_special_type) );
/* in debug mode, an attempt to re-configure an already
* configured SingletonSubclassFactory with another type
* should be detected and spotted by assertion failure */
// Note: the following won't compile, because the "subclass" isn't a subclass...
//
// singleton::UseSubclass<Unrelated> yet_another_type;
// SingletonSubclassFactory<Interface> instance (yet_another_type);
}
};
/** Register this test class... */
LAUNCHER (SingletonSubclass_test, "unit common");
}} // namespace lib::test

View file

@ -40,23 +40,23 @@ using util::isnil;
namespace lib {
namespace test{
using std::string;
using boost::hash;
namespace { // test data
Symbol KEY1 ("Key1");
Symbol KEY2 ("Key2");
Symbol KEY3 ("Key3");
Symbol KEY4 ("Key4");
Symbol KEY5 ("Key5");
}
typedef std::tr1::unordered_map< Symbol, string, hash<Symbol> > HTable;
/*************************************************************
* @test build a hashtable using Symbol objects as Keys.
* Especially this verifies picking up a customised
@ -65,20 +65,20 @@ namespace test{
*/
class SymbolHashtable_test : public Test
{
void
virtual void
run (Arg)
{
checkHashFunction();
HTable table;
CHECK (isnil(table));
table[KEY1] = string (KEY1);
table[KEY2] = string (KEY2);
table[KEY3] = string (KEY3);
table[KEY4] = string (KEY4);
CHECK (!isnil(table));
CHECK (4 == table.size());
CHECK (contains (table, KEY1));
@ -86,80 +86,79 @@ namespace test{
CHECK (contains (table, KEY3));
CHECK (contains (table, KEY4));
CHECK (!contains (table, KEY5));
CHECK (string (KEY1) == table[KEY1]);
CHECK (string (KEY2) == table[KEY2]);
CHECK (string (KEY3) == table[KEY3]);
CHECK (string (KEY4) == table[KEY4]);
table[KEY3] += "...";
CHECK (string(KEY3) != table[KEY3]);
CHECK (string(KEY3)+"..." == table[KEY3]);
CHECK (isnil (table[KEY5])); // adds a new empty value object as side effect
CHECK (5 == table.size());
}
void
checkHashFunction()
{
string random = randStr(STRING_MAX_RELEVANT+1);
string copy1(random);
copy1[5] = '\0'; // truncate the c-String to 5 chars
string copy2(random);
copy2[rand() % STRING_MAX_RELEVANT] = '*'; // modify a random position
string copy3(copy2);
copy3[STRING_MAX_RELEVANT] = '*'; // modify behind observation limit
Symbol l0;
Literal l51 (copy1.c_str());
Literal l52 (random.substr(0,5).c_str());
Literal l_1 (random.c_str());
Literal l_2 (copy2.c_str());
Literal l_3 (copy3.c_str());
CHECK (isnil (l0));
CHECK (l0 != l51);
CHECK (l51 == l52);
CHECK (l51 != l_1);
CHECK (l_1 != l_2);
CHECK (l_2 == l_3); // difference not detected due to observation limit...
CHECK (!std::strncmp (l_2, l_3, STRING_MAX_RELEVANT ));
CHECK ( std::strncmp (l_2, l_3, STRING_MAX_RELEVANT+1));
size_t h0 = hash_value (l0);
size_t h51 = hash_value (l51);
size_t h52 = hash_value (l52);
size_t h_1 = hash_value (l_1);
size_t h_2 = hash_value (l_2);
size_t h_3 = hash_value (l_3);
CHECK (h0 == 0);
CHECK (h51 != 0);
CHECK (h52 != 0);
CHECK (h_1 != 0);
CHECK (h_2 != 0);
CHECK (h_3 != 0);
CHECK (h51 == h52); // verify the hash function indeed stops at '\0'
CHECK (h51 != h_1); // share a common prefix, but the hash differs
CHECK (h_1 != h_2); // the single random modification is detected
CHECK (h_2 == h_3); // because l_2 and l_3 differ behind the fixed observation limit
CHECK (h_1 == hash_value (l_1)); //reproducible
CHECK (h_2 == hash_value (l_2));
CHECK (h_3 == hash_value (l_3));
}
};
LAUNCHER (SymbolHashtable_test, "function common");
}} // namespace lib::test

View file

@ -45,13 +45,13 @@ namespace test{
* @test properties of Symbol data type. Currently this is
* just a thin wrapper for a const char *
* @todo this test is very much WIP, as the implementation
* of a real symbol type and symbol table remains
* to be done. See Ticket #157
* of a real symbol type and symbol table remains
* to be done. ///////////////////////////Ticket #157
*/
class Symbol_test : public Test
{
void
virtual void
run (Arg)
{
checkLiteral();
@ -126,4 +126,3 @@ namespace test{
}} // namespace lib::test

View file

@ -85,7 +85,7 @@ namespace test {
}
ClassLock<Probe> get_class_lock;
CHECK ( 1 == get_class_lock.use_count()); // embedded PerClassMonitor<Probe> got created exactly once
CHECK ( 1 == get_class_lock.use_count()); // embedded PerClassMonitor<Probe> got created exactly once
} // and stays alive until static dtors are called....
};

View file

@ -34,29 +34,29 @@ using test::Test;
namespace lib {
namespace test{
namespace { // private test classes and data...
const uint WAIT_mSec = 200; ///< milliseconds to wait before timeout
} // (End) test classes and data....
/********************************************************************************
* @test timeout feature on condition wait as provided by pthread and accessible
* via the object monitor based locking/waiting mechanism. Without creating
* multiple threads, we engage into a blocking wait, which aborts due to
* setting a timeout. (Note it is discouraged to use the timed wait feature;
* when possible, you should prefer relying on the Lumiera scheduler)
*
*
* @see SyncWaiting_test
* @see sync::Timeout
* @see sync.hpp
@ -65,48 +65,48 @@ namespace test{
: public Test,
Sync<RecursiveLock_Waitable>
{
friend class Lock; // allows inheriting privately from Sync
virtual void
run (Arg)
{
checkTimeoutStruct();
Lock block(this, &SyncTimedwait_test::neverHappens);
cout << "back from LaLaLand, alive and thriving!\n";
CHECK (block.isTimedWait());
}
bool
neverHappens() ///< the "condition test" used for waiting....
{
Lock currentLock(this); // get the Lock recursively
if (!currentLock.isTimedWait()) // right from within the condition test:
currentLock.setTimeout(WAIT_mSec); // switch waiting mode to timed wait and set timeout
return false;
}
void
checkTimeoutStruct()
{
sync::Timeout tout;
CHECK (!tout);
CHECK (0 == tout.tv_sec);
CHECK (0 == tout.tv_nsec);
tout.setOffset (0);
CHECK (!tout);
CHECK (0 == tout.tv_sec);
CHECK (0 == tout.tv_nsec);
timespec ref;
clock_gettime(CLOCK_REALTIME, &ref);
tout.setOffset (1);
@ -114,7 +114,7 @@ namespace test{
CHECK (0 < tout.tv_sec);
CHECK (ref.tv_sec <= tout.tv_sec);
CHECK (ref.tv_nsec <= 1000000 + tout.tv_nsec || ref.tv_nsec > 1000000000-100000);
clock_gettime(CLOCK_REALTIME, &ref);
tout.setOffset (1000);
CHECK (tout);
@ -125,14 +125,14 @@ namespace test{
<= tout.tv_nsec);
}
}
};
/** Register this test class... */
LAUNCHER (SyncTimedwait_test, "unit common");
}} // namespace lib::test

View file

@ -23,6 +23,7 @@
#include <boost/noncopyable.hpp>
#include <algorithm>
namespace lib {
@ -63,6 +64,12 @@ namespace test{
val_ = newVal;
}
friend void
swap (Dummy& dum1, Dummy& dum2) ///< checksum neutral
{
std::swap(dum1.val_, dum2.val_);
}
private:
void
init()

View file

@ -24,7 +24,7 @@
#include "lib/test/run.hpp"
#include "lib/scopedholdertransfer.hpp"
#include "lib/scoped-holder-transfer.hpp"
#include "testdummy.hpp"
#include <iostream>

View file

@ -1,6 +1,6 @@
format 58
"Builder" // ProcessingLayer::MObject::Builder
revision 20
revision 21
modified_by 5 "hiv"
// class settings
//class diagram settings
@ -74,7 +74,7 @@ format 58
iterative
activityaction 128773 "define segment"
opaque_action
pin 128133 "inFixture" explicit_type ""
pin 128133 "inContent" explicit_type ""
unordered
in
end
@ -107,11 +107,11 @@ format 58
end
flow 130181 "<flow>"
on pin_ref 128133 // inFixture
on pin_ref 128133 // inContent
end
flow 131717 "<flow>"
on pin_ref 128133 // inFixture
on pin_ref 128133 // inContent
end
end
@ -168,6 +168,14 @@ format 58
activitynode 129157 activity_final ""
end
activityobject 129157 "Timeline contents"
explicit_type ""
unordered
flow 133125 "<flow>"
on pin_ref 128133 // inContent
end
end
end
classdiagram 129285 "Builder Tool (Visitor)"

View file

@ -2,76 +2,72 @@ format 58
activitycanvas 130437 activity_ref 128005 // building the Engine
show_infonote default drawing_language default show_stereotype_properties default
xyzwh 147 27 2000 581 532
xyzwh 120 26 2000 468 525
params
parametercanvas 130565 parameter_ref 128645 // build Request
xyzwh 541 12 2002 113 31
xyzwh 401 11 2002 113 31
end
end
end
activityactioncanvas 130693 activityaction_ref 128645 // activity action configure Tools
show_infonote default drawing_language default show_stereotype_properties default
show_opaque_action_definition default
xyzwh 529 71 2005 136 41
xyzwh 389 63 2005 136 41
end
expansionregioncanvas 130821 expansionregion_ref 128133 // establish partitioning
xyzwh 359 208 2005 205 96
xyzwh 219 200 2005 205 96
nodes
expansionnodecanvas 131077 expansionnode_ref 128005 // segment Tool
xyzwh 458 203 2007 33 11 label_xy 456 183
xyzwh 318 195 2007 33 11 label_xy 316 175
end
expansionnodecanvas 132613 expansionnode_ref 128133 // segments
xyzwh 460 298 2007 33 11 label_xy 459 312
xyzwh 320 290 2007 33 11 label_xy 319 304
end
end
end
activityactioncanvas 130949 activityaction_ref 128773 // activity action define segment
show_infonote default drawing_language default show_stereotype_properties default
show_opaque_action_definition default
xyzwh 409 236 2010 135 42
xyzwh 269 228 2010 135 42
pins
pincanvas 131205 pin_ref 128133 // inFixture
xyzwh 399 249 2012 11 11 label_xy 315 241
pincanvas 131205 pin_ref 128133 // inContent
xyzwh 259 241 2012 11 11 label_xy 170 250
end
end
end
activityobjectcanvas 131333 activityobject_ref 128005 // activity object Fixture
show_infonote default drawing_language default show_stereotype_properties default
xyzwh 177 239 2005 49 31
end
activitynodecanvas 133509 activitynode_ref 129029 // fork
horizontal xyz 585 147 2005
horizontal xyz 445 139 2005
end
expansionregioncanvas 133893 expansionregion_ref 128261 // build Processors
xyzwh 359 349 2005 272 152
xyzwh 219 341 2005 272 152
nodes
expansionnodecanvas 134021 expansionnode_ref 128261 // build Tool
xyzwh 581 344 2007 33 11 label_xy 573 324
xyzwh 441 336 2007 33 11 label_xy 433 316
end
expansionnodecanvas 134149 expansionnode_ref 128389 // segments
xyzwh 460 344 2007 33 11 label_xy 460 359
xyzwh 320 336 2007 33 11 label_xy 320 351
end
expansionnodecanvas 136581 expansionnode_ref 128517 // complete Render Engine
xyzwh 460 495 2007 33 11 label_xy 496 500
xyzwh 320 487 2007 33 11 label_xy 356 492
end
end
end
activityactioncanvas 134277 activityaction_ref 128901 // activity action create ProcNode
show_infonote default drawing_language default show_stereotype_properties default
show_opaque_action_definition default
xyzwh 419 389 2010 114 42
xyzwh 279 381 2010 114 42
end
activityactioncanvas 134405 activityaction_ref 129029 // activity action connect
show_infonote default drawing_language default show_stereotype_properties default
show_opaque_action_definition default
xyzwh 420 442 2015 113 42
xyzwh 280 434 2015 113 42
end
activitynodecanvas 134533 activitynode_ref 129157 // activity_final
xyz 543 527 2005
xyz 403 519 2005
end
simplerelationcanvas 131461 simplerelation_ref 128389
from ref 131333 z 1999 to point 400 253
line 131589 z 1999 to ref 130437
activityobjectcanvas 137093 activityobject_ref 129157 // activity object Timeline contents
show_infonote default drawing_language default show_stereotype_properties default
xyzwh 147 157 2005 99 31
end
flowcanvas 132101 flow_ref 130309 // <flow>
@ -92,8 +88,8 @@ end
flowcanvas 133765 flow_ref 130949 // <flow>
geometry VHV
from ref 133509 z 2004 to point 595 180
line 134789 z 2004 to point 472 180
from ref 133509 z 2004 to point 455 172
line 134789 z 2004 to point 332 172
line 134917 z 2004 to ref 131077
show_infonote default drawing_language default show_stereotype_properties default write_horizontally default
end
@ -105,7 +101,7 @@ end
flowcanvas 135429 flow_ref 131205 // <flow>
geometry HVr
from ref 134021 z 2006 to point 595 407
from ref 134021 z 2006 to point 455 399
line 135557 z 2006 to ref 134277
show_infonote default drawing_language default show_stereotype_properties default write_horizontally default
end
@ -119,11 +115,6 @@ flowcanvas 136197 flow_ref 131461 // <flow>
from ref 134277 z 2009 to ref 134405
show_infonote default drawing_language default show_stereotype_properties default write_horizontally default
end
flowcanvas 136453 flow_ref 131717 // <flow>
from ref 131333 z 2004 to ref 131205
show_infonote default drawing_language default show_stereotype_properties default write_horizontally default
end
flowcanvas 136709 flow_ref 131845 // <flow>
from ref 134405 z 2006 to ref 136581
@ -131,9 +122,16 @@ flowcanvas 136709 flow_ref 131845 // <flow>
end
flowcanvas 136837 flow_ref 131973 // <flow>
from ref 136581 z 2004 to point 487 524
from ref 136581 z 2004 to point 347 516
line 136965 z 2004 to ref 134533
show_infonote default drawing_language default show_stereotype_properties default write_horizontally default
end
preferred_whz 768 616 1
flowcanvas 137221 flow_ref 133125 // <flow>
geometry VH
from ref 137093 z 2004 to point 194 244
line 137349 z 2004 to ref 131205
show_infonote default drawing_language default show_stereotype_properties default write_horizontally default
end
preferred_whz 627 621 1
end

View file

@ -1,11 +1,13 @@
window_sizes 1324 1020 270 1044 872 71
window_sizes 1615 1020 270 1335 872 71
diagrams
classdiagram_ref 136453 // Session backbone
631 352 100 4 0 0
active objectdiagram_ref 138885 // ModelAssetRelations
objectdiagram_ref 138885 // ModelAssetRelations
730 488 100 4 0 0
classdiagram_ref 128133 // Session structure
835 697 100 4 0 0
835 697 100 4 300 0
active activitydiagram_ref 129413 // build flow
627 621 100 4 0 0
end
show_stereotypes
selected
@ -31,7 +33,7 @@ open
class_ref 152453 // PlacementRef
classrelation_ref 178437 // <realization>
class_ref 153733 // QueryFocusStack
classview_ref 128261 // Builder Workings
expansionregion_ref 128133 // establish partitioning
usecaseview_ref 128261 // config examples
classview_ref 128133 // Engine Workings
class_ref 164485 // Request

View file

@ -1,6 +1,6 @@
format 58
"lumiera"
revision 64
revision 65
modified_by 5 "hiv"
cpp_root_dir "../../src/"

BIN
wiki/draw/Fixture1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

View file

@ -1077,6 +1077,26 @@ Please note the shortcomings and logical contradictions in the solution currentl
* The current design rather places the implementation according to the roles of the involved entities, which causes some ping-pong on the implementation level. Especially the ScopeLocator singleton can be accessed multiple times. This is the usual clarity vs. performance tradeoff. Scope resolution is assumed rather to be //not performance critical.//
</pre>
</div>
<div title="BuildFixture" modifier="Ichthyostega" modified="201011290504" created="201011282003" tags="Builder spec operational" changecount="32">
<pre>//Building the fixture is actually at the core of the [[builder's operation|Builder]]//
{{red{WIP as of 11/10}}} &amp;rarr; see also the [[planning page|PlanningBuildFixture]]
;Resolving the DAG[&gt;img[Steps towards creating a Segmentation|draw/SegmentationSteps1.png]]
Because of the possibility of binding a Sequence multiple times, and maybe even nested as virtual clip, the [[high-level model|HighLevelModel]] actually constitutes a DAG, not a tree. This leds to quite some tricky problems, which we try to resolve by //rectifying the DAG into N virtual trees.// (&amp;rarr; BindingScopeProblem)
Relying on this transformation, each Timeline spans a sub-tree virtually separated from all other timelines; the BuildProcess is driven by [[visiting|VisitorUse]] all the //tangible// objects within this subtree. In the example shown to the right, Sequence-β is both bound as VirtualClip into Sequence-α, as well as bound independently as top-level sequence into Timeline-2. Thus it will be visited twice, but the QueryFocus mechanism ensures that each visitation »sees« the proper context.
;Explicit Placements
Each tangible object placement (relevant for rendering), which is encountered during that visitation, gets //resolved// into an [[explicit placement|ExplicitPlacement]]. If we see [[Placement]] as a positioning within a multi dimensional configuration space, then the resolution into an explicit placement is like the creation of an ''orthogonal base'': Within the explicit placement, each LocatingPin corresponds exactly to one degree of freedom and can be considered independently from all other locating pins. This resolution step removes any fancy dynamic behaviour and all scoping and indirect references. Indeed, an explicit placement is a mere //value object;// it isn't part of the session core (PlacementIndex), isn't typed and can't be referred indirectly.
;Segmentation of Time axis
This simple and explicit positioning thus allows to arrange all objects as time intervals on a single axis. Any change and especially any overlap is likely to create a different wiring configuration. Thus, for each such configuration change, we fork off a new //segment// and //copy over// all partially touched placements. The resulting seamless sequence of non-overlapping time intervals provides the backbone of the datastructure called [[Fixture]].
;Building the Network
From this backbone, the actual [[building mechanism|BuilderMechanics]] proceeds as a ongoing visitation and resolution, resulting in the gowth of a network of [[render nodes|ProcNode]] starting out from the source reading nodes and proceeding up through the local pipes, the transitions and the global pipes. When this build process is exhausted, besides the actual network, the result is a //residuum of nodes not connected any further.// Any of these [[exit nodes|ExitNode]] can be associated to a ~Pipe-ID in the high-level model. Within each segment, there should be one exit node per pipe-ID at max. These are the [[model ports|ModelPort]] resulting from the build process, keyed by their corresponding ~Pipe-ID.
&amp;rarr; see [[Structure of the Fixture|Fixture]]
</pre>
</div>
<div title="BuildProcess" modifier="Ichthyostega" modified="200906071813" created="200706190658" tags="Builder operational img" changecount="30">
<pre>All decisions on //how // the RenderProcess has to be carried out are concentrated in this rather complicated Builder Subsystem. The benefit of this approach is, besides decoupling of subsystems, to keep the actual performance-intensive video processing code as simple and transparent as possible. The price, in terms of increased complexity &amp;mdash; to pay in the Builder &amp;mdash; can be handled by making the Build Process generic to a large degree. Using a Design By Contract approach we can decompose the various decisions into small decision modules without having to trace the actual workings of the Build Process as a whole.
@ -1708,8 +1728,12 @@ To make the intended use of the classes more clear, consider the following two e
<pre>a special ProcNode which is used to pull the finished output of one Render Pipeline (Tree or Graph). This term is already used in the Cinelerra2 codebase. I am unsure at the moment if it is a distinct subclass or rahter a specially configured ProcNode (a general design rule tells us to err in favour of the latter if in doubt).
</pre>
</div>
<div title="ExplicitPlacement" modifier="MichaelPloujnikov" modified="200706271458" created="200706220304" tags="def" changecount="2">
<pre>A special kind (subclass) of [[Placement]]. As such it is always linked to a //Subject//, i.e. a MObject. In addition to the properties of a (unspecific) Placement, the ExplicitPlacement specifies a absolute time and track position for locating the Subject
<div title="ExplicitPlacement" modifier="Ichthyostega" modified="201012122052" created="200706220304" tags="def" changecount="6">
<pre>A special kind (subclass) of [[Placement]]. As such it is always linked to a //Subject//, i.e. a MObject. But contrary to the (standard) placements, which may exhibit all kinds of fancy dynamic and scope dependent behaviour, within an explicit placement all properties are resolved and materialised. While the (standard) placement may contain an arbitrary list of LocatingPin objects, the resolution into an explicit placement performs a kind of »orthogonalisation«: each remaining LocatingPin defines exactly one degree of freedom independent of all others. Most notably, the explicit placement always specifies a absolute time and [[output designation|OutputDesignation]] for for locating the Subject. Explicit placements are ''immutable''.
!!Implementation considerations
Explicit placements are just created and never mutated, but copying and storage might become a problem.
It would thus be desirable to have a fixed-sized allocation, able to hold the placement body as well as the (fixed) locating pins inline.
</pre>
</div>
<div title="Factories" modifier="Ichthyostega" modified="201003160211" created="200708100401" tags="impl discuss excludeMissing rewrite" changecount="24">
@ -1766,19 +1790,34 @@ Some further details
* a special case of this factory use is the [[Singleton]] factory, which is used a lot within the Proy-Layer code
</pre>
</div>
<div title="Fixture" modifier="Ichthyostega" modified="201007160052" created="200706220324" tags="def" changecount="10">
<pre>a specially configured sequence list
<div title="Fixture" modifier="Ichthyostega" modified="201012092320" created="200706220324" tags="def spec Builder Model" changecount="44">
<pre>a specially configured view -- joining together high-level and low-level model
* all MObjects have their position, length and configuration set up ready for rendering.
* any nested sequences (or other kinds of indirections) have been resolved.
* every MObject is associated with an ExplicitPlacement, which declares a fixed position (Time, [[Pipe-ID|OutputDesignation]])
* these ~ExplicitPlacements are contained in a ordered List, sometimes denoted as the //effective timeline.//
* besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPort]]
* every MObject is attached by an ExplicitPlacement, which declares a fixed position (Time, [[Pipe|OutputDesignation]])
* these ~ExplicitPlacements are contained immediately within the Fixture, ordered by time
* besides, there is a collection of all effective, possibly externally visible [[model ports|ModelPortRegistry]]
As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. Creating the Fixture can be seen as a preprocessing step to simplify the build process. For this reason, the process of [[(re)building the fixture|PlanningBuildFixture]] has been designed together with the [[Builder]]
As the builder and thus render engine //only consults the fixture,// while all editing operations finally propagate to the fixture as well, we get an isolation layer between the high level part of the Proc layer (editing, object manipulation) and the render engine. [[Creating the Fixture|BuildFixture]] is an important sideeffect of running the [[Builder]] when createing the [[render engine network|LowLevelModel]].
!{{red{WIP}}} Structure of the fixture
The fixture is like a grid, where one dimension is given by the [[model ports|ModelPort]], and the other dimension extends in time. The time axis is grouped in segments of constant structure.
A problem yet to be solved is how the fixture relates to the mulitude of top-level timelines, without generating a too fine grained segmentation.
[&lt;img[Structure of the Fixture|draw/Fixture1.png]]
The fixture is like a grid, where one dimension is given by the [[model ports|ModelPortRegistry]], and the other dimension extends in time. Within the time dimension there is a grouping into [[segments|Segmentation]] of constant structure.
;Model Ports
:The model ports share a single uniform and global name space: actually they're keyed by ~Pipe-ID
:Model ports are derived as a result of the build process, as the //residuum// of all nodes not connected any further
:Each port belongs to a specific Timeline and is associated with the [[Segmentation]] of that timeline.
;Segmentation
:The segmentation partitiones the time axis of a single timeline into segments of constant (wiring) configuration
:Together, the segments form a seamless sequence of time intervals. They contain a copy of each (explicit) placement of a visible object touching that time interval. Besides that, segments are the top level grouping device of the render engine node graph; they are always built and discarded at once.
:Segments may be //hot swapped// into an ongoing render.
;Exit Nodes
:Each segment holds an ExitNode for each relevant ModelPort of the corresponding timeline.
:Thus the exit nodes are keyed by ~Pipe-ID as well (and consequently have a distinct [[stream type|StreamType]]) -- each model port coresponds to {{{&lt;number_of_segments&gt;}}} separate exit nodes, but of course an exit node may be //mute.//
</pre>
</div>
<div title="ForwardIterator" modifier="Ichthyostega" modified="200912190027" created="200910312114" tags="Concepts def spec" changecount="17">
@ -2784,12 +2823,41 @@ These are used as token for dealing with other objects and have no identity of t
</pre>
</div>
<div title="ModelPort" modifier="Ichthyostega" created="201011100234" tags="def" changecount="1">
<pre>Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &amp;mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel and denote a specific ExitNode in the render nodes network.
<div title="ModelPort" modifier="Ichthyostega" modified="201012112233" created="201011100234" tags="def spec" changecount="8">
<pre>Any point where output possibly might be produced. Model port entities are located within the [[Fixture]] &amp;mdash; model port as a concept spans the high-level and low-level view. A model port can be associated both to a pipe in the HighLevelModel but at the same time denotes a set of corresponding [[exit nodes|ExitNode]] within the [[segments|Segmentation]] of the render nodes network.
A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output desitnation and some other entity actually uses this designation as a target, either directly or indirecly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
A model port is rather derived than configured; it emerges when a pipe [[claims|WiringClaim]] an output destination and some other entity actually uses this designation as a target, either directly or indirectly. This match of provision and usage is detected during the build process and produces an entry in the fixture's model port table. These model ports in the fixture are keyed by ~Pipe-ID, thus each model port has an associated StreamType.
Model ports are the effective, resulting ouputs of each timeline; additional ports result from [[connecting a viewer|ViewerPlayConnection]]. Any render or display process happens at a model port.
Model ports are the effective, resulting outputs of each timeline; additional ports result from [[connecting a viewer|ViewerPlayConnection]]. Any render or display process happens at a model port.
!formal specification
Model port is a //conceptual entity,// denoting the possibility to pull generated data of a distinct (stream)type from a specific bus within the model -- any possible output produced or provided by Lumiera is bound to appear at a model port. The namespace of model ports is global, each being associated with a ~Pipe-ID.
Model ports are represented by small non-copyable descriptor objects with distinct identity, which are owned and managed by the [[Fixture]]. Clients are bound to resolve a model port on each usage, as configuration changes within the model might cause ports to appear and decease. To stress this usage pattern, actually {{{ModelPort}}} instances are small copyable value objects (smart handles), which can be used to access data within an opaque registry. Each model port belongs to a specific Timeline and is aware of this association, as is the timeline, allowing to get the collection of all ports of a given timeline. Besides, within the Fixture each model port refers to a specific [[segmentation of the time axis|Segmentation]] (relevant for this special timeline actually). Thus, with the help of this segmentation, a model port can yield an ExitNode to pull frames for a given time.
</pre>
</div>
<div title="ModelPortRegistry" modifier="Ichthyostega" modified="201012112230" created="201012030314" tags="Model impl" changecount="3">
<pre>Model ports are conceptual entities, denoting the points where output might possibly be produced &amp;rarr; see [[definition|ModelPort]].
But there is an actual representation, a collection of small descriptor objects managed by the Fixture and organised within the model port table datastructure. Because model ports are discovered during the build process, we need the ability to (re)build this table dynamically, finally swapping in the modified configuration with a transactional switch. Only the builder is allowed to perform such mutations, while for client code model ports are immutable.
!supported operations
* get the model port by ~Pipe-ID
* a collection of model ports per timeline
* sub-grouping by media kind, possibly even by StreamType
* possibility to enumerate model ports in distinct //order,// defined within the timeline
* with the additional possibility to filter by media kind or suitable stream type.
* a way to express the fact of //not having a model port.//
!!!mutating and rebuilding
Model ports are added once and never changed. The corresponding timeline and pipe is known at setup time, but the overall number of model ports is determined only as a result of completing the build process. At that point, the newly built configuration is swapped in transactionally to become the current configuration.
!Implementation considerations
The transactional switch creates a clear partitioning in the lifespan of the model port table. //Before// that point, entries are just added, but not accessed in any way. //After// that point, no further mutation occurs, but lookup is frequent and happens in a variety of different configurations and transient orderings.
This observation leads to the idea of using //model port references// as frontend to provide all kinds of access, searching and reordering. These encapsulate the actual access by silently assuming reference to &quot;the&quot; global current model port configuration. This way the actual model port descriptors could be bulk allocated in a similar manner as the processing nodes and wiring descriptors. Access to stale model ports could be detected by the port references, allowing also for a {{{bool}}} checkable &quot;has no port&quot; information.
A model port registry, maintained by the builder, is responsible for storing the discovered model ports within a model port table, which is then swapped in after completing the build process. The {{{builder::ModelPortRegistry}}} acts as management interface, while client code accesses just the {{{ModelPort}}} frontend. A link to the actual registry instance is hooked into that frontend when bringing up the builder subsystem.
</pre>
</div>
<div title="ModelRootMO" modifier="Ichthyostega" modified="200912110245" created="200912080307" tags="def" changecount="5">
@ -4611,6 +4679,44 @@ We need to detect attaching and detaching of
* root &amp;harr; [[Track]]
</pre>
</div>
<div title="Segmentation" modifier="Ichthyostega" modified="201012122202" created="201012121901" tags="def spec Builder" changecount="24">
<pre>//Segmentation of timeline// denotes a data structure and a step in the BuildProcess.
When [[building the fixture|BuildFixture]], ~MObjects -- as handled by their Placements -- are grouped below each timeline using them; Placements are then to be resolved into [[explicit Placements|ExplicitPlacement]], resulting in a single well defined time interval for each object. This allows to cut this effective timeline into slices of constant wiring structure, which are represented through the ''Segmentation Datastructure'', a time axis with segments holding object placements and [[exit nodes|ExitNode]]. &amp;nbsp;&amp;rarr; see [[structure of the Fixture|Fixture]]
* for each Timeline we get a Segmentation
** which in turn is a list of non-overlapping segments
*** each holding
**** an ExplicitPlacement for each covered object
**** an ExitNode for each ModelPort of the corresponding timeline
!Storage considerations
;(1) build process
:&amp;rarr; a tree walk yields the placements per timeline, which then get //resolved//
:&amp;rarr; after //sorting,// the segmentation can be established, thereby copying placements spanning multiple segments
:&amp;rarr; only //after running the complete build process for each segment,// the list of model ports and exit nodes can be established
;(2) commit stage
: -- after the build process(es) are completed, the new fixture gets ''committed'', thus becoming the officially valid state to be rendered. As render processes might be going on in parallel, some kind of locking or barrier is required. It seems advisable to make the change into a single atomic hot-swap. Meaning we'd get a single access point to be protected. But there is another twist: We need to find out which render processes to cancel an restart, to pick up the changes introduced by this build process, which might include adding and deleting of timelines as a whole, and any changes to the segmentation grid. Because of the highly dynamic nature of the placements, on the other hand it isn't viable to expect the high-level model to provide this information. Thus we need to find out about a ''change coverage'' at this point. We might expand on that idea to //prune any new segments which aren't changed.// This way, only a write barrier would be necessary on switching the actually changed segments, and any render processes touching these would be //tainted.// Old allocations could be released after all tainted processes are known to be terminated.
;(3) rendering use
:Each play/render process employs a ''frame dispatch step'' to get the right exit node for pulling a given frame. From there on, the process proceeds into the [[processing nodes|ProcNodes]], interleaved with backend/scheduler actions due to splitting into individually scheduled jobs. The storage of these processing nodes and accompanying wiring descriptors is hooked up behind the individual segments, by sharing a common {{{AllocationCluster}}}. Yet the calculation of individual frames also depends on ''parameters'' and especially ''automation'' connected with objects in the high-level model. It is likely that there might be some sharing, as the intention was to allow ''live changes'' to automated values. &lt;br/&gt;{{red{WIP 12/2010}}} details need to be worked out. &amp;rarr; [[parameter wiring concept|Wiring]]
!!!observations
* Storage and initialisation for explicit placements is an issue. We should strive at making that inline as much as possible.
* the overall segmentation emerges from a sorting of time points, which are start points of explicit placements
* after the segmentation has been built, the usage pattern changes entirely into a lookup of segment by time
* the individual segments act as umbrella for a lot of further objects hooked up behind.
* we need the ability to exchange or swap-in whole segments
* each segment controls an AllocationCluster
* we need to track processes for tainting
* access happens per ModelPort
!!!conclusions
The Fixture is mostly comprised of the Segementation datastructure, but some other facilities are involved too
# at top level, we need somehow to organise access per groups of model ports, actually grouped by timeline
# during the build process, there is a collection of placements; this can be discarded afterwards
# the backbone of the segmentation is closely linked to an ordering by time. Initially it should support sorting, access by time interval search later on.
# discarding a segment (or failing to do so) has an high impact on the whole application. We should employ a reliable mechanism for that.
# the frame dispatch and the tracking of processes can be combined; data duplication is a virtue when it comes to parallel processes
# the process of comparing and tainting is broken out into a separate data structure to be used just once
</pre>
</div>
<div title="Sequence" modifier="Ichthyostega" modified="201010180156" created="201001252327" tags="def" changecount="17">
<pre>A sequence is a collection of media objects, arranged onto a track tree. Sequences are the building blocks within the session. To be visible and editable, a session needs to be bound into a top-level [[Timeline]]. Alternatively, it may be used as a VirtualClip nested within another sequence.