LUMIERA.clone/tests/core/proc/mobject/session/scope-path-test.cpp
Ichthyostega c2ea15695e amend harmless PlacementIndex test failures. Test Suite PASS
c++11 uses another hashtable implementation.
This uncovered some poorly written tests, which relied on
objects being returned in a specific order. As far as poissible,
we're using generic query functions now to get our test objects.

But these tests still rely on a specifically crafted test index content,
which as such is acceptable IMHO. The only remaining problem is
that we check the order of generated output in some tests, and this
order is still implementation dependent.
2014-05-11 02:08:53 +02:00

407 lines
14 KiB
C++

/*
ScopePath(Test) - verify handling of logical access path down from Session root
Copyright (C) Lumiera.org
2009, 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/session/test-scopes.hpp"
#include "proc/mobject/session/placement-index.hpp"
#include "proc/mobject/session/scope-path.hpp"
#include "proc/mobject/session/test-scope-invalid.hpp"
#include "lib/util.hpp"
#include <iostream>
#include <string>
namespace proc {
namespace mobject {
namespace session {
namespace test {
using std::cout;
using std::endl;
using std::string;
using util::isnil;
using util::isSameObject;
using lumiera::error::LUMIERA_ERROR_LOGIC;
using lumiera::error::LUMIERA_ERROR_INVALID;
/***********************************************************************//**
* @test properties and behaviour of the path of nested scopes.
* Using a pseudo-session (actually just a PlacementIndex),
* this test creates some nested scopes, builds scope paths
* and executes various comparisons navigation moves on them.
* Especially detection of invalid scopes and paths and the
* special handling of empty and root paths is covered.
* @see mobject::Placement
* @see mobject::session::ScopePath
* @see mobject::session::QueryFocus
*/
class ScopePath_test : public Test
{
virtual void
run (Arg)
{
// Prepare an (test)Index backing the PlacementRefs
PPIdx index = build_testScopes();
PMO& startPlacement = retrieve_startElm();
CHECK (startPlacement.isValid());
checkInvalidScopeDetection();
ScopePath testPath = buildPath (startPlacement);
checkRelations (testPath,startPlacement);
invalidPath (testPath,startPlacement);
rootPath (testPath);
check_Identity_and_Copy (startPlacement);
check_RefcountProtection (startPlacement);
navigate (testPath, index);
clear (testPath, index);
}
ScopePath
buildPath (PMO& startPla)
{
Scope startScope (startPla);
ScopePath path (startScope);
ScopePath path2 (startScope);
ScopePath path3 (path2);
CHECK (path);
CHECK (path.contains (startScope));
CHECK ( path.getLeaf() == path2.getLeaf());
CHECK (path2.getLeaf() == path3.getLeaf());
return path;
}
void
checkInvalidScopeDetection()
{
// verify detection of illegal scopes and paths...
TestPlacement<> notRelated2anything (*new DummyMO);
VERIFY_ERROR (NOT_IN_SESSION, Scope invalid (notRelated2anything) );
Scope const& scopeOfEvil = fabricate_invalidScope();
CHECK (!scopeOfEvil.isValid());
VERIFY_ERROR (INVALID_SCOPE, ScopePath outsideCurrentModel (scopeOfEvil) );
// but there is one exception to this rule...
ScopePath theInvalidToken (Scope::INVALID);
CHECK (!theInvalidToken.isValid());
CHECK (theInvalidToken.empty());
}
void
checkIteration (ScopePath path, PMO& refPlacement)
{
Scope refScope(refPlacement);
ScopePath::iterator ii = path.begin();
CHECK (ii);
while (++ii)
{
CHECK (*ii == refScope.getParent());
refScope = *ii;
}
}
void
checkRelations (ScopePath path1, PMO& refPlacement)
{
CHECK (path1.contains (refPlacement));
Scope refScope (refPlacement);
CHECK (path1.contains (refScope));
CHECK (path1.endsAt (refScope));
ScopePath path2 (refScope);
CHECK (path2.contains (refScope));
CHECK (path2.endsAt (refScope));
CHECK (path1 == path2);
CHECK (!isSameObject (path1,path2));
Scope parent = path2.moveUp();
CHECK (path2.endsAt (parent));
CHECK (path1.endsAt (refScope));
CHECK (parent == refScope.getParent());
CHECK (path1 != path2);
CHECK (path2 != path1);
CHECK (path1.contains (path2));
CHECK (!disjoint(path1,path2));
CHECK (path2 == commonPrefix(path1,path2));
CHECK (path2 == commonPrefix(path2,path1));
CHECK (path1 != commonPrefix(path1,path2));
CHECK (path1 != commonPrefix(path2,path1));
}
void
rootPath (ScopePath refPath)
{
CHECK ( refPath);
refPath.goRoot();
CHECK (!refPath);
CHECK (!refPath.empty());
CHECK (!refPath.isValid());
CHECK (1 == refPath.length());
ScopePath defaultPath;
CHECK (!defaultPath);
CHECK (refPath == defaultPath);
}
void
invalidPath (ScopePath refPath, PMO& refPlacement)
{
CHECK (refPath);
CHECK (!ScopePath::INVALID);
CHECK (isnil (ScopePath::INVALID));
CHECK ("!" == string(ScopePath::INVALID));
ScopePath invalidP (ScopePath::INVALID);
CHECK (isnil (invalidP));
CHECK (invalidP == ScopePath::INVALID);
CHECK (!isSameObject (invalidP, ScopePath::INVALID));
CHECK (refPath.contains (refPlacement));
CHECK (!invalidP.contains (refPlacement));
Scope refScope (refPlacement);
CHECK (!invalidP.contains (refScope));
VERIFY_ERROR (EMPTY_SCOPE_PATH, invalidP.endsAt (refScope) ); // Logic: can't inspect the end of nothing
CHECK (refPath.contains (invalidP)); // If the moon is made of green cheese, I'll eat my hat!
CHECK (!invalidP.contains (refPath));
CHECK (invalidP == commonPrefix(refPath,invalidP));
CHECK (invalidP == commonPrefix(invalidP,refPath));
VERIFY_ERROR (EMPTY_SCOPE_PATH, invalidP.moveUp() );
Scope root = refPath.goRoot();
CHECK (1 == refPath.length());
Scope const& nil = refPath.moveUp();
CHECK (refPath.empty());
CHECK (!nil.isValid());
CHECK (refPath == invalidP);
CHECK (invalidP.contains (nil));
CHECK (invalidP.contains (refPath));
CHECK (!invalidP.contains (refScope));
VERIFY_ERROR (EMPTY_SCOPE_PATH, refPath.navigate(root) );
//ScopePath::INVALID.navigate(root); // doesn't compile: INVALID is immutable
}
void
check_Identity_and_Copy (PMO& refPlacement)
{
Scope startScope (refPlacement);
ScopePath path1 (startScope);
ScopePath path2 (startScope);
ScopePath path3 (path2);
CHECK (path1.contains (startScope));
CHECK (path2.contains (startScope));
CHECK (path3.contains (startScope));
CHECK (path1 == path2);
CHECK (path2 == path3);
CHECK (path1 == path3);
CHECK (!isSameObject (path1,path2));
CHECK (!isSameObject (path2,path3));
CHECK (!isSameObject (path1,path3));
Scope parent = path3.moveUp();
CHECK (path1 == path2);
CHECK (path2 != path3);
CHECK (path1 != path3);
path2 = path3;
CHECK (path1 != path2);
CHECK (path2 == path3);
CHECK (path1 != path3);
path2 = ScopePath::INVALID;
CHECK (path1 != path2);
CHECK (path2 != path3);
CHECK (path1 != path3);
}
/** @test the embedded refcount is handled sensibly
* when it comes to copying. (This refcount
* is used by QueryFocusStack) */
void
check_RefcountProtection (PMO& refPlacement)
{
Scope startScope (refPlacement);
ScopePath path1 (startScope);
ScopePath path2 (path1);
path1 = path2;
CHECK (!isSameObject (path1,path2));
CHECK (0 == path1.ref_count());
CHECK (0 == path2.ref_count());
intrusive_ptr_add_ref (&path2);
CHECK (0 == path1.ref_count());
CHECK (0 < path2.ref_count());
ScopePath path3 (path2);
CHECK (0 == path3.ref_count()); // refcount not copied
path3.moveUp();
VERIFY_ERROR (LOGIC, path2 = path3 ); // overwriting of path with refcount is prohibited
CHECK (path1 != path3);
path1 = path2; // but path without refcount may be overwritten
path1 = path3;
CHECK (path1 == path3);
intrusive_ptr_release (&path2); // refcount drops to zero...
CHECK (0 == path1.ref_count());
CHECK (0 == path2.ref_count());
}
/** @test modify a path by \em navigating it.
* - move one step above the current leaf
* - move up to the root element
* - move back to the parent and verify we're just above the leaf
* - attach a new sibling node and move the path down to there
* - extract the common prefix, which should again point to the parent
* - find a placement in a completely separate branch (only sharing the
* root node). Navigate to there and verify root is the common prefix.
*/
void
navigate (const ScopePath refPath, PPIdx index)
{
#define __SHOWPATH(N) cout << "Step("<<N<<"): "<< string(path) << endl;
ScopePath path (refPath); __SHOWPATH(1)
CHECK (path == refPath);
Scope leaf = path.getLeaf();
Scope parent = path.moveUp(); __SHOWPATH(2)
CHECK (path != refPath);
CHECK (refPath.contains (path));
CHECK (refPath.endsAt (leaf));
CHECK (path.endsAt (parent));
CHECK (parent == leaf.getParent());
CHECK (parent == path.getLeaf());
Scope root = path.goRoot(); __SHOWPATH(3)
CHECK (path != refPath);
CHECK (path.endsAt (root));
CHECK (refPath.contains (path));
CHECK (!path.endsAt (parent));
CHECK (!path.endsAt (leaf));
path.navigate (parent); __SHOWPATH(4)
CHECK (path.endsAt (parent));
CHECK (!path.endsAt (root));
CHECK (!path.endsAt (leaf));
TestPlacement<> newNode (*new DummyMO);
PMO& parentRefPoint = parent.getTop();
Scope newLocation =
index->find( // place newNode as sibling of "leaf"
index->insert (newNode, parentRefPoint));
path.navigate (newLocation); __SHOWPATH(5)
Scope sibling = path.getLeaf();
CHECK (sibling == newLocation);
CHECK (parent == sibling.getParent());
CHECK (path.endsAt (sibling));
CHECK (path.contains (parent));
CHECK (path.contains (root));
CHECK (!refPath.contains (path));
CHECK (!path.contains (refPath));
CHECK (!disjoint (path,refPath));
CHECK (!disjoint (refPath,path));
ScopePath prefix = commonPrefix (path,refPath);
CHECK (prefix == commonPrefix (refPath,path));
CHECK (prefix.endsAt (parent));
CHECK (!prefix.contains (leaf));
CHECK (!prefix.contains (sibling));
path.navigate (prefix.getLeaf()); __SHOWPATH(6)
CHECK (path == prefix);
// try to navigate to an unconnected location...
ScopePath beforeInvalidNavigation = path;
Scope const& unrelatedScope (fabricate_invalidScope());
VERIFY_ERROR (INVALID_SCOPE, path.navigate (unrelatedScope) );
CHECK (path == beforeInvalidNavigation); // not messed up by the incident
// now explore a completely separate branch....
PMO& separatePlacement = *explore_testScope (
*explore_testScope (
retrieve_firstTestSubMO21()));
path.navigate (separatePlacement);
CHECK (path);
CHECK (disjoint (path,refPath));
CHECK (path.contains(separatePlacement));
Scope other = path.getLeaf();
CHECK (isSameObject (other.getTop(), separatePlacement));
ScopePath rootPrefix = commonPrefix (path,refPath);
CHECK (rootPrefix.endsAt (root));
}
void
clear (ScopePath& path, PPIdx index)
{
CHECK (path);
PMO& rootNode = index->getRoot();
CHECK (path.getLeaf() != rootNode);
path.clear();
CHECK (!path);
CHECK (!isnil (path));
CHECK (path.getLeaf() == rootNode);
}
};
/** Register this test class... */
LAUNCHER (ScopePath_test, "unit session");
}}}} // namespace proc::mobject::session::test