test-driven-brainstorming: DefsManager interface building blocks

This commit is contained in:
Fischlurch 2008-02-29 04:27:24 +01:00
parent 919a57ed9a
commit 4af2d47995
5 changed files with 258 additions and 15 deletions

View file

@ -44,6 +44,7 @@ namespace mobject
{
namespace session
{
using std::tr1::shared_ptr;
/** initialize the most basic internal defaults. */
DefsManager::DefsManager () throw()
@ -51,11 +52,40 @@ namespace mobject
TODO ("setup basic defaults of the session");
}
template<class TAR>
shared_ptr<TAR>
DefsManager::search (const Query<TAR>& capabilities)
{
UNIMPLEMENTED ("search for default registered object, dont create");
}
template<class TAR>
shared_ptr<TAR>
DefsManager::create (const Query<TAR>& capabilities)
{
UNIMPLEMENTED ("retrieve object and register as default");
}
template<class TAR>
bool
DefsManager::define (shared_ptr<TAR>& defaultObj, const Query<TAR>& capabilities)
{
UNIMPLEMENTED ("just do the defaults registration");
}
template<class TAR>
bool
DefsManager::forget (shared_ptr<TAR>& defaultObj)
{
UNIMPLEMENTED ("purge defaults registration");
}
template<class TAR>
shared_ptr<TAR>
DefsManager::operator() (const Query<TAR>& capabilities)
{
TODO ("move this code to create()");
QueryHandler<TAR>& typeHandler = ConfigRules::instance();
shared_ptr<TAR> res = typeHandler.resolve (capabilities);

View file

@ -56,9 +56,46 @@ namespace mobject
friend class SessManagerImpl;
public:
/** common access point: retrieve the default object fulfilling
* some given conditions. May silently trigger object creation.
* @throw error::Config in case no solution is possible, which
* is considered \e misconfiguration.
*/
template<class TAR>
shared_ptr<TAR> operator() (const cinelerra::Query<TAR>&);
/** search through the registered defaults, never create anything.
* @return object fulfilling the query, \c empty ptr if not found.
*/
template<class TAR>
shared_ptr<TAR> search (const cinelerra::Query<TAR>&);
/** retrieve an object fulfilling the query and register it as default.
* The resolution is delegated to the ConfigQuery system (which may cause
* creation of new object instances)
* @return object fulfilling the query, \c empty ptr if no solution.
*/
template<class TAR>
shared_ptr<TAR> create (const cinelerra::Query<TAR>&);
/** register the given object as default, after ensuring it fulfills the
* query. The latter may cause some properties of the object to be set,
* trigger creation of additional objects, and may fail altogether.
* @return true if query was successfull and object is registered as default
* @note only a weak ref to the object is stored
*/
template<class TAR>
bool define (shared_ptr<TAR>&, const cinelerra::Query<TAR>&);
/** remove the defaults registration of the given object, if there was such
* @return false if nothing has been changed because the object wasn't registered
*/
template<class TAR>
bool forget (shared_ptr<TAR>&);
// Q: can we have something along the line of...?
//
// template
// < class TAR, ///< the target to query for
// template <class> class SMP ///< smart pointer class to wrap the result

View file

@ -0,0 +1,163 @@
/*
DefsManagerImpl(Test) - checking implementation details of the defaults manager
Copyright (C) CinelerraCV
2007, 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 "common/test/run.hpp"
#include "common/util.hpp"
#include "proc/asset.hpp"
#include "proc/asset/pipe.hpp"
#include "common/configrules.hpp"
#include "proc/assetmanager.hpp"
#include "proc/mobject/session.hpp"
#include <boost/format.hpp>
#include <cstdlib>
using boost::format;
using util::isnil;
using std::string;
namespace asset
{
namespace test
{
using mobject::Session;
using cinelerra::Symbol;
using cinelerra::Query;
using cinelerra::query::normalizeID;
using cinelerra::ConfigRules;
using cinelerra::query::QueryHandler;
/** shortcut: query without creating new instances */
bool
find (Query<Pipe>& q)
{
return Session::current->defaults.search (q);
}
/** shortcut: query for given Pipe-ID */
bool
find (const string& pID)
{
return Session::current->defaults.search (Query<Pipe> ("pipe("+pID+")"));
}
format pattern ("dummy_%s_%i");
/** create a random new ID */
string
newID (Symbol prefix)
{
return str (pattern % prefix % std::rand());
}
/************************************************************************
* @test verify some general assumptions regarding implementation details
* of the the defaults manager.
* @see DefsManager_test for the "big picture"
*/
class DefsManagerImpl_test : public Test
{
virtual void run(Arg arg)
{
define_and_search();
string pipeID = create();
forget(pipeID);
}
void define_and_search ()
{
string sID = newID ("stream");
// create Pipes explicitly
// (without utilizing default queries)
PPipe pipe1 = Struct::create (newID("pipe"), newID("stream"));
PPipe pipe2 = Struct::create (newID("pipe"), sID );
ASSERT (pipe1 != pipe2);
ASSERT (sID == pipe2->getProcPatt()->queryStreamID());
ASSERT (!find (pipe1->getPipeID()), "accidental clash of random test-IDs");
ASSERT (!find (pipe2->getPipeID()), "accidental clash of random test-IDs");
// now declare that these objects should be considered "default"
ASSERT (Session::current->defaults.define (pipe1, Query<Pipe> (""))); // unrestricted default
ASSERT (Session::current->defaults.define (pipe1, Query<Pipe> ("stream("+sID+")")));
ASSERT ( find (pipe1->getPipeID()), "failure declaring object as default");
ASSERT ( find (pipe2->getPipeID()), "failure declaring object as default");
}
const string& create ()
{
string sID = newID ("stream");
Query<Pipe> query_for_streamID ("stream("+sID+")");
// issue a ConfigQuery directly, without involving the DefaultsManager
QueryHandler<Pipe>& typeHandler = ConfigRules::instance();
PPipe pipe1 = typeHandler.resolve (query_for_streamID);
ASSERT (pipe1);
ASSERT (!find (pipe1->getPipeID()));
PPipe pipe2 = Session::current->defaults.create (query_for_streamID);
ASSERT (pipe2);
ASSERT (pipe2 == pipe1);
ASSERT ( find (pipe1->getPipeID())); // now declared as "default Pipe" for this stream-ID
return pipe1->getPipeID();
}
void forget(string pID)
{
PPipe pipe = Pipe::query ("pipe("+pID+")");
REQUIRE (find (pipe->getPipeID()), "need an object registered as default");
long cnt = pipe.use_count();
// now de-register the pipe as "default Pipe"
ASSERT (Session::current->defaults.forget (pipe));
ASSERT (!find (pipe->getPipeID()));
ASSERT (cnt == pipe.use_count()); // indicates that DefaultsManager only holds a weak ref.
}
};
/** Register this test class... */
LAUNCHER (DefsManagerImpl_test, "function session");
} // namespace test
} // namespace asset

View file

@ -47,6 +47,17 @@ namespace asset
using cinelerra::query::normalizeID;
/** shortcut: run just a query
* without creating new instances
*/
bool
find (Query<Pipe>& q)
{
return Session::current->defaults.search (q);
}
/***********************************************************************
* @test basic behaviour of the defaults manager.
@ -76,7 +87,7 @@ namespace asset
void retrieveSimpleDefault(string pID)
void retrieveSimpleDefault (string pID)
{
PPipe pipe1 = Pipe::query (""); // "the default pipe"
PPipe pipe2;
@ -95,7 +106,7 @@ namespace asset
}
void retrieveConstrainedDefault(string pID, string sID)
void retrieveConstrainedDefault (string pID, string sID)
{
PPipe pipe1 = Pipe::query (""); // "the default pipe"
ASSERT (sID != pipe1->getProcPatt()->queryStreamID(),
@ -121,23 +132,24 @@ namespace asset
% pipe1->getPipeID()
% std::rand()
)); // make random new pipeID
string query_for_new ("pipe("+new_pID+")");
PPipe pipe2 = Session::current->defaults(Query<Pipe> (query_for_new));
Query<Pipe> query_for_new ("pipe("+new_pID+")");
TODO ("a way to do only a query, without creating. Use this to check it is really added as default");
ASSERT (!find (query_for_new)); // check it doesn't exist
PPipe pipe2 = Session::current->defaults (query_for_new); // triggers creation
ASSERT ( find (query_for_new)); // check it exists now
ASSERT (pipe1 != pipe2);
ASSERT (pipe2 == Pipe::query (query_for_new));
ASSERT (pipe2 == Session::current->defaults (query_for_new));
return new_pID;
}
void verifyRemoval(string pID)
void verifyRemoval (string pID)
{
string query_for_pID ("pipe("+pID+")");
Query<Pipe> query_for_pID ("pipe("+pID+")");
size_t hash;
{
PPipe pipe1 = Pipe::query (query_for_pID);
PPipe pipe1 = Session::current->defaults (query_for_pID);
hash = pipe1->getID();
}
// now AssetManager should have the only ref
@ -148,9 +160,10 @@ namespace asset
aMang.remove (assetID);
ASSERT (!aMang.known (assetID));
TODO ("assure the bare default-query now fails...");
PPipe pipe2 = Session::current->defaults(Query<Pipe> (query_for_pID));
TODO ("assure the bare default-query now succeeds...");
ASSERT (!find(query_for_pID)); // bare default-query should fail...
PPipe pipe2 = Session::current->defaults (query_for_pID); // triggers re-creation
ASSERT ( find(query_for_pID)); // should succeed again
}
};

View file

@ -842,8 +842,8 @@ This is an very important external Interface, because it links together all thre
<pre>[[ProcLayer and Engine]]
</pre>
</div>
<div title="DefaultsImplementation" modifier="Ichthyostega" modified="200802200145" created="200802200043" tags="spec impl draft" changecount="6">
<pre>As detailed in the [[definition|DefaultsManagement]], {{{default(Obj)}}} is sort of a Joker along the lines &quot;give me a suitable Object and I don't care for further details&quot;. Actually, default objects are implemented by the {{{mobject::session::DefsManager}}}, which remembers and keeps track of anything labeled as &quot;default&quot;. This defaults manager is a singleton and can be accessed via the [[Session]] interface, meaning that the memory regarding defaults is part of the session state. Accessing an object via the query for an default actually //tagges// this object (stores a weak ref in the ~DefsManager). Alongside with each object successfully queried via &quot;default&quot;, the degree of constriction is remembered, i.e. the number of additional conditions contained in the query. This enables us to search for default objects starting with the most unspecific.
<div title="DefaultsImplementation" modifier="Ichthyostega" modified="200802290000" created="200802200043" tags="spec impl draft" changecount="10">
<pre>As detailed in the [[definition|DefaultsManagement]], {{{default(Obj)}}} is sort of a Joker along the lines &quot;give me a suitable Object and I don't care for further details&quot;. Actually, default objects are implemented by the {{{mobject::session::DefsManager}}}, which remembers and keeps track of anything labeled as &quot;default&quot;. This defaults manager is a singleton and can be accessed via the [[Session]] interface, meaning that the memory track regarding defaults is part of the session state. Accessing an object via the query for an default actually //tagges// this object (storing a weak ref in the ~DefsManager). Alongside with each object successfully queried via &quot;default&quot;, the degree of constriction is remembered, i.e. the number of additional conditions contained in the query. This enables us to search for default objects starting with the most unspecific.
!Skeleton
# ''search'': using the predicate {{{default(X)}}} enumerates existing objects of suitable type
@ -859,7 +859,7 @@ This is an very important external Interface, because it links together all thre
!!!Implementation details
Taken precisely, the &quot;degree of constriction&quot; yields only a partial ordering &amp;mdash; but as the &quot;default&quot;-predicate is sort of a existential quantification anyways, its sole purpose is to avoid polluting the session with unnecessary default objects, and we don't need to care for absolute precision. A suitable approximation is to count the number of predicates terms in the query and use a pqueue (separate for each Type) to store weak refs to the the objects tagged as &quot;default&quot;
{{red{WARN}}} there is an interference with the (planned) Undo function. This is a general problem of the config queries; it seems reasonable we can ignore this issue.
{{red{WARN}}} there is an interference with the (planned) Undo function. This is a general problem of the config queries; just ignoring this issue seems reasonable.
!!!Problems with the (preliminary) mock implementation
As we don't have a Prolog interpreter on board yet, we utilize a mock store with preconfigured answers. (see {{{MockConfigQuery}}}). As this preliminary solution is lacking the ability to create new objects, we need to resort to some trickery here (please look away). The overall logic is quite broken, because the system isn't capable to do any real resolution &amp;mdash; if we ignore this fact, the rest of the algorithm can be implemented, tested and used right now.