WIP test covering implementation of the default object registry

(doesn't yet compile)
This commit is contained in:
Fischlurch 2008-04-01 06:57:00 +02:00
parent 6596699f6e
commit e9a364f7ad
5 changed files with 319 additions and 7 deletions

View file

@ -106,10 +106,14 @@ namespace asset
// now declare that these objects should be considered "default" // 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> (""))); // unrestricted default
ASSERT (Session::current->defaults.define (pipe1, Query<Pipe> ("stream("+sID+")"))); ASSERT (Session::current->defaults.define (pipe2, Query<Pipe> ("stream("+sID+")")));
ASSERT ( find (pipe1->getPipeID()), "failure declaring object as default"); ASSERT ( find (pipe1->getPipeID()), "failure declaring object as default");
ASSERT ( find (pipe2->getPipeID()), "failure declaring object as default"); ASSERT ( find (pipe2->getPipeID()), "failure declaring object as default");
ASSERT (sID != pipe1->getProcPatt()->queryStreamID(), "accidental clash");
ASSERT (!Session::current->defaults.define (pipe1, Query<Pipe> ("stream("+sID+")")));
// can't be registered with this query, due to failure caused by wrong stream-ID
} }
@ -143,7 +147,7 @@ namespace asset
// now de-register the pipe as "default Pipe" // now de-register the pipe as "default Pipe"
ASSERT (Session::current->defaults.forget (pipe)); ASSERT (Session::current->defaults.forget (pipe));
ASSERT (!find (pipe->getPipeID())); ASSERT (!find (pipe->getPipeID()));
ASSERT (cnt == pipe.use_count()); // indicates that DefaultsManager only holds a weak ref. ASSERT (cnt == pipe.use_count()); // indicates DefaultsManager holding only a weak ref.
} }
}; };

View file

@ -152,7 +152,7 @@ namespace asset
PPipe pipe1 = Session::current->defaults (query_for_pID); PPipe pipe1 = Session::current->defaults (query_for_pID);
hash = pipe1->getID(); hash = pipe1->getID();
} }
// now AssetManager should have the only ref // now AssetManager should hold the only ref
ID<Asset> assetID (hash); ID<Asset> assetID (hash);
AssetManager& aMang (AssetManager::instance()); AssetManager& aMang (AssetManager::instance());

View file

@ -0,0 +1,308 @@
/*
DefsRegistryImpl(Test) - verifying correct behaviour of the defaults registry
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.
* *****************************************************/
#include "common/test/run.hpp"
#include "common/util.hpp"
#include "proc/mobject/session/defsregistry.hpp"
#include "common/factory.hpp"
#include "common/query.hpp"
#include <boost/scoped_ptr.hpp>
#include <boost/format.hpp>
#include <cstdlib>
#include <map>
using lumiera::Query;
using std::tr1::shared_ptr;
using boost::scoped_ptr;
using boost::format;
using util::isnil;
using std::string;
using std::rand;
using std::map;
namespace mobject
{
namespace session
{
namespace test
{
const uint MAX_DEGREE_RAND = 9;
format typePatt ("Dummy<%2i>");
format instancePatt ("obj_%s_%i");
format predicatePatt ("%s_%2i( %s )");
const string garbage ("asdfghjklqwertzuiop");
/** create a random new ID */
string
newID (Symbol prefix)
{
return str (instancePatt % prefix % rand());
}
/** template for generating some different test types */
template<int I>
struct Dummy
{
static string name;
string instanceID;
Dummy () : instanceID (newID (name)) {}
};
template<int I>
string Dummy<I>::name = str (typePatt % I);
/** fabricating (random) query strings */
string
q_str (int degree=0)
{
string fake;
if (!degree)
degree = 1 + rand() % MAX_DEGREE_RAND;
while (0 < --degree)
fake += garbage_term() + ", ";
fake += garbage_term() + ".";
return fake;
}
string
garbage_term ()
{
return (predicatePatt
% char ('a'+ rand() % 26)
% rand() % 100
% garbage.substr(rand() % 17 , 3)
);
}
/************************************************************************
* @test build an registry table (just for this test) configured for
* some artificial test Types. Register some entries and verify
* the intended behaviour of the storage structure.
* @see DefsManagerImpl_test for checking the implementation details
* in the actual context used in Lumiera.
*/
class DefsRegistryImpl_test : public Test
{
scoped_ptr<DefsRegistry> reg_;
typedef shared_ptr<Dummy<13> > O;
typedef shared_ptr<Dummy<23> > P;
typedef Query<Dummy<13> > Q13;
typedef Query<Dummy<23> > Q23;
// fabricating Objects wrapped into smart-ptrs
lumiera::factory::RefcountPtr<O> oFac;
lumiera::factory::RefcountPtr<P> pFac;
O o1, o2, o3;
Q13 q1, q2, q3, q4, q5;
map<Q23, P> ps;
DefsRegistryImpl_test ()
: o1 (oFac()), o2 (oFac()), o3 (oFac()),
q1 (q_str (1)),
q2 (q_str (2)),
q3 (q_str (3)),
q4 (q_str (4)),
q5 (q_str (5))
{ }
virtual void run(Arg arg)
{
this->reg_.reset (new DefsRegistry);
fill_table ();
check_query ();
check_remove ();
}
void fill_table ()
{
// at start the registry is indeed empty
// thus a query doesnt yield any results....
ASSERT ( ! *(reg_->candidates(Q13 ("something"))) );
reg_->put (o1, q5);
reg_->put (o2, q4);
reg_->put (o3, q3);
reg_->put (o3, q2);
reg_->put (o2, q1);
reg_->put (o1, Q13); // the empty query
ps.clear();
for (int i=0; i<100; ++i)
{
P px (pFac());
Q23 qx (q_str());
ps[qx] = px;
reg_->put (px, qx);
px.instanceID = qx;
}
}
void check_query ()
{
DefsRegistry::Iter<Dummy<13> > i;
i = reg_->candidates(Q13 ("irrelevant query"));
ASSERT ( i.hasNext());
ASSERT ( *i++ == o1); // ordered according to the degree of the queries
ASSERT ( *i++ == o2);
ASSERT ( *i++ == o3);
ASSERT ( *i++ == o3);
ASSERT ( *i++ == o2);
ASSERT ( *i == o1);
ASSERT (!i.hasNext());
i = reg_->candidates(q3);
ASSERT ( *i++ == o3); // found by direct match
ASSERT ( *i++ == o1); // followed by the ordered ennumeration
ASSERT ( *i++ == o2);
ASSERT ( *i++ == o3);
ASSERT ( *i++ == o3);
ASSERT ( *i++ == o2);
ASSERT ( *i++ == o1);
ASSERT (!i.hasNext());
i = reg_->candidates(Q13);
ASSERT ( *i++ == o1); // found by direct match to the empty query
ASSERT ( *i++ == o1);
ASSERT ( *i++ == o2);
ASSERT ( *i++ == o3);
ASSERT ( *i++ == o3);
ASSERT ( *i++ == o2);
ASSERT ( *i++ == o1);
ASSERT (!i.hasNext());
uint d=0;
uint d_prev=0;
for (i = reg_->candidates(Q23 ("some crap"));
i.hasNext(); ++i )
{
ASSERT ( *i );
Q23 qx = (*i)->instanceID;
ASSERT ( ps[qx] == (*i));
d = lumiera::query::countPraed (qx);
ASSERT ( d_prev <= d );
d_prev = d;
}
ASSERT (!i.hasNext());
// calling with an arbitrary (registered) query
// yields the corresponding object at start of the ennumeration
i = reg_->candidates(ps.begin()->first);
ASSERT ( *i == ps.begin()->second);
}
void check_remove ()
{
reg_->forget (o2);
DefsRegistry::Iter<Dummy<13> > i;
i = reg_->candidates(q4);
ASSERT ( i.hasNext());
ASSERT ( *i++ == o1); // ordered according to the degree of the queries
// but the o2 entries are missing
ASSERT ( *i++ == o3);
ASSERT ( *i++ == o3);
// missing
ASSERT ( *i == o1);
ASSERT (!i.hasNext());
o3.reset(); // killing the only reference....
// expires the weak ref in the registry
i = reg_->candidates(Q13 ("something"));
ASSERT ( i.hasNext());
ASSERT ( *i++ == o1); // ordered according to the degree of the queries
// but now also the o3 entries are missing...
ASSERT ( *i == o1);
ASSERT (!i.hasNext());
ASSERT ( reg_->put (o1, q5)); // trying to register the same object at the same place
// doesn't change anything (but counts as "success")
i = reg_->candidates(q5);
ASSERT ( *i++ == o1); // direct match
ASSERT ( *i++ == o1);
ASSERT ( *i++ == o1);
ASSERT (!i.hasNext());
ASSERT ( reg_->put (o2, q5)); // trying to (re)register o2 with a existing query
// counts as failure (nothing chages)
i = reg_->candidates(q5);
ASSERT ( *i++ == o1); // direct match
ASSERT ( *i++ == o1);
ASSERT ( *i++ == o1);
ASSERT (!i.hasNext());
ASSERT ( reg_->put (o2, q2)); // trying to (re)register o2 with another query succeeds
i = reg_->candidates(q2);
ASSERT ( *i++ == o2); // direct match
ASSERT ( *i++ == o1);
ASSERT ( *i++ == o2); // inserted here in the dataset
ASSERT ( *i++ == o1);
ASSERT (!i.hasNext());
ASSERT ( reg_->forget (o1));
ASSERT ( reg_->forget (o1)); // failure, because it's already removed
ASSERT (!reg_->forget (o2));
o3.reset (oFac()); // another object is another object (it's irrelevant...)
i = reg_->candidates(q2);
ASSERT (! (*i)); // empty
}
};
/** Register this test class... */
LAUNCHER (DefsRegistryImpl_test, "function session");
} // namespace test
} // namespace mobject
} // namespace mobject

View file

@ -842,7 +842,7 @@ This is an very important external Interface, because it links together all thre
<pre>[[ProcLayer and Engine]] <pre>[[ProcLayer and Engine]]
</pre> </pre>
</div> </div>
<div title="DefaultsImplementation" modifier="Ichthyostega" modified="200802290000" created="200802200043" tags="spec impl draft" changecount="10"> <div title="DefaultsImplementation" modifier="Ichthyostega" modified="200804010137" created="200802200043" tags="spec impl draft" changecount="11">
<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. <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 !Skeleton
@ -858,7 +858,7 @@ This is an very important external Interface, because it links together all thre
#** otherwise, the newly created object is remembered (tagged) as new default, together with the degree of constriction #** otherwise, the newly created object is remembered (tagged) as new default, together with the degree of constriction
!!!Implementation details !!!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; 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 (sorted) set (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; just ignoring this issue seems reasonable. {{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 !!!Problems with the (preliminary) mock implementation