/** * @file lltreeiterators.cpp * @author Nat Goodspeed * @date 2008-08-20 * @brief Test of lltreeiterators.h * * $LicenseInfo:firstyear=2008&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ // Precompiled header #include "linden_common.h" // STL headers // std headers #include #include #include // external library headers #include #include #include // associated header #include "../lltreeiterators.h" #include "../llpointer.h" #include "../test/lltut.h" /***************************************************************************** * tut test group *****************************************************************************/ namespace tut { struct iter_data { }; typedef test_group iter_group; typedef iter_group::object iter_object; tut::iter_group ig("LLTreeIterators"); } // namespace tut /***************************************************************************** * boost::get_pointer() specialization for LLPointer<> *****************************************************************************/ // This specialization of boost::get_pointer() undoubtedly belongs in // llmemory.h. It's used by boost::bind() so that you can pass an // LLPointer as well as a Foo* to a functor such as // boost::bind(&Foo::method, _1). //namespace boost //{ template NODE* get_pointer(const LLPointer& ptr) { return ptr.get(); } //}; /***************************************************************************** * ScopeLabel *****************************************************************************/ class ScopeLabel { public: ScopeLabel(const std::string& label): mLabel(label) { std::cout << "Entering " << mLabel << '\n'; } ~ScopeLabel() { std::cout << "Leaving " << mLabel << '\n'; } private: std::string mLabel; }; /***************************************************************************** * Cleanup *****************************************************************************/ // Yes, we realize this is redundant with auto_ptr and LLPointer and all // kinds of better mechanisms. But in this particular source file, we need to // test nodes managed with plain old dumb pointers as well as nodes managed // with LLPointer, so we introduce this mechanism. // // In the general case, when we declare a Cleanup for some pointer, delete the // pointer when the Cleanup goes out of scope. template struct Cleanup { Cleanup(const PTRTYPE& ptr): mPtr(ptr) {} ~Cleanup() { delete mPtr; } PTRTYPE mPtr; }; // But when the pointer is an LLPointer, Cleanup is a no-op: // LLPointer will handle the cleanup automagically. template struct Cleanup< LLPointer > { Cleanup(const LLPointer& ptr) {} ~Cleanup() {} }; /***************************************************************************** * Expected *****************************************************************************/ // Expected is a base class used to capture the expected results -- a sequence // of string node names -- from one of our traversals of this example data. // Its subclasses initialize it with a pair of string iterators. It's not // strictly necessary to customize Expected to model Boost.Range, it's just // convenient. struct Expected { template Expected(ITER begin, ITER end): strings(begin, end) {} /*------ The following are to make Expected work with Boost.Range ------*/ typedef std::vector container_type; typedef container_type::iterator iterator; typedef container_type::const_iterator const_iterator; typedef container_type::size_type size_type; container_type strings; iterator begin() { return strings.begin(); } iterator end() { return strings.end(); } size_type size() { return strings.size(); } const_iterator begin() const { return strings.begin(); } const_iterator end() const { return strings.end(); } }; // We have a couple of generic Expected template subclasses. This list of // strings is used for the "else" case when all specializations fail. const char* bad_strings[] = { "FAIL" }; /***************************************************************************** * verify() *****************************************************************************/ // test function: given (an object modeling) a Boost.Range of tree nodes, // compare the sequence of visited node names with a range of expected name // strings. Report success both with std::cout output and a bool return. The // string desc parameter is to identify the different tests. template bool verify(const std::string& desc, NODERANGE noderange, STRINGRANGE expected) { typename boost::range_iterator::type nri = boost::begin(noderange), nrend = boost::end(noderange); typename boost::range_iterator::type sri = boost::begin(expected), srend = boost::end(expected); // We choose to loop over both sequences explicitly rather than using // std::equal() or std::lexicographical_compare(). The latter tells you // whether one sequence is *less* than the other -- it doesn't tell you // equality. std::equal() needs you to verify the sequence lengths ahead // of time. Anyway, comparing explicitly allows us to report much more // information about any sequence mismatch. for ( ; nri != nrend && sri != srend; ++nri, ++sri) { if ((*nri)->name() != *sri) { std::cout << desc << " mismatch: " << "expected " << *sri << ", got " << (*nri)->name() << "\n"; return false; } } if (nri != nrend) { std::cout << desc << " produced too many items:\n"; for ( ; nri != nrend; ++nri) { std::cout << " " << (*nri)->name() << '\n'; } return false; } if (sri != srend) { std::cout << desc << " produced too few items, omitting:\n"; for ( ; sri != srend; ++sri) { std::cout << " " << *sri << '\n'; } return false; } // std::cout << desc << " test passed\n"; return true; } /***************************************************************************** * PlainNode: LLLinkIter, non-refcounted *****************************************************************************/ class PlainNode { public: PlainNode(const std::string& name, PlainNode* next=NULL): mName(name), mNext(next) {} ~PlainNode() { delete mNext; } std::string name() const { return mName; } PlainNode* next() const { return mNext; } public: // if this were 'private', couldn't bind mNext PlainNode* mNext; private: std::string mName; }; namespace tut { template<> template<> void iter_object::test<1>() { // set_test_name("LLLinkedIter -- non-refcounted class"); PlainNode* last(new PlainNode("c")); PlainNode* second(new PlainNode("b", last)); PlainNode* first(new PlainNode("a", second)); Cleanup cleanup(first); static const char* cseq[] = { "a", "b", "c" }; Expected seq(boost::begin(cseq), boost::end(cseq)); std::string desc1("Iterate by public link member"); // std::cout << desc1 << ":\n"; // Try instantiating an iterator with NULL. This test is less about // "did we iterate once?" than "did we avoid blowing up?" for (LLLinkedIter pni(NULL, boost::bind(&PlainNode::mNext, _1)), end; pni != end; ++pni) { // std::cout << (*pni)->name() << '\n'; ensure("LLLinkedIter(NULL)", false); } ensure(desc1, verify(desc1, boost::make_iterator_range(LLLinkedIter(first, boost::bind(&PlainNode::mNext, _1)), LLLinkedIter()), seq)); std::string desc2("Iterate by next() method"); // std::cout << desc2 << ":\n"; // for (LLLinkedIter pni(first, boost::bind(&PlainNode::next, _1)); ! (pni == end); ++pni) // std::cout << (**pni).name() << '\n'; ensure(desc2, verify(desc2, boost::make_iterator_range(LLLinkedIter(first, boost::bind(&PlainNode::next, _1)), LLLinkedIter()), seq)); { // LLLinkedIter pni(first, boost::bind(&PlainNode::next, _1)); // std::cout << "First is " << (*pni++)->name() << '\n'; // std::cout << "Second is " << (*pni )->name() << '\n'; } { LLLinkedIter pni(first, boost::bind(&PlainNode::next, _1)); ensure_equals("first", (*pni++)->name(), "a"); ensure_equals("second", (*pni )->name(), "b"); } } } // tut /***************************************************************************** * RCNode: LLLinkIter, refcounted *****************************************************************************/ class RCNode; typedef LLPointer RCNodePtr; class RCNode: public LLRefCount { public: RCNode(const std::string& name, const RCNodePtr& next=RCNodePtr()): mName(name), mNext(next) { // std::cout << "New RCNode(" << mName << ")\n"; } RCNode(const RCNode& that): mName(that.mName), mNext(that.mNext) { // std::cout << "Copy RCNode(" << mName << ")\n"; } virtual ~RCNode(); std::string name() const { return mName; } RCNodePtr next() const { return mNext; } public: // if this were 'private', couldn't bind mNext RCNodePtr mNext; private: std::string mName; }; std::ostream& operator<<(std::ostream& out, const RCNode& node) { out << "RCNode(" << node.name() << ')'; return out; } // This string contains the node name of the last RCNode destroyed. We use it // to validate that LLLinkedIter in fact contains LLPointer, // and that therefore an outstanding LLLinkedIter to an instance of a // refcounted class suffices to keep that instance alive. std::string last_RCNode_destroyed; RCNode::~RCNode() { // std::cout << "Kill " << *this << "\n"; last_RCNode_destroyed = mName; } namespace tut { template<> template<> void iter_object::test<2>() { // set_test_name("LLLinkedIter -- refcounted class"); LLLinkedIter rcni, end2; { // ScopeLabel label("inner scope"); RCNodePtr head(new RCNode("x", new RCNode("y", new RCNode("z")))); // for (rcni = LLLinkedIter(head, boost::bind(&RCNode::mNext, _1)); rcni != end2; ++rcni) // std::cout << **rcni << '\n'; rcni = LLLinkedIter(head, boost::bind(&RCNode::next, _1)); } // std::cout << "Now the LLLinkedIter is the only remaining reference to RCNode chain\n"; ensure_equals(last_RCNode_destroyed, ""); ensure(rcni != end2); ensure_equals((*rcni)->name(), "x"); ++rcni; ensure_equals(last_RCNode_destroyed, "x"); ensure(rcni != end2); ensure_equals((*rcni)->name(), "y"); ++rcni; ensure_equals(last_RCNode_destroyed, "y"); ensure(rcni != end2); ensure_equals((*rcni)->name(), "z"); ++rcni; ensure_equals(last_RCNode_destroyed, "z"); ensure(rcni == end2); } } /***************************************************************************** * TreeNode *****************************************************************************/ class TreeNode; typedef LLPointer TreeNodePtr; /** * TreeNode represents a refcounted tree-node class that hasn't (yet) been * modified to incorporate LLTreeIter methods. This illustrates how you can * use tree iterators either standalone, or with free functions. */ class TreeNode: public LLRefCount { public: typedef std::vector list_type; typedef list_type::const_iterator child_iterator; // To avoid cycles, use a "weak" raw pointer for the parent link TreeNode(const std::string& name, TreeNode* parent=0): mParent(parent), mName(name) {} TreeNodePtr newChild(const std::string& name) { TreeNodePtr child(new TreeNode(name, this)); mChildren.push_back(child); return child; } std::string name() const { return mName; } TreeNodePtr getParent() const { return mParent; } child_iterator child_begin() const { return mChildren.begin(); } child_iterator child_end() const { return mChildren.end(); } private: std::string mName; // To avoid cycles, use a "weak" raw pointer for the parent link TreeNode* mParent; list_type mChildren; }; /** * This is an example of a helper function to facilitate iterating from a * TreeNode up to the root or down from the root (see LLTreeIter::RootIter). * * Example: * @code * BOOST_FOREACH(TreeNodePtr node, getRootRange(somenode)) * { * std::cout << node->name() << '\n'; * } * @endcode */ template boost::iterator_range< LLTreeRootIter > getRootRange(const TreeNodePtr& node) { typedef LLTreeRootIter iter_type; typedef boost::iterator_range range_type; return range_type(iter_type(node, boost::bind(&TreeNode::getParent, _1)), iter_type()); } /** * This is an example of a helper function to facilitate walking a given * TreeNode's subtree in any supported order (see LLTreeIter::WalkIter). * * Example: * @code * BOOST_FOREACH(TreeNodePtr node, getWalkRange(root)) * { * std::cout << node->name() << '\n'; * } * @endcode */ template boost::iterator_range< LLTreeWalkIter > getWalkRange(const TreeNodePtr& node) { typedef LLTreeWalkIter iter_type; typedef boost::iterator_range range_type; return range_type(iter_type(node, boost::bind(&TreeNode::child_begin, _1), boost::bind(&TreeNode::child_end, _1)), iter_type()); } /***************************************************************************** * EnhancedTreeNode *****************************************************************************/ class EnhancedTreeNode; typedef LLPointer EnhancedTreeNodePtr; /** * More typically, you enhance the tree-node class itself with template * methods like the above. This EnhancedTreeNode class illustrates the * technique. Normally, of course, you'd simply add these methods to TreeNode; * we put them in a separate class to preserve the undecorated TreeNode class * to illustrate (and test) the use of plain tree iterators and standalone * helper functions. * * We originally implemented EnhancedTreeNode as a subclass of TreeNode -- but * because TreeNode stores and manipulates TreeNodePtrs and TreeNode*s, * reusing its methods required so much ugly downcast logic that we gave up * and restated the whole class. Bear in mind that logically these aren't two * separate classes; logically they're two snapshots of the @em same class at * different moments in time. */ class EnhancedTreeNode: public LLRefCount { public: /*-------------- The following is restated from TreeNode ---------------*/ typedef std::vector list_type; typedef list_type::const_iterator child_iterator; // To avoid cycles, use a "weak" raw pointer for the parent link EnhancedTreeNode(const std::string& name, EnhancedTreeNode* parent=0): mParent(parent), mName(name) {} EnhancedTreeNodePtr newChild(const std::string& name) { EnhancedTreeNodePtr child(new EnhancedTreeNode(name, this)); mChildren.push_back(child); return child; } std::string name() const { return mName; } EnhancedTreeNodePtr getParent() const { return mParent; } child_iterator child_begin() const { return mChildren.begin(); } child_iterator child_end() const { return mChildren.end(); } private: std::string mName; // To avoid cycles, use a "weak" raw pointer for the parent link EnhancedTreeNode* mParent; list_type mChildren; public: /*----- End of TreeNode; what follows is new with EnhancedTreeNode -----*/ /** * Because the type of the iterator range returned by getRootRange() * depends on the discriminator enum value, instead of a simple typedef we * use a templated struct. Example usage: * * @code * for (EnhancedTreeNode::root_range::type range = * somenode->getRootRange(); * range.first != range.second; ++range.first) * { * std::cout << (*range.first)->name() << '\n'; * } * @endcode */ template struct root_range { typedef boost::iterator_range< LLTreeRootIter > type; }; /** * Helper method for walking up to (or down from) the tree root. See * LLTreeIter::RootIter. * * Example usage: * @code * BOOST_FOREACH(EnhancedTreeNodePtr node, somenode->getRootRange()) * { * std::cout << node->name() << '\n'; * } * @endcode */ template typename root_range::type getRootRange() const { typedef typename root_range::type range_type; typedef typename range_type::iterator iter_type; return range_type(iter_type(const_cast(this), boost::bind(&EnhancedTreeNode::getParent, _1)), iter_type()); } /** * Because the type of the iterator range returned by getWalkRange() * depends on the discriminator enum value, instead of a simple typedef we * use a templated stuct. Example usage: * * @code * for (EnhancedTreeNode::walk_range::type range = * somenode->getWalkRange(); * range.first != range.second; ++range.first) * { * std::cout << (*range.first)->name() << '\n'; * } * @endcode */ template struct walk_range { typedef boost::iterator_range< LLTreeWalkIter > type; }; /** * Helper method for walking a given node's subtree in any supported * order (see LLTreeIter::WalkIter). * * Example usage: * @code * BOOST_FOREACH(EnhancedTreeNodePtr node, somenode->getWalkRange()) * { * std::cout << node->name() << '\n'; * } * @endcode */ template typename walk_range::type getWalkRange() const { typedef typename walk_range::type range_type; typedef typename range_type::iterator iter_type; return range_type(iter_type(const_cast(this), boost::bind(&EnhancedTreeNode::child_begin, _1), boost::bind(&EnhancedTreeNode::child_end, _1)), iter_type()); } }; /***************************************************************************** * PlainTree *****************************************************************************/ struct PlainTree { PlainTree(const std::string& name, PlainTree* parent=0): mName(name), mParent(parent), mNextSibling(0), mFirstChild(0) { mLastChildLink = &mFirstChild; } ~PlainTree() { delete mNextSibling; delete mFirstChild; } PlainTree* newChild(const std::string& name) { PlainTree* child(new PlainTree(name, this)); *mLastChildLink = child; mLastChildLink = &child->mNextSibling; return child; } std::string name() const { return mName; } std::string mName; PlainTree* mParent; PlainTree* mNextSibling; PlainTree* mFirstChild; PlainTree** mLastChildLink; }; // This "classic" tree tracks each node's children with a linked list anchored // at the parent's mFirstChild and linked through each child's mNextSibling. // LLTreeDFSIter<> and LLTreeBFSIter<> need functors to return begin()/end() // iterators over a given node's children. But because this tree's children // aren't stored in an STL container, we can't just export that container's // begin()/end(). Instead we'll use LLLinkedIter<> to view the hand-maintained // linked list as an iterator range. The straightforward way to do that would // be to add child_begin() and child_end() methods. But let's say (for the // sake of argument) that this struct is so venerable we don't dare modify it // even to add new methods. Well, we can use free functions (or functors) too. LLLinkedIter PlainTree_child_begin(PlainTree* node) { return LLLinkedIter(node->mFirstChild, boost::bind(&PlainTree::mNextSibling, _1)); } LLLinkedIter PlainTree_child_end(PlainTree* node) { return LLLinkedIter(); } /** * This is an example of a helper function to facilitate iterating from a * PlainTree up to the root or down from the root (see LLTreeIter::RootIter). * Note that we're simply overloading the same getRootRange() helper function * name we used for TreeNode. * * Example: * @code * BOOST_FOREACH(PlainTree* node, getRootRange(somenode)) * { * std::cout << node->name() << '\n'; * } * @endcode */ template boost::iterator_range< LLTreeRootIter > getRootRange(PlainTree* node) { typedef LLTreeRootIter iter_type; typedef boost::iterator_range range_type; return range_type(iter_type(node, boost::bind(&PlainTree::mParent, _1)), iter_type()); } /** * This is an example of a helper function to facilitate walking a given * PlainTree's subtree in any supported order (see LLTreeIter::WalkIter). Note * that we're simply overloading the same getWalkRange() helper function name * we used for TreeNode. * * Example: * @code * BOOST_FOREACH(PlainTree* node, getWalkRange(root)) * { * std::cout << node->name() << '\n'; * } * @endcode */ template boost::iterator_range< LLTreeWalkIter > > getWalkRange(PlainTree* node) { typedef LLTreeWalkIter > iter_type; typedef boost::iterator_range range_type; return range_type(iter_type(node, PlainTree_child_begin, PlainTree_child_end), iter_type()); } // We could go through the exercise of writing EnhancedPlainTree containing // root_range, getRootRange(), walk_range and getWalkRange() members -- but we // won't. See EnhancedTreeNode for examples. /***************************************************************************** * Generic tree test data *****************************************************************************/ template typename LLPtrTo::type example_tree() { typedef typename LLPtrTo::type NodePtr; NodePtr root(new NODE("root")); NodePtr A(root->newChild("A")); NodePtr A1(A->newChild("A1")); /* NodePtr A1a*/(A1->newChild("A1a")); /* NodePtr A1b*/(A1->newChild("A1b")); /* NodePtr A1c*/(A1->newChild("A1c")); NodePtr A2(A->newChild("A2")); /* NodePtr A2a*/(A2->newChild("A2a")); /* NodePtr A2b*/(A2->newChild("A2b")); /* NodePtr A2c*/(A2->newChild("A2c")); NodePtr A3(A->newChild("A3")); /* NodePtr A3a*/(A3->newChild("A3a")); /* NodePtr A3b*/(A3->newChild("A3b")); /* NodePtr A3c*/(A3->newChild("A3c")); NodePtr B(root->newChild("B")); NodePtr B1(B->newChild("B1")); /* NodePtr B1a*/(B1->newChild("B1a")); /* NodePtr B1b*/(B1->newChild("B1b")); /* NodePtr B1c*/(B1->newChild("B1c")); NodePtr B2(B->newChild("B2")); /* NodePtr B2a*/(B2->newChild("B2a")); /* NodePtr B2b*/(B2->newChild("B2b")); /* NodePtr B2c*/(B2->newChild("B2c")); NodePtr B3(B->newChild("B3")); /* NodePtr B3a*/(B3->newChild("B3a")); /* NodePtr B3b*/(B3->newChild("B3b")); /* NodePtr B3c*/(B3->newChild("B3c")); NodePtr C(root->newChild("C")); NodePtr C1(C->newChild("C1")); /* NodePtr C1a*/(C1->newChild("C1a")); /* NodePtr C1b*/(C1->newChild("C1b")); /* NodePtr C1c*/(C1->newChild("C1c")); NodePtr C2(C->newChild("C2")); /* NodePtr C2a*/(C2->newChild("C2a")); /* NodePtr C2b*/(C2->newChild("C2b")); /* NodePtr C2c*/(C2->newChild("C2c")); NodePtr C3(C->newChild("C3")); /* NodePtr C3a*/(C3->newChild("C3a")); /* NodePtr C3b*/(C3->newChild("C3b")); /* NodePtr C3c*/(C3->newChild("C3c")); return root; } // WalkExpected is the list of string node names we expect from a // WalkIter traversal of our example_tree() data. template struct WalkExpected: public Expected { // Initialize with bad_strings: we don't expect to use this generic case, // only the specializations. Note that for a classic C-style array we must // pass a pair of iterators rather than extracting boost::begin() and // boost::end() within the target constructor: a template ctor accepts // these classic C-style arrays as char** rather than char*[length]. Oh well. WalkExpected(): Expected(boost::begin(bad_strings), boost::end(bad_strings)) {} }; // list of string node names we expect from traversing example_tree() in // DFS_PRE order const char* dfs_pre_strings[] = { "root", "A", "A1", "A1a", "A1b", "A1c", "A2", "A2a", "A2b", "A2c", "A3", "A3a", "A3b", "A3c", "B", "B1", "B1a", "B1b", "B1c", "B2", "B2a", "B2b", "B2c", "B3", "B3a", "B3b", "B3c", "C", "C1", "C1a", "C1b", "C1c", "C2", "C2a", "C2b", "C2c", "C3", "C3a", "C3b", "C3c" }; // specialize WalkExpected with the expected strings template <> struct WalkExpected: public Expected { WalkExpected(): Expected(boost::begin(dfs_pre_strings), boost::end(dfs_pre_strings)) {} }; // list of string node names we expect from traversing example_tree() in // DFS_POST order const char* dfs_post_strings[] = { "A1a", "A1b", "A1c", "A1", "A2a", "A2b", "A2c", "A2", "A3a", "A3b", "A3c", "A3", "A", "B1a", "B1b", "B1c", "B1", "B2a", "B2b", "B2c", "B2", "B3a", "B3b", "B3c", "B3", "B", "C1a", "C1b", "C1c", "C1", "C2a", "C2b", "C2c", "C2", "C3a", "C3b", "C3c", "C3", "C", "root" }; // specialize WalkExpected with the expected strings template <> struct WalkExpected: public Expected { WalkExpected(): Expected(boost::begin(dfs_post_strings), boost::end(dfs_post_strings)) {} }; // list of string node names we expect from traversing example_tree() in BFS order const char* bfs_strings[] = { "root", "A", "B", "C", "A1", "A2", "A3", "B1", "B2", "B3", "C1", "C2", "C3", "A1a", "A1b", "A1c", "A2a", "A2b", "A2c", "A3a", "A3b", "A3c", "B1a", "B1b", "B1c", "B2a", "B2b", "B2c", "B3a", "B3b", "B3c", "C1a", "C1b", "C1c", "C2a", "C2b", "C2c", "C3a", "C3b", "C3c" }; // specialize WalkExpected with the expected strings template <> struct WalkExpected: public Expected { WalkExpected(): Expected(boost::begin(bfs_strings), boost::end(bfs_strings)) {} }; // extract a particular "arbitrary" node from the example_tree() data: the // second (middle) node at each child level template typename LLPtrTo::type get_B2b(const typename LLPtrTo::type& root, const boost::function::type&)>& child_begin) { typedef typename LLPtrTo::type NodePtr; CHILDITER Bi(child_begin(root)); ++Bi; NodePtr B(*Bi); CHILDITER B2i(child_begin(B)); ++B2i; NodePtr B2(*B2i); CHILDITER B2bi(child_begin(B2)); ++B2bi; NodePtr B2b(*B2bi); return B2b; } // RootExpected is the list of string node names we expect from a // RootIter traversal of our example_tree() data. template struct RootExpected: public Expected { // Initialize with bad_strings: we don't expect to use this generic case, // only the specializations. RootExpected(): Expected(boost::begin(bad_strings), boost::end(bad_strings)) {} }; // list of string node names we expect from traversing UP from // example_tree()'s B2b node const char* up_from_B2b[] = { "B2b", "B2", "B", "root" }; // specialize RootExpected with the expected strings template <> struct RootExpected: public Expected { RootExpected(): Expected(boost::begin(up_from_B2b), boost::end(up_from_B2b)) {} }; // list of string node names we expect from traversing DOWN to // example_tree()'s B2b node const char* down_to_B2b[] = { "root", "B", "B2", "B2b" }; // specialize RootExpected with the expected strings template <> struct RootExpected: public Expected { RootExpected(): Expected(boost::begin(down_to_B2b), boost::end(down_to_B2b)) {} }; /***************************************************************************** * Generic tree test functions *****************************************************************************/ template bool LLTreeRootIter_test(const std::string& itername, const std::string& nodename, const typename LLPtrTo::type& node, PARENTFUNC parentfunc) { std::ostringstream desc; desc << itername << '<' << nodename << "> from " << node->name(); if (! verify(desc.str(), boost::make_iterator_range(LLTreeRootIter(node, parentfunc), LLTreeRootIter()), RootExpected())) return false; // std::cout << desc.str() << '\n'; // Try instantiating an iterator with NULL (that is, a default-constructed // node pointer). This test is less about "did we iterate once?" than "did // we avoid blowing up?" for (LLTreeRootIter hri = LLTreeRootIter(typename LLPtrTo::type(), parentfunc), hrend; hri != hrend; /* ++hri */) // incrementing is moot, and MSVC complains { // std::cout << nodename << '(' << (*hri)->name() << ")\n"; std::cout << itername << '<' << nodename << ">(NULL)\n"; return false; } return true; } template bool LLTreeUpIter_test(const std::string& nodename, PARENTFUNC parentfunc, CHILDFUNC childfunc) { bool success = true; typedef typename LLPtrTo::type ptr_type; ptr_type root(example_tree()); Cleanup cleanup(root); ptr_type B2b(get_B2b(root, childfunc)); if (! LLTreeRootIter_test("LLTreeUpIter", nodename, B2b, parentfunc)) success = false; if (! LLTreeRootIter_test("LLTreeDownIter", nodename, B2b, parentfunc)) success = false; return success; } template bool LLTreeWalkIter_test(const std::string& itername, const std::string& nodename, CHILDBEGINFUNC childbegin, CHILDENDFUNC childend) { typename LLPtrTo::type root(example_tree()); Cleanup::type> cleanup(root); std::ostringstream desc; desc << itername << '<' << nodename << "> from " << root->name(); if (! verify(desc.str(), boost::make_iterator_range(LLTreeWalkIter(root, childbegin, childend), LLTreeWalkIter()), WalkExpected())) return false; // Try instantiating an iterator with NULL (that is, a default-constructed // node pointer). This test is less about "did we iterate once?" than "did // we avoid blowing up?" for (LLTreeWalkIter twi = LLTreeWalkIter(typename LLPtrTo::type(), childbegin, childend), twend; twi != twend; /* ++twi */) // incrementing is moot, and MSVC complains { std::cout << itername << '<' << nodename << ">(NULL)\n"; return false; } return true; } template bool LLTreeIter_tests(const std::string& nodename, PARENTFUNC parentfunc, CHILDBEGINFUNC childbegin, CHILDENDFUNC childend) { bool success = true; if (! LLTreeUpIter_test(nodename, parentfunc, childbegin)) success = false; /*==========================================================================*| LLTreeIter_test >("LLTreeDFSIter", nodename, childbegin, childend); LLTreeIter_test >("LLTreeDFSPostIter", nodename, childbegin, childend); LLTreeIter_test >("LLTreeBFSIter", nodename, childbegin, childend); |*==========================================================================*/ if (! LLTreeWalkIter_test("LLTreeDFSIter", nodename, childbegin, childend)) success = false; if (! LLTreeWalkIter_test("LLTreeDFSPostIter", nodename, childbegin, childend)) success = false; if (! LLTreeWalkIter_test("LLTreeBFSIter", nodename, childbegin, childend)) success = false; return success; } namespace tut { template<> template<> void iter_object::test<3>() { // set_test_name("LLTreeIter tests"); ensure(LLTreeIter_tests ("TreeNode", boost::bind(&TreeNode::getParent, _1), boost::bind(&TreeNode::child_begin, _1), boost::bind(&TreeNode::child_end, _1))); ensure(LLTreeIter_tests > ("PlainTree", boost::bind(&PlainTree::mParent, _1), PlainTree_child_begin, PlainTree_child_end)); } template<> template<> void iter_object::test<4>() { // set_test_name("getRootRange() tests"); // This test function illustrates the looping techniques described in the // comments for the getRootRange() free function, the // EnhancedTreeNode::root_range template and the // EnhancedTreeNode::getRootRange() method. Obviously the BOOST_FOREACH() // forms are more succinct. TreeNodePtr tnroot(example_tree()); TreeNodePtr tnB2b(get_B2b (tnroot, boost::bind(&TreeNode::child_begin, _1))); std::string desc1("BOOST_FOREACH(TreeNodePr, getRootRange(tnB2b))"); // std::cout << desc1 << "\n"; // Although we've commented out the output statement, ensure that the // loop construct is still valid, as promised by the getRootRange() // documentation. BOOST_FOREACH(TreeNodePtr node, getRootRange(tnB2b)) { // std::cout << node->name() << '\n'; } ensure(desc1, verify(desc1, getRootRange(tnB2b), RootExpected())); EnhancedTreeNodePtr etnroot(example_tree()); EnhancedTreeNodePtr etnB2b(get_B2b (etnroot, boost::bind(&EnhancedTreeNode::child_begin, _1))); // std::cout << "EnhancedTreeNode::root_range::type range =\n" // << " etnB2b->getRootRange();\n" // << "for (EnhancedTreeNode::root_range::type::iterator ri = range.begin();\n" // << " ri != range.end(); ++ri)\n"; EnhancedTreeNode::root_range::type range = etnB2b->getRootRange(); for (EnhancedTreeNode::root_range::type::iterator ri = range.begin(); ri != range.end(); ++ri) { // std::cout << (*ri)->name() << '\n'; } std::string desc2("BOOST_FOREACH(EnhancedTreeNodePtr node, etnB2b->getRootRange())"); // std::cout << desc2 << '\n'; BOOST_FOREACH(EnhancedTreeNodePtr node, etnB2b->getRootRange()) { // std::cout << node->name() << '\n'; } ensure(desc2, verify(desc2, etnB2b->getRootRange(), RootExpected())); } template<> template<> void iter_object::test<5>() { // set_test_name("getWalkRange() tests"); // This test function doesn't illustrate the looping permutations for // getWalkRange(); see getRootRange_tests() for such examples. This // function simply verifies that they all work. // TreeNode, using helper function TreeNodePtr tnroot(example_tree()); std::string desc_tnpre("getWalkRange(tnroot)"); ensure(desc_tnpre, verify(desc_tnpre, getWalkRange(tnroot), WalkExpected())); std::string desc_tnpost("getWalkRange(tnroot)"); ensure(desc_tnpost, verify(desc_tnpost, getWalkRange(tnroot), WalkExpected())); std::string desc_tnb("getWalkRange(tnroot)"); ensure(desc_tnb, verify(desc_tnb, getWalkRange(tnroot), WalkExpected())); // EnhancedTreeNode, using method EnhancedTreeNodePtr etnroot(example_tree()); std::string desc_etnpre("etnroot->getWalkRange()"); ensure(desc_etnpre, verify(desc_etnpre, etnroot->getWalkRange(), WalkExpected())); std::string desc_etnpost("etnroot->getWalkRange()"); ensure(desc_etnpost, verify(desc_etnpost, etnroot->getWalkRange(), WalkExpected())); std::string desc_etnb("etnroot->getWalkRange()"); ensure(desc_etnb, verify(desc_etnb, etnroot->getWalkRange(), WalkExpected())); // PlainTree, using helper function PlainTree* ptroot(example_tree()); Cleanup cleanup(ptroot); std::string desc_ptpre("getWalkRange(ptroot)"); ensure(desc_ptpre, verify(desc_ptpre, getWalkRange(ptroot), WalkExpected())); std::string desc_ptpost("getWalkRange(ptroot)"); ensure(desc_ptpost, verify(desc_ptpost, getWalkRange(ptroot), WalkExpected())); std::string desc_ptb("getWalkRange(ptroot)"); ensure(desc_ptb, verify(desc_ptb, getWalkRange(ptroot), WalkExpected())); } } // tut