From 4af2d479956d27affc8ff74d63c464273f344fb0 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 29 Feb 2008 04:27:24 +0100 Subject: [PATCH] test-driven-brainstorming: DefsManager interface building blocks --- src/proc/mobject/session/defsmanager.cpp | 30 ++++ src/proc/mobject/session/defsmanager.hpp | 37 ++++ .../mobject/session/defsmanagerimpltest.cpp | 163 ++++++++++++++++++ .../proc/mobject/session/defsmanagertest.cpp | 37 ++-- wiki/renderengine.html | 6 +- 5 files changed, 258 insertions(+), 15 deletions(-) create mode 100644 tests/components/proc/mobject/session/defsmanagerimpltest.cpp diff --git a/src/proc/mobject/session/defsmanager.cpp b/src/proc/mobject/session/defsmanager.cpp index bfb505efb..52b78437b 100644 --- a/src/proc/mobject/session/defsmanager.cpp +++ b/src/proc/mobject/session/defsmanager.cpp @@ -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 + shared_ptr + DefsManager::search (const Query& capabilities) + { + UNIMPLEMENTED ("search for default registered object, dont create"); + } + + template + shared_ptr + DefsManager::create (const Query& capabilities) + { + UNIMPLEMENTED ("retrieve object and register as default"); + } + + template + bool + DefsManager::define (shared_ptr& defaultObj, const Query& capabilities) + { + UNIMPLEMENTED ("just do the defaults registration"); + } + + template + bool + DefsManager::forget (shared_ptr& defaultObj) + { + UNIMPLEMENTED ("purge defaults registration"); + } + template shared_ptr DefsManager::operator() (const Query& capabilities) { + TODO ("move this code to create()"); QueryHandler& typeHandler = ConfigRules::instance(); shared_ptr res = typeHandler.resolve (capabilities); diff --git a/src/proc/mobject/session/defsmanager.hpp b/src/proc/mobject/session/defsmanager.hpp index b48411058..16c6a7bfc 100644 --- a/src/proc/mobject/session/defsmanager.hpp +++ b/src/proc/mobject/session/defsmanager.hpp @@ -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 shared_ptr operator() (const cinelerra::Query&); + /** search through the registered defaults, never create anything. + * @return object fulfilling the query, \c empty ptr if not found. + */ + template + shared_ptr search (const cinelerra::Query&); + + /** 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 + shared_ptr create (const cinelerra::Query&); + + /** 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 + bool define (shared_ptr&, const cinelerra::Query&); + + /** 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 + bool forget (shared_ptr&); + + +// Q: can we have something along the line of...? +// // template // < class TAR, ///< the target to query for // template class SMP ///< smart pointer class to wrap the result diff --git a/tests/components/proc/mobject/session/defsmanagerimpltest.cpp b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp new file mode 100644 index 000000000..920da9467 --- /dev/null +++ b/tests/components/proc/mobject/session/defsmanagerimpltest.cpp @@ -0,0 +1,163 @@ +/* + DefsManagerImpl(Test) - checking implementation details of the defaults manager + + Copyright (C) CinelerraCV + 2007, Hermann Vosseler + + 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 +#include + +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& 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("+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 (""))); // unrestricted default + ASSERT (Session::current->defaults.define (pipe1, Query ("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 query_for_streamID ("stream("+sID+")"); + + // issue a ConfigQuery directly, without involving the DefaultsManager + QueryHandler& 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 diff --git a/tests/components/proc/mobject/session/defsmanagertest.cpp b/tests/components/proc/mobject/session/defsmanagertest.cpp index c43f00583..16c09336a 100644 --- a/tests/components/proc/mobject/session/defsmanagertest.cpp +++ b/tests/components/proc/mobject/session/defsmanagertest.cpp @@ -47,6 +47,17 @@ namespace asset using cinelerra::query::normalizeID; + /** shortcut: run just a query + * without creating new instances + */ + bool + find (Query& 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 (query_for_new)); + Query 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 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 (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 } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 35ed4e339..0f3427f05 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -842,8 +842,8 @@ This is an very important external Interface, because it links together all thre
[[ProcLayer and Engine]]
 
-
-
As detailed in the [[definition|DefaultsManagement]], {{{default(Obj)}}} is sort of a Joker along the lines "give me a suitable Object and I don't care for further details". Actually, default objects are implemented by the {{{mobject::session::DefsManager}}}, which remembers and keeps track of anything labeled as "default". 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 "default", 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.
+
+
As detailed in the [[definition|DefaultsManagement]], {{{default(Obj)}}} is sort of a Joker along the lines "give me a suitable Object and I don't care for further details". Actually, default objects are implemented by the {{{mobject::session::DefsManager}}}, which remembers and keeps track of anything labeled as "default". 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 "default", 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 "degree of constriction" yields only a partial ordering &mdash; but as the "default"-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 "default"
-{{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 &mdash; if we ignore this fact, the rest of the algorithm can be implemented, tested and used right now.