From b7f67dc04857e8d303f94cbfa246a10bcfec4817 Mon Sep 17 00:00:00 2001 From: Andreas Stöckel Date: Fri, 9 Jan 2015 14:55:10 +0100 Subject: Added new unit tests for the Node.resolve function and fixed the behaviour for not following references if a compositum was found --- src/core/model/Node.cpp | 98 +++++++++++++----------- test/core/model/NodeTest.cpp | 173 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 215 insertions(+), 56 deletions(-) diff --git a/src/core/model/Node.cpp b/src/core/model/Node.cpp index d12e92b..930fbff 100644 --- a/src/core/model/Node.cpp +++ b/src/core/model/Node.cpp @@ -102,57 +102,52 @@ public: * resolving Node instances by name. */ class ResolutionState { -private: - /** - * Constructor of the ResolutionState class. - * - * @param shared is the shared, path independent state. - * @param resolutionRoot is the current resolution root node. - */ - ResolutionState(SharedResolutionState &shared, int idx, - Node *resolutionRoot) - : shared(shared), idx(idx), resolutionRoot(resolutionRoot) - { - } - public: - /** - * Constructor of the ResolutionState class. - * - * @param shared is the shared, path independent state. - * @param resolutionRoot is the current resolution root node. - */ - ResolutionState(SharedResolutionState &shared, - Node *resolutionRoot = nullptr, bool atStartNode = true) - : shared(shared), - idx(0), - resolutionRoot(resolutionRoot), - atStartNode(atStartNode) - { - } - /** * Reference at the resolution state that is shared between the various * resolution paths. */ SharedResolutionState &shared; + /** + * Current resolution root node or nullptr if no resolution root node has + * been set yet. + */ + Node *resolutionRoot; + /** * Current index within the given path. */ int idx; /** - * Current resolution root node or nullptr if no resolution root node has - * been set yet. + * Set to true if the resolution currently is in the subtree in which the + * node resolution process was started (no reference boundary has been + * passed yet). */ - Node *resolutionRoot; + bool inStartTree; /** - * Set to true if the resolution currently is at the node at which the - * resolution process was started. + * Set to true, once a compositum has been found. */ - bool atStartNode; + bool foundCompositum; + + /** + * Constructor of the ResolutionState class. + * + * @param shared is the shared, path independent state. + * @param resolutionRoot is the current resolution root node. + */ + ResolutionState(SharedResolutionState &shared, + Node *resolutionRoot = nullptr, int idx = 0, + bool inStartTree = true) + : shared(shared), + resolutionRoot(resolutionRoot), + idx(idx), + inStartTree(inStartTree), + foundCompositum(false) + { + } /** * Adds a node to the result. @@ -193,21 +188,31 @@ public: */ bool typeMatches(const RttiBase &type) { return type.isa(shared.type); } + bool canContainType(const RttiBase &type) + { + return type.composedOf(shared.type); + } + const std::string ¤tName() { return shared.path[idx]; } ResolutionState advance() { - return ResolutionState{shared, idx + 1, resolutionRoot}; + return ResolutionState{shared, resolutionRoot, idx + 1, false}; } ResolutionState fork(Node *newResolutionRoot) { - return ResolutionState{shared, newResolutionRoot, false}; + return ResolutionState{shared, newResolutionRoot, 0, false}; } - bool canFollowReferences() { return idx == 0 && atStartNode; } + bool canFollowReferences() + { + return idx == 0 && inStartTree && !foundCompositum; + } bool canFollowComposita() { return idx == 0; } + + size_t resultCount() { return shared.result.size(); } }; /* Class Node */ @@ -249,7 +254,8 @@ bool Node::resolve(ResolutionState &state) { // Try to mark this note as visited, do nothing if already has been visited if (state.markVisited(this)) { - std::cout << "visiting " << name << std::endl; + std::cout << "visiting " << name << " (" << state.idx << ")" + << std::endl; // Add this node to the result if it matches the current description if (state.atEndOfPath()) { @@ -259,7 +265,9 @@ bool Node::resolve(ResolutionState &state) return true; } } else { + size_t resCount = state.resultCount(); continueResolve(state); + return state.resultCount() > resCount; } } return false; @@ -275,7 +283,10 @@ bool Node::continueResolveIndex(const Index &index, ResolutionState &state) Rooted h = index.resolve(state.currentName()); if (h != nullptr) { ResolutionState advancedState = state.advance(); - return h->resolve(advancedState); + if (h->resolve(advancedState)) { + state.foundCompositum = true; + return true; + } } return false; } @@ -287,6 +298,7 @@ bool Node::continueResolveCompositum(Handle h, ResolutionState &state) if (h->getName() == state.currentName()) { ResolutionState advancedState = state.advance(); if (h->resolve(advancedState)) { + state.foundCompositum = true; return true; } } @@ -302,8 +314,10 @@ bool Node::continueResolveCompositum(Handle h, ResolutionState &state) bool Node::continueResolveReference(Handle h, ResolutionState &state) { // We can only follow references if we currently are at the beginning of the - // path and this node is the root node - if (canFollowReferences(state)) { + // path and this node is the root node. Additionally only follow a reference + // if the node the reference points to is known to contain the type that is + // currently asked for in the resolution process + if (canFollowReferences(state) && state.canContainType(h->type())) { std::cout << "following reference to " << h->getName() << std::endl; ResolutionState forkedState = state.fork(this); return continueResolveCompositum(h, forkedState); @@ -321,7 +335,7 @@ std::vector Node::resolve( // Kickstart the resolution process by treating this very node as compositum std::cout << "------------" << std::endl; std::cout << "resolving: "; - for (auto s: path) { + for (auto s : path) { std::cout << s << " "; } std::cout << " of type " << type.name << std::endl; diff --git a/test/core/model/NodeTest.cpp b/test/core/model/NodeTest.cpp index 334a311..880ce33 100644 --- a/test/core/model/NodeTest.cpp +++ b/test/core/model/NodeTest.cpp @@ -24,23 +24,35 @@ namespace ousia { class TestNode : public Node { -private: - std::vector> children; - protected: void continueResolve(ResolutionState &state) override { - continueResolveComposita(children, state); + continueResolveComposita(composita, composita.getIndex(), state); + continueResolveReferences(references, state); } public: - using Node::Node; + NodeVector composita; + NodeVector references; - Rooted addChild(Handle node) + TestNode(Manager &mgr, Handle parent = nullptr) + : Node(mgr, parent), composita(this), references(this) { - Owned nd = acquire(node); - children.push_back(nd); - return nd; + } + + TestNode(Manager &mgr, std::string name, Handle parent = nullptr) + : Node(mgr, name, parent), composita(this), references(this) + { + } + + Rooted addCompositum(Handle n) { + composita.push_back(n); + return n; + } + + Rooted addReference(Handle n) { + references.push_back(n); + return n; } }; @@ -51,7 +63,7 @@ const Rtti TestNode = TEST(Node, isRoot) { - Manager mgr; + Manager mgr{1}; Rooted n1{new TestNode(mgr)}; Rooted n2{new TestNode(mgr)}; Rooted n3{new TestNode(mgr, n2)}; @@ -65,12 +77,37 @@ TEST(Node, isRoot) ASSERT_FALSE(n3->isRoot()); } -TEST(Node, simpleResolve) +TEST(Node, resolveCompositaSimple) +{ + Manager mgr{1}; + Rooted root{new TestNode(mgr, "root")}; + Rooted child1 = root->addCompositum(new TestNode(mgr, "child1")); + Rooted child11 = child1->addCompositum(new TestNode(mgr, "child11")); + + std::vector res; + res = root->resolve(std::vector{"root", "child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child11 == res[0].node); + + res = root->resolve(std::vector{"child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child11 == res[0].node); + + res = + root->resolve(std::vector{"child11"}, RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child11 == res[0].node); +} + +TEST(Node, resolveCompositaDouble) { - Manager mgr; + Manager mgr{1}; Rooted root{new TestNode(mgr, "root")}; - Rooted child1 = root->addChild(new TestNode(mgr, "child1")); - Rooted child11 = child1->addChild(new TestNode(mgr, "child11")); + Rooted root2 = root->addCompositum(new TestNode(mgr, "root")); + Rooted child1 = root2->addCompositum(new TestNode(mgr, "child1")); + Rooted child11 = child1->addCompositum(new TestNode(mgr, "child11")); std::vector res; res = root->resolve(std::vector{"root", "child1", "child11"}, @@ -89,4 +126,112 @@ TEST(Node, simpleResolve) ASSERT_TRUE(child11 == res[0].node); } +TEST(Node, resolveAmbigousComposita) +{ + Manager mgr{1}; + Rooted root{new TestNode(mgr, "root")}; + Rooted a = root->addCompositum(new TestNode(mgr, "a")); + Rooted b = root->addCompositum(new TestNode(mgr, "b")); + Rooted child1 = a->addCompositum(new TestNode(mgr, "child1")); + Rooted child11 = child1->addCompositum(new TestNode(mgr, "child11")); + Rooted child12 = b->addCompositum(new TestNode(mgr, "child1")); + Rooted child112 = child12->addCompositum(new TestNode(mgr, "child11")); + + std::vector res; + res = root->resolve(std::vector{"child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); + + res = + root->resolve(std::vector{"child11"}, RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); +} + +TEST(Node, resolveReferences) +{ + Manager mgr{1}; + Rooted root{new TestNode(mgr, "root")}; + Rooted a = root->addReference(new TestNode(mgr, "a")); + Rooted b = root->addReference(new TestNode(mgr, "b")); + Rooted child1 = a->addCompositum(new TestNode(mgr, "child1")); + Rooted child11 = child1->addCompositum(new TestNode(mgr, "child11")); + Rooted child12 = b->addCompositum(new TestNode(mgr, "child1")); + Rooted child112 = child12->addCompositum(new TestNode(mgr, "child11")); + + std::vector res; + res = root->resolve(std::vector{"a", "child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child11 == res[0].node); + + res = root->resolve(std::vector{"b", "child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child112 == res[0].node); + + res = root->resolve(std::vector{"child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); + + res = + root->resolve(std::vector{"child11"}, RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); + + res = + root->resolve(std::vector{"child1"}, RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child1 == res[0].node || child1 == res[1].node); + ASSERT_TRUE(child12 == res[0].node || child12 == res[1].node); +} + +TEST(Node, resolveReferencesAndComposita) +{ + Manager mgr{1}; + Rooted root{new TestNode(mgr, "root")}; + Rooted a = root->addReference(new TestNode(mgr, "a")); + Rooted b = root->addReference(new TestNode(mgr, "b")); + Rooted child1 = a->addCompositum(new TestNode(mgr, "child1")); + Rooted child11 = child1->addCompositum(new TestNode(mgr, "child11")); + Rooted child12 = b->addCompositum(new TestNode(mgr, "child1")); + Rooted child112 = child12->addCompositum(new TestNode(mgr, "child11")); + Rooted child13 = root->addCompositum(new TestNode(mgr, "child1")); + + std::vector res; + res = root->resolve(std::vector{"a", "child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child11 == res[0].node); + + res = root->resolve(std::vector{"b", "child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child112 == res[0].node); + + res = root->resolve(std::vector{"child1", "child11"}, + RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); + + res = + root->resolve(std::vector{"child11"}, RttiTypes::TestNode); + ASSERT_EQ(2U, res.size()); + ASSERT_TRUE(child11 == res[0].node || child11 == res[1].node); + ASSERT_TRUE(child112 == res[0].node || child112 == res[1].node); + + // Resolving for "child1" should not descend into the referenced nodes + res = + root->resolve(std::vector{"child1"}, RttiTypes::TestNode); + ASSERT_EQ(1U, res.size()); + ASSERT_TRUE(child13 == res[0].node); +} + } -- cgit v1.2.3