OutputMapping unit test pass (closes #651 and #716)

This commit is contained in:
Fischlurch 2010-11-28 05:18:57 +01:00
parent c7794e7cbf
commit 828206e6b1
5 changed files with 160 additions and 50 deletions

View file

@ -56,6 +56,7 @@
#include "lib/util.hpp"
#include <boost/format.hpp>
#include <cstdlib>
using boost::format;
@ -80,7 +81,13 @@ namespace asset {
namespace {
Symbol genericIdSymbol ("id");
Symbol seqNrPredicate ("ord");
inline uint
asNumber (string const& spec)
{
return abs(std::atoi (spec.c_str()));
} // returns 0 in case of unparseable number
}
@ -116,6 +123,11 @@ namespace asset {
}
ENSURE (!isnil (nameID));
// does the query actually demand the Nth instance/element?
string seqID = extractID (seqNrPredicate, query);
if (!isnil (seqID) && 1 < asNumber(seqID))
nameID += "."+seqID;
Category cat (STRUCT, StructTraits<STRU>::catFolder());
return Asset::Ident (nameID, cat );
}

View file

@ -38,6 +38,7 @@
#include "lib/error.hpp"
#include "lib/symbol.hpp"
#include "proc/mobject/mobject.hpp"
#include "proc/mobject/placement-ref.hpp"
#include "proc/mobject/output-designation.hpp"
@ -45,9 +46,13 @@
#include "common/configrules.hpp"
#include <boost/functional/hash.hpp>
#include <cstdlib>
using lumiera::query::QueryHandler;
using lumiera::query::removeTerm;
using lumiera::query::extractID;
using lumiera::ConfigRules;
using lumiera::Symbol;
namespace mobject {
@ -167,6 +172,23 @@ namespace mobject {
HashVal resulting_targetPipeID (res? (HashVal)res->getID() : 0 );
return resulting_targetPipeID;
}
Symbol SEQNR_PREDICATE = "ord";
uint
is_defaults_query_with_channel (Query<asset::Pipe> const& query4pipe)
{
string seqNr = extractID (SEQNR_PREDICATE, query4pipe);
return abs(std::atoi (seqNr.c_str())); // also 0 in case of an invalid number
}
Query<asset::Pipe>
build_corresponding_sourceQuery (Query<asset::Pipe> const& query4pipe)
{
Query<asset::Pipe> srcQuery = query4pipe;
removeTerm (SEQNR_PREDICATE, srcQuery);
return srcQuery;
}
}

View file

@ -52,8 +52,9 @@
#ifndef PROC_MOBJECT_OUTPUT_MAPPING_H
#define PROC_MOBJECT_OUTPUT_MAPPING_H
#include "lib/error.hpp"
#include "proc/asset/pipe.hpp"
#include "lib/bool-checkable.hpp"
#include "lib/error.hpp"
#include "lib/query.hpp"
#include "lib/util.hpp"
@ -66,8 +67,6 @@ namespace mobject {
namespace { // Helper to extract and rebind definition types
using util::contains;
/**
* @internal used by OutputMapping
* to figure out the mapping target type
@ -120,8 +119,8 @@ namespace mobject {
* - a functor \c DEF::output usable as function pipe-ID --> Target
* - the concrete output-functor also defines the concrete Target type,
* which will be returned when accessing the OutputMapping
* - a function \c DEF::buildQuery(sourcePipeID) yielding a defaults querry,
* to be issued in case of accessing a non existant mapping
* - a function \c DEF::buildQuery(sourcePipeID,seqNr) yielding a (defaults)
* query to be issued in case of accessing a non existent mapping
*/
template<class DEF>
class OutputMapping
@ -234,7 +233,7 @@ namespace mobject {
operator== (Resolver const& a, Resolver const& b)
{
return a.pID_ == b.pID_;
} // note: Resolver depends on tempate parameter DEF
} // note: Resolver depends on template parameter DEF
// All instances of DEF are considered equivalent!
friend bool
operator== (Resolver const& rr, Target const& tval)
@ -249,7 +248,20 @@ namespace mobject {
Resolver operator[] (PId sourcePipeID);
Resolver operator[] (PPipe const& pipe);
Resolver operator[] (Query<asset::Pipe> const& query4pipe);
Resolver operator[] (Query<asset::Pipe> query4pipe);
bool
contains (PId mapping4sourcePipeID)
{
return util::contains (table_, mapping4sourcePipeID);
}
bool
contains (PPipe sourcePipe)
{
return !sourcePipe
|| this->contains (sourcePipe->getID());
}
@ -263,7 +275,7 @@ namespace mobject {
Resolver
buildResolutionWrapper (HashVal tableSlot)
{
ASSERT (contains (table_, tableSlot));
ASSERT (this->contains (tableSlot));
return Resolver (*this, table_[tableSlot]);
}
};
@ -280,14 +292,35 @@ namespace mobject {
/** delegate target pipe resolution to the rules system */
HashVal resolveQuery (Query<asset::Pipe> const&);
/** detect the special case, when actually the Nth
* solution of a defaults query is requested */
uint is_defaults_query_with_channel (Query<asset::Pipe> const&);
/** ..and build the corresponding defaults source query for this case */
Query<asset::Pipe> build_corresponding_sourceQuery (Query<asset::Pipe> const&);
}
/** standard map-style access to an OutputMapping.
* For the given source pipe-ID the mapped target pipe-ID is fetched
* and then handed over to the configured \c DEF::output functor, which
* is assumed to calculate or retrieve the actual result object.
*
* \par default mappings
* whenever accessing an yet non-existent mapping, a query is issued
* behind the scenes to establish a suitable default mapping. The actual
* query is built from a query template by the \c DEF::buildQuery function
* and thus can be configured for the concrete usage situation of the mapping.
* @warning depending on the actually configured defaults query, there might be
* no solution, in which case an \em unconnected marker is retrieved and
* stored. Thus the yielded Resolver should be checked, if in doubt.
*/
template<class DEF>
inline typename OutputMapping<DEF>::Resolver
OutputMapping<DEF>::operator[] (PId sourcePipeID)
{
if (!contains (table_, sourcePipeID))
if (!contains (sourcePipeID))
{
// issue a defaults query to resolve this mapping first
Query<asset::Pipe> query4pipe = DEF::buildQuery (sourcePipeID);
@ -297,6 +330,8 @@ namespace mobject {
}
/** similar to the standard map-style access, but accepts
* a source pipe object instead of just a pipe-ID */
template<class DEF>
inline typename OutputMapping<DEF>::Resolver
OutputMapping<DEF>::operator[] (PPipe const& pipe)
@ -311,7 +346,20 @@ namespace mobject {
* Accessing the OutputMapping this way by query enables all kinds of
* extended usages: It suffices that the given query somehow yields a Pipe,
* which then is considered the mapped result and handed over to the \c DEF::output
* functor for resolution to a result object to be returned.
* functor for resolution to a result object to be returned.
*
* \par Query for the Nth default instance
* OutputMapping provides a special behaviour for retrieving "the Nth default pipe".
* The rationale being the request for connection to the Nth bus of a given kind, like
* e.g. the 3rd audio subgroup or the 2nd video master. This special behaviour is triggered
* by the predicate "ord(##)" in the query. The \em remainder of the query is supposed to
* designate a \em default in this case, rather then querying directly for the result of
* the mapping. Thus this remainder of the query is used to retrieve a \em source pipe,
* which then is treated as if accessing a non-existent mapping: a suitable default
* solution for this mapping is retrieved, but in this special case, we append the
* given sequence number to the ID of the retrieved pipe, i.e. we get the Nth
* (identical) solution to the aforementioned query for a default pipe.
*
* @note the mapped result is remembered within this mapping. Further invocations
* with the \em same query will just fetch this stored pipe-ID and hand it
* to the functor, without resolving the query again. You might want to
@ -325,14 +373,29 @@ namespace mobject {
*/
template<class DEF>
inline typename OutputMapping<DEF>::Resolver
OutputMapping<DEF>::operator[] (Query<asset::Pipe> const& query4pipe)
OutputMapping<DEF>::operator[] (Query<asset::Pipe> query4pipe)
{
HashVal hash4query = _mapping::slot (query4pipe);
if (!contains (table_, hash4query))
// need to resolve this query first
table_[hash4query] = _mapping::resolveQuery (query4pipe);
if (!contains (hash4query))
{
if (uint seqNr = _mapping::is_defaults_query_with_channel (query4pipe))
{
// treat the special case
// when actually requesting the "Nth default of this kind"
PPipe corresponding_sourcePipe
= asset::Pipe::query(
_mapping::build_corresponding_sourceQuery (query4pipe));
ENSURE (corresponding_sourcePipe);
PId sourcePipeID = corresponding_sourcePipe->getID();
query4pipe = DEF::buildQuery (sourcePipeID, seqNr);
}
// need to resolve this query first
table_[hash4query] = _mapping::resolveQuery (query4pipe);
}
ENSURE (contains (table_, hash4query));
ENSURE (this->contains (hash4query));
return buildResolutionWrapper (hash4query);
}

View file

@ -15,3 +15,7 @@ PLANNED "BuildSegment_test" BuildSegment_test <<END
END
TEST "Output pipe mapping" OutputMapping_test <<END
return: 0
END

View file

@ -23,44 +23,40 @@
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
//#include "proc/asset/media.hpp"
//#include "proc/mobject/session.hpp"
//#include "proc/mobject/session/testclip.hpp"
#include "proc/asset/pipe.hpp"
//#include "proc/mobject/placement.hpp"
#include "proc/mobject/output-mapping.hpp"
#include "proc/asset/pipe.hpp"
#include "lib/util.hpp"
//#include "proc/mobject/session/mobjectfactory.hpp" ////TODO: avoidable?
//#include <boost/format.hpp>
//#include <iostream>
#include <boost/format.hpp>
#include <string>
//using boost::format;
//using lumiera::Time;
//using util::contains;
using boost::format;
using util::isnil;
using std::string;
//using std::cout;
namespace mobject {
namespace test {
//using asset::VIDEO;
using asset::Pipe;
using asset::PPipe;
typedef asset::ID<Pipe> PID;
namespace {
}
/*******************************************************************
* @test create a synthetic / example mapping to verify
* generic mapping behaviour
/*********************************************************************************
* @test create a synthetic / example mapping to verify generic mapping behaviour.
* We're creating a custom mapping type here, for this test only: The
* struct DummyDef provides a "definition context" for this custom mapping
* - there is a function to retrieve the actual target object
* for any target pipe stored in the mapping. In this case,
* we just extract the name-ID string from the pipe as result
* - as an additional sideeffect, this DummyDef::output functor
* also defines the Target type of this custom mapping to be std::string
* - DummyDef::buildQuery provides a template query, to be issued whenever
* a yet nonexistent mapping is requested. In this special case here
* we query for a pipe with the name "master_XXX", where XXX denotes
* the stream-type of the source pipe to be mapped.
*
* @see mobject::OutputDesignation
* @see mobject::session::Binding
@ -76,9 +72,11 @@ namespace test {
}
Query<Pipe>
buildQuery (PID sourcePipeID)
buildQuery (PID sourcePipeID, uint seqNr =0)
{
UNIMPLEMENTED ("fabricate defaults query for unit-test");
PPipe srcP = Pipe::lookup (sourcePipeID);
format queryPattern ("id(master_%1%), stream(%1%), ord(%2%)");
return Query<Pipe> (queryPattern % srcP->getStreamID() % seqNr);
}
};
@ -110,8 +108,14 @@ namespace test {
CHECK (map[p1].isValid());
CHECK (map[p1]);
CHECK (!map.contains (pX));
CHECK (!map.contains (p2));
// create an unconnected mapping
map[pX].disconnect();
CHECK (map.contains (pX));
CHECK (!map[pX].isValid());
CHECK (!map[pX]);
CHECK (!map[p2]);
}
@ -128,22 +132,22 @@ namespace test {
Mapping m2(m1);
CHECK (!isnil (m2));
CHECK (1 == m2.size());
CHECK (m1[p1] == "hairy");
CHECK (m2[p1] == "hairy");
CHECK (m1[pi] == "hairy");
CHECK (m2[pi] == "hairy");
m1[pi] = p2;
CHECK (m1[p1] == "furry");
CHECK (m2[p1] == "hairy");
CHECK (m1[pi] == "furry");
CHECK (m2[pi] == "hairy");
m2 = m1;
CHECK (m1[p1] == "furry");
CHECK (m2[p1] == "furry");
CHECK (m1[pi] == "furry");
CHECK (m2[pi] == "furry");
m1.clear();
CHECK (isnil(m1));
CHECK (!isnil(m2));
CHECK (m2[p1] == "furry");
CHECK (!m1[p1].isValid());
CHECK (m2[pi] == "furry");
CHECK (!m1.contains (pi));
}
@ -156,17 +160,22 @@ namespace test {
PPipe p1 = Pipe::query("stream(hairy)");
PPipe p2 = Pipe::query("stream(furry)");
CHECK (map[p1] == "master(hairy)");
CHECK (map[p2] == "master(furry)");
CHECK (map[p1] == "master_hairy");
CHECK (map[p2] == "master_furry");
// create new mapping to an explicitly queried target
Query<Pipe> some_pipe ("pipe(super_curly)");
CHECK (map[some_pipe] == "super_curly");
// create a new mapping to the 2nd master for "furry" data
Query<Pipe> special_bus ("stream(furry), ord(2)");
CHECK (map[special_bus] == "master.2(furry)");
CHECK (map[special_bus] == "master_furry.2");
}
};
/** Register this test class... */
LAUNCHER (OutputMapping_test, "unit session");
LAUNCHER (OutputMapping_test, "unit session builder");