/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include #include "mozilla/RefPtr.h" #include "gtest/gtest.h" #include "nsRegion.h" #include "nsRect.h" #include "TreeTraversal.h" #include #include using namespace mozilla::layers; using namespace mozilla; enum class SearchNodeType { Needle, Hay }; enum class ForEachNodeType { Continue, Skip }; template class TestNodeBase { public: NS_INLINE_DECL_REFCOUNTING(TestNodeBase); explicit TestNodeBase(T aType, int aExpectedTraversalRank = -1); explicit TestNodeBase(); void SetActualTraversalRank(int aRank); void SetValue(int aValue); void SetType(T aType); void SetRegion(nsRegion aRegion); int GetExpectedTraversalRank(); int GetActualTraversalRank(); int GetValue(); T GetType(); nsRegion GetRegion(); virtual bool IsLeaf() = 0; private: MOZ_INIT_OUTSIDE_CTOR int mExpectedTraversalRank; MOZ_INIT_OUTSIDE_CTOR int mActualTraversalRank; MOZ_INIT_OUTSIDE_CTOR int mValue; MOZ_INIT_OUTSIDE_CTOR nsRegion mRegion; MOZ_INIT_OUTSIDE_CTOR T mType; protected: virtual ~TestNodeBase() = default; }; template class TestNodeReverse : public TestNodeBase { public: explicit TestNodeReverse(T aType, int aExpectedTraversalRank = -1); explicit TestNodeReverse(); void AddChild(RefPtr> aNode); TestNodeReverse* GetLastChild(); TestNodeReverse* GetPrevSibling(); bool IsLeaf(); private: void SetPrevSibling(RefPtr> aNode); void SetLastChild(RefPtr> aNode); RefPtr> mSiblingNode; RefPtr> mLastChildNode; ~TestNodeReverse() = default; }; template class TestNodeForward : public TestNodeBase { public: explicit TestNodeForward(T aType, int aExpectedTraversalRank = -1); explicit TestNodeForward(); void AddChild(RefPtr> aNode); TestNodeForward* GetFirstChild(); TestNodeForward* GetNextSibling(); bool IsLeaf(); private: void SetNextSibling(RefPtr> aNode); void SetLastChild(RefPtr> aNode); void SetFirstChild(RefPtr> aNode); RefPtr> mSiblingNode = nullptr; RefPtr> mFirstChildNode = nullptr; // Track last child to facilitate appending children RefPtr> mLastChildNode = nullptr; ~TestNodeForward() = default; }; template TestNodeReverse::TestNodeReverse(T aType, int aExpectedTraversalRank) : TestNodeBase(aType, aExpectedTraversalRank) {} template TestNodeReverse::TestNodeReverse() : TestNodeBase() {} template void TestNodeReverse::SetLastChild(RefPtr> aNode) { mLastChildNode = aNode; } template void TestNodeReverse::AddChild(RefPtr> aNode) { aNode->SetPrevSibling(mLastChildNode); SetLastChild(aNode); } template void TestNodeReverse::SetPrevSibling(RefPtr> aNode) { mSiblingNode = aNode; } template TestNodeReverse* TestNodeReverse::GetLastChild() { return mLastChildNode; } template TestNodeReverse* TestNodeReverse::GetPrevSibling() { return mSiblingNode; } template bool TestNodeReverse::IsLeaf() { return !mLastChildNode; } template TestNodeForward::TestNodeForward(T aType, int aExpectedTraversalRank) : TestNodeBase(aType, aExpectedTraversalRank) {} template TestNodeForward::TestNodeForward() : TestNodeBase() {} template void TestNodeForward::AddChild(RefPtr> aNode) { if (mFirstChildNode == nullptr) { SetFirstChild(aNode); SetLastChild(aNode); } else { mLastChildNode->SetNextSibling(aNode); SetLastChild(aNode); } } template void TestNodeForward::SetLastChild(RefPtr> aNode) { mLastChildNode = aNode; } template void TestNodeForward::SetFirstChild(RefPtr> aNode) { mFirstChildNode = aNode; } template void TestNodeForward::SetNextSibling(RefPtr> aNode) { mSiblingNode = aNode; } template bool TestNodeForward::IsLeaf() { return !mFirstChildNode; } template TestNodeForward* TestNodeForward::GetFirstChild() { return mFirstChildNode; } template TestNodeForward* TestNodeForward::GetNextSibling() { return mSiblingNode; } template TestNodeBase::TestNodeBase(T aType, int aExpectedTraversalRank) : mExpectedTraversalRank(aExpectedTraversalRank), mActualTraversalRank(-1), mType(aType) {} template TestNodeBase::TestNodeBase() = default; template int TestNodeBase::GetActualTraversalRank() { return mActualTraversalRank; } template void TestNodeBase::SetActualTraversalRank(int aRank) { mActualTraversalRank = aRank; } template int TestNodeBase::GetExpectedTraversalRank() { return mExpectedTraversalRank; } template T TestNodeBase::GetType() { return mType; } template void TestNodeBase::SetType(T aType) { mType = aType; } template nsRegion TestNodeBase::GetRegion() { return mRegion; } template void TestNodeBase::SetRegion(nsRegion aRegion) { mRegion = aRegion; } template int TestNodeBase::GetValue() { return mValue; } template void TestNodeBase::SetValue(int aValue) { mValue = aValue; } typedef TestNodeBase SearchTestNode; typedef TestNodeBase ForEachTestNode; typedef TestNodeReverse SearchTestNodeReverse; typedef TestNodeReverse ForEachTestNodeReverse; typedef TestNodeForward SearchTestNodeForward; typedef TestNodeForward ForEachTestNodeForward; TEST(TreeTraversal, DepthFirstSearchNull) { RefPtr nullNode; RefPtr result = DepthFirstSearch( nullNode.get(), [](SearchTestNodeReverse* aNode) { return aNode->GetType() == SearchNodeType::Needle; }); ASSERT_EQ(result.get(), nullptr) << "Null root did not return null search result."; } TEST(TreeTraversal, DepthFirstSearchValueExists) { int visitCount = 0; size_t expectedNeedleTraversalRank = 7; RefPtr needleNode; std::vector> nodeList; nodeList.reserve(10); for (size_t i = 0; i < 10; i++) { if (i == expectedNeedleTraversalRank) { needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i); nodeList.push_back(needleNode); } else if (i < expectedNeedleTraversalRank) { nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i)); } else { nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay)); } } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[4]); nodeList[1]->AddChild(nodeList[2]); nodeList[1]->AddChild(nodeList[3]); nodeList[4]->AddChild(nodeList[5]); nodeList[4]->AddChild(nodeList[6]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); RefPtr foundNode = DepthFirstSearch( root.get(), [&visitCount](SearchTestNodeForward* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node."; ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle) << "Returned node does not match expected value (something odd " "happened)."; } TEST(TreeTraversal, DepthFirstSearchValueExistsReverse) { int visitCount = 0; size_t expectedNeedleTraversalRank = 7; RefPtr needleNode; std::vector> nodeList; nodeList.reserve(10); for (size_t i = 0; i < 10; i++) { if (i == expectedNeedleTraversalRank) { needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i); nodeList.push_back(needleNode); } else if (i < expectedNeedleTraversalRank) { nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i)); } else { nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay)); } } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[4]); nodeList[0]->AddChild(nodeList[1]); nodeList[1]->AddChild(nodeList[3]); nodeList[1]->AddChild(nodeList[2]); nodeList[4]->AddChild(nodeList[6]); nodeList[4]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[9]); nodeList[7]->AddChild(nodeList[8]); RefPtr foundNode = DepthFirstSearch( root.get(), [&visitCount](SearchTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node."; ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle) << "Returned node does not match expected value (something odd " "happened)."; } TEST(TreeTraversal, DepthFirstSearchRootIsNeedle) { RefPtr root = new SearchTestNodeReverse(SearchNodeType::Needle, 0); RefPtr childNode1 = new SearchTestNodeReverse(SearchNodeType::Hay); RefPtr childNode2 = new SearchTestNodeReverse(SearchNodeType::Hay); int visitCount = 0; RefPtr result = DepthFirstSearch( root.get(), [&visitCount](SearchTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); ASSERT_EQ(result, root) << "Search starting at needle did not return needle."; ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank()) << "Search starting at needle did not return needle."; ASSERT_EQ(childNode1->GetExpectedTraversalRank(), childNode1->GetActualTraversalRank()) << "Search starting at needle continued past needle."; ASSERT_EQ(childNode2->GetExpectedTraversalRank(), childNode2->GetActualTraversalRank()) << "Search starting at needle continued past needle."; } TEST(TreeTraversal, DepthFirstSearchValueDoesNotExist) { int visitCount = 0; std::vector> nodeList; nodeList.reserve(10); for (int i = 0; i < 10; i++) { nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i)); } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[4]); nodeList[1]->AddChild(nodeList[2]); nodeList[1]->AddChild(nodeList[3]); nodeList[4]->AddChild(nodeList[5]); nodeList[4]->AddChild(nodeList[6]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); RefPtr foundNode = DepthFirstSearch( root.get(), [&visitCount](SearchTestNodeForward* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (int i = 0; i < 10; i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode.get(), nullptr) << "Search found something that should not exist."; } TEST(TreeTraversal, DepthFirstSearchValueDoesNotExistReverse) { int visitCount = 0; std::vector> nodeList; nodeList.reserve(10); for (int i = 0; i < 10; i++) { nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i)); } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[4]); nodeList[0]->AddChild(nodeList[1]); nodeList[1]->AddChild(nodeList[3]); nodeList[1]->AddChild(nodeList[2]); nodeList[4]->AddChild(nodeList[6]); nodeList[4]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[9]); nodeList[7]->AddChild(nodeList[8]); RefPtr foundNode = DepthFirstSearch( root.get(), [&visitCount](SearchTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (int i = 0; i < 10; i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode.get(), nullptr) << "Search found something that should not exist."; } TEST(TreeTraversal, DepthFirstSearchPostOrderNull) { RefPtr nullNode; RefPtr result = DepthFirstSearchPostOrder( nullNode.get(), [](SearchTestNodeReverse* aNode) { return aNode->GetType() == SearchNodeType::Needle; }); ASSERT_EQ(result.get(), nullptr) << "Null root did not return null search result."; } TEST(TreeTraversal, DepthFirstSearchPostOrderValueExists) { int visitCount = 0; size_t expectedNeedleTraversalRank = 7; RefPtr needleNode; std::vector> nodeList; for (size_t i = 0; i < 10; i++) { if (i == expectedNeedleTraversalRank) { needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i); nodeList.push_back(needleNode); } else if (i < expectedNeedleTraversalRank) { nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i)); } else { nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay)); } } RefPtr root = nodeList[9]; nodeList[9]->AddChild(nodeList[2]); nodeList[9]->AddChild(nodeList[8]); nodeList[2]->AddChild(nodeList[0]); nodeList[2]->AddChild(nodeList[1]); nodeList[8]->AddChild(nodeList[6]); nodeList[8]->AddChild(nodeList[7]); nodeList[6]->AddChild(nodeList[5]); nodeList[5]->AddChild(nodeList[3]); nodeList[5]->AddChild(nodeList[4]); RefPtr foundNode = DepthFirstSearchPostOrder( root.get(), [&visitCount](SearchTestNodeForward* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node."; ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle) << "Returned node does not match expected value (something odd " "happened)."; } TEST(TreeTraversal, DepthFirstSearchPostOrderValueExistsReverse) { int visitCount = 0; size_t expectedNeedleTraversalRank = 7; RefPtr needleNode; std::vector> nodeList; for (size_t i = 0; i < 10; i++) { if (i == expectedNeedleTraversalRank) { needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i); nodeList.push_back(needleNode); } else if (i < expectedNeedleTraversalRank) { nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i)); } else { nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay)); } } RefPtr root = nodeList[9]; nodeList[9]->AddChild(nodeList[8]); nodeList[9]->AddChild(nodeList[2]); nodeList[2]->AddChild(nodeList[1]); nodeList[2]->AddChild(nodeList[0]); nodeList[8]->AddChild(nodeList[7]); nodeList[8]->AddChild(nodeList[6]); nodeList[6]->AddChild(nodeList[5]); nodeList[5]->AddChild(nodeList[4]); nodeList[5]->AddChild(nodeList[3]); RefPtr foundNode = DepthFirstSearchPostOrder( root.get(), [&visitCount](SearchTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node."; ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle) << "Returned node does not match expected value (something odd " "happened)."; } TEST(TreeTraversal, DepthFirstSearchPostOrderRootIsNeedle) { RefPtr root = new SearchTestNodeReverse(SearchNodeType::Needle, 0); RefPtr childNode1 = new SearchTestNodeReverse(SearchNodeType::Hay); RefPtr childNode2 = new SearchTestNodeReverse(SearchNodeType::Hay); int visitCount = 0; RefPtr result = DepthFirstSearchPostOrder( root.get(), [&visitCount](SearchTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); ASSERT_EQ(result, root) << "Search starting at needle did not return needle."; ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank()) << "Search starting at needle did not return needle."; ASSERT_EQ(childNode1->GetExpectedTraversalRank(), childNode1->GetActualTraversalRank()) << "Search starting at needle continued past needle."; ASSERT_EQ(childNode2->GetExpectedTraversalRank(), childNode2->GetActualTraversalRank()) << "Search starting at needle continued past needle."; } TEST(TreeTraversal, DepthFirstSearchPostOrderValueDoesNotExist) { int visitCount = 0; std::vector> nodeList; nodeList.reserve(10); for (int i = 0; i < 10; i++) { nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i)); } RefPtr root = nodeList[9]; nodeList[9]->AddChild(nodeList[2]); nodeList[9]->AddChild(nodeList[8]); nodeList[2]->AddChild(nodeList[0]); nodeList[2]->AddChild(nodeList[1]); nodeList[8]->AddChild(nodeList[6]); nodeList[8]->AddChild(nodeList[7]); nodeList[6]->AddChild(nodeList[5]); nodeList[5]->AddChild(nodeList[3]); nodeList[5]->AddChild(nodeList[4]); RefPtr foundNode = DepthFirstSearchPostOrder( root.get(), [&visitCount](SearchTestNodeForward* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (int i = 0; i < 10; i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode.get(), nullptr) << "Search found something that should not exist."; } TEST(TreeTraversal, DepthFirstSearchPostOrderValueDoesNotExistReverse) { int visitCount = 0; std::vector> nodeList; nodeList.reserve(10); for (int i = 0; i < 10; i++) { nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i)); } RefPtr root = nodeList[9]; nodeList[9]->AddChild(nodeList[8]); nodeList[9]->AddChild(nodeList[2]); nodeList[2]->AddChild(nodeList[1]); nodeList[2]->AddChild(nodeList[0]); nodeList[8]->AddChild(nodeList[7]); nodeList[8]->AddChild(nodeList[6]); nodeList[6]->AddChild(nodeList[5]); nodeList[5]->AddChild(nodeList[4]); nodeList[5]->AddChild(nodeList[3]); RefPtr foundNode = DepthFirstSearchPostOrder( root.get(), [&visitCount](SearchTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (int i = 0; i < 10; i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode.get(), nullptr) << "Search found something that should not exist."; } TEST(TreeTraversal, BreadthFirstSearchNull) { RefPtr nullNode; RefPtr result = BreadthFirstSearch( nullNode.get(), [](SearchTestNodeReverse* aNode) { return aNode->GetType() == SearchNodeType::Needle; }); ASSERT_EQ(result.get(), nullptr) << "Null root did not return null search result."; } TEST(TreeTraversal, BreadthFirstSearchRootIsNeedle) { RefPtr root = new SearchTestNodeReverse(SearchNodeType::Needle, 0); RefPtr childNode1 = new SearchTestNodeReverse(SearchNodeType::Hay); RefPtr childNode2 = new SearchTestNodeReverse(SearchNodeType::Hay); int visitCount = 0; RefPtr result = BreadthFirstSearch( root.get(), [&visitCount](SearchTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); ASSERT_EQ(result, root) << "Search starting at needle did not return needle."; ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank()) << "Search starting at needle did not return needle."; ASSERT_EQ(childNode1->GetExpectedTraversalRank(), childNode1->GetActualTraversalRank()) << "Search starting at needle continued past needle."; ASSERT_EQ(childNode2->GetExpectedTraversalRank(), childNode2->GetActualTraversalRank()) << "Search starting at needle continued past needle."; } TEST(TreeTraversal, BreadthFirstSearchValueExists) { int visitCount = 0; size_t expectedNeedleTraversalRank = 7; RefPtr needleNode; std::vector> nodeList; nodeList.reserve(10); for (size_t i = 0; i < 10; i++) { if (i == expectedNeedleTraversalRank) { needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i); nodeList.push_back(needleNode); } else if (i < expectedNeedleTraversalRank) { nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i)); } else { nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay)); } } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[2]); nodeList[1]->AddChild(nodeList[3]); nodeList[1]->AddChild(nodeList[4]); nodeList[2]->AddChild(nodeList[5]); nodeList[2]->AddChild(nodeList[6]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); RefPtr foundNode = BreadthFirstSearch( root.get(), [&visitCount](SearchTestNodeForward* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node."; ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle) << "Returned node does not match expected value (something odd " "happened)."; } TEST(TreeTraversal, BreadthFirstSearchValueExistsReverse) { int visitCount = 0; size_t expectedNeedleTraversalRank = 7; RefPtr needleNode; std::vector> nodeList; nodeList.reserve(10); for (size_t i = 0; i < 10; i++) { if (i == expectedNeedleTraversalRank) { needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i); nodeList.push_back(needleNode); } else if (i < expectedNeedleTraversalRank) { nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i)); } else { nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay)); } } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[2]); nodeList[0]->AddChild(nodeList[1]); nodeList[1]->AddChild(nodeList[4]); nodeList[1]->AddChild(nodeList[3]); nodeList[2]->AddChild(nodeList[6]); nodeList[2]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[9]); nodeList[7]->AddChild(nodeList[8]); RefPtr foundNode = BreadthFirstSearch( root.get(), [&visitCount](SearchTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node."; ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle) << "Returned node does not match expected value (something odd " "happened)."; } TEST(TreeTraversal, BreadthFirstSearchValueDoesNotExist) { int visitCount = 0; std::vector> nodeList; nodeList.reserve(10); for (int i = 0; i < 10; i++) { nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i)); } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[2]); nodeList[1]->AddChild(nodeList[3]); nodeList[1]->AddChild(nodeList[4]); nodeList[2]->AddChild(nodeList[5]); nodeList[2]->AddChild(nodeList[6]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); RefPtr foundNode = BreadthFirstSearch( root.get(), [&visitCount](SearchTestNodeForward* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode.get(), nullptr) << "Search found something that should not exist."; } TEST(TreeTraversal, BreadthFirstSearchValueDoesNotExistReverse) { int visitCount = 0; std::vector> nodeList; nodeList.reserve(10); for (int i = 0; i < 10; i++) { nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i)); } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[2]); nodeList[0]->AddChild(nodeList[1]); nodeList[1]->AddChild(nodeList[4]); nodeList[1]->AddChild(nodeList[3]); nodeList[2]->AddChild(nodeList[6]); nodeList[2]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[9]); nodeList[7]->AddChild(nodeList[8]); RefPtr foundNode = BreadthFirstSearch( root.get(), [&visitCount](SearchTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == SearchNodeType::Needle; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } ASSERT_EQ(foundNode.get(), nullptr) << "Search found something that should not exist."; } TEST(TreeTraversal, ForEachNodeNullStillRuns) { RefPtr nullNode; ForEachNode( nullNode.get(), [](ForEachTestNodeReverse* aNode) { return TraversalFlag::Continue; }); } TEST(TreeTraversal, ForEachNodeAllEligible) { std::vector> nodeList; int visitCount = 0; nodeList.reserve(10); for (int i = 0; i < 10; i++) { nodeList.push_back( new ForEachTestNodeForward(ForEachNodeType::Continue, i)); } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[4]); nodeList[1]->AddChild(nodeList[2]); nodeList[1]->AddChild(nodeList[3]); nodeList[4]->AddChild(nodeList[5]); nodeList[4]->AddChild(nodeList[6]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); ForEachNode( root.get(), [&visitCount](ForEachTestNodeForward* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == ForEachNodeType::Continue ? TraversalFlag::Continue : TraversalFlag::Skip; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } } TEST(TreeTraversal, ForEachNodeAllEligibleReverse) { std::vector> nodeList; int visitCount = 0; nodeList.reserve(10); for (int i = 0; i < 10; i++) { nodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Continue, i)); } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[4]); nodeList[0]->AddChild(nodeList[1]); nodeList[1]->AddChild(nodeList[3]); nodeList[1]->AddChild(nodeList[2]); nodeList[4]->AddChild(nodeList[6]); nodeList[4]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[9]); nodeList[7]->AddChild(nodeList[8]); ForEachNode( root.get(), [&visitCount](ForEachTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == ForEachNodeType::Continue ? TraversalFlag::Continue : TraversalFlag::Skip; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } } TEST(TreeTraversal, ForEachNodeSomeIneligibleNodes) { std::vector> expectedVisitedNodeList; std::vector> expectedSkippedNodeList; int visitCount = 0; expectedVisitedNodeList.push_back( new ForEachTestNodeForward(ForEachNodeType::Continue, 0)); expectedVisitedNodeList.push_back( new ForEachTestNodeForward(ForEachNodeType::Skip, 1)); expectedVisitedNodeList.push_back( new ForEachTestNodeForward(ForEachNodeType::Continue, 2)); expectedVisitedNodeList.push_back( new ForEachTestNodeForward(ForEachNodeType::Skip, 3)); expectedSkippedNodeList.push_back( new ForEachTestNodeForward(ForEachNodeType::Continue)); expectedSkippedNodeList.push_back( new ForEachTestNodeForward(ForEachNodeType::Continue)); expectedSkippedNodeList.push_back( new ForEachTestNodeForward(ForEachNodeType::Skip)); expectedSkippedNodeList.push_back( new ForEachTestNodeForward(ForEachNodeType::Skip)); RefPtr root = expectedVisitedNodeList[0]; expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]); expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]); expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]); expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[1]); expectedVisitedNodeList[2]->AddChild(expectedVisitedNodeList[3]); expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]); expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]); ForEachNode( root.get(), [&visitCount](ForEachTestNodeForward* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == ForEachNodeType::Continue ? TraversalFlag::Continue : TraversalFlag::Skip; }); for (size_t i = 0; i < expectedVisitedNodeList.size(); i++) { ASSERT_EQ(expectedVisitedNodeList[i]->GetExpectedTraversalRank(), expectedVisitedNodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } for (size_t i = 0; i < expectedSkippedNodeList.size(); i++) { ASSERT_EQ(expectedSkippedNodeList[i]->GetExpectedTraversalRank(), expectedSkippedNodeList[i]->GetActualTraversalRank()) << "Node at index " << i << "was not expected to be hit."; } } TEST(TreeTraversal, ForEachNodeSomeIneligibleNodesReverse) { std::vector> expectedVisitedNodeList; std::vector> expectedSkippedNodeList; int visitCount = 0; expectedVisitedNodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Continue, 0)); expectedVisitedNodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Skip, 1)); expectedVisitedNodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Continue, 2)); expectedVisitedNodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Skip, 3)); expectedSkippedNodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Continue)); expectedSkippedNodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Continue)); expectedSkippedNodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Skip)); expectedSkippedNodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Skip)); RefPtr root = expectedVisitedNodeList[0]; expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]); expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]); expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[1]); expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]); expectedVisitedNodeList[2]->AddChild(expectedVisitedNodeList[3]); expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]); expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]); ForEachNode( root.get(), [&visitCount](ForEachTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == ForEachNodeType::Continue ? TraversalFlag::Continue : TraversalFlag::Skip; }); for (size_t i = 0; i < expectedVisitedNodeList.size(); i++) { ASSERT_EQ(expectedVisitedNodeList[i]->GetExpectedTraversalRank(), expectedVisitedNodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } for (size_t i = 0; i < expectedSkippedNodeList.size(); i++) { ASSERT_EQ(expectedSkippedNodeList[i]->GetExpectedTraversalRank(), expectedSkippedNodeList[i]->GetActualTraversalRank()) << "Node at index " << i << "was not expected to be hit."; } } TEST(TreeTraversal, ForEachNodeIneligibleRoot) { int visitCount = 0; RefPtr root = new ForEachTestNodeReverse(ForEachNodeType::Skip, 0); RefPtr childNode1 = new ForEachTestNodeReverse(ForEachNodeType::Continue); RefPtr chlidNode2 = new ForEachTestNodeReverse(ForEachNodeType::Skip); ForEachNode( root.get(), [&visitCount](ForEachTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == ForEachNodeType::Continue ? TraversalFlag::Continue : TraversalFlag::Skip; }); ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank()) << "Root was hit out of order."; ASSERT_EQ(childNode1->GetExpectedTraversalRank(), childNode1->GetActualTraversalRank()) << "Eligible child was still hit."; ASSERT_EQ(chlidNode2->GetExpectedTraversalRank(), chlidNode2->GetActualTraversalRank()) << "Ineligible child was still hit."; } TEST(TreeTraversal, ForEachNodeLeavesIneligible) { std::vector> nodeList; nodeList.reserve(10); int visitCount = 0; for (int i = 0; i < 10; i++) { if (i == 1 || i == 9) { nodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Skip, i)); } else { nodeList.push_back( new ForEachTestNodeForward(ForEachNodeType::Continue, i)); } } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[1]); nodeList[0]->AddChild(nodeList[2]); nodeList[2]->AddChild(nodeList[3]); nodeList[2]->AddChild(nodeList[4]); nodeList[4]->AddChild(nodeList[5]); nodeList[4]->AddChild(nodeList[6]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[8]); nodeList[7]->AddChild(nodeList[9]); ForEachNode( root.get(), [&visitCount](ForEachTestNodeForward* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == ForEachNodeType::Continue ? TraversalFlag::Continue : TraversalFlag::Skip; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } } TEST(TreeTraversal, ForEachNodeLeavesIneligibleReverse) { std::vector> nodeList; nodeList.reserve(10); int visitCount = 0; for (int i = 0; i < 10; i++) { if (i == 1 || i == 9) { nodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Skip, i)); } else { nodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Continue, i)); } } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[2]); nodeList[0]->AddChild(nodeList[1]); nodeList[2]->AddChild(nodeList[4]); nodeList[2]->AddChild(nodeList[3]); nodeList[4]->AddChild(nodeList[6]); nodeList[4]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[9]); nodeList[7]->AddChild(nodeList[8]); ForEachNode( root.get(), [&visitCount](ForEachTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; return aNode->GetType() == ForEachNodeType::Continue ? TraversalFlag::Continue : TraversalFlag::Skip; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } } TEST(TreeTraversal, ForEachNodeLambdaReturnsVoid) { std::vector> nodeList; nodeList.reserve(10); int visitCount = 0; for (int i = 0; i < 10; i++) { nodeList.push_back( new ForEachTestNodeReverse(ForEachNodeType::Continue, i)); } RefPtr root = nodeList[0]; nodeList[0]->AddChild(nodeList[4]); nodeList[0]->AddChild(nodeList[1]); nodeList[1]->AddChild(nodeList[3]); nodeList[1]->AddChild(nodeList[2]); nodeList[4]->AddChild(nodeList[6]); nodeList[4]->AddChild(nodeList[5]); nodeList[6]->AddChild(nodeList[7]); nodeList[7]->AddChild(nodeList[9]); nodeList[7]->AddChild(nodeList[8]); ForEachNode( root.get(), [&visitCount](ForEachTestNodeReverse* aNode) { aNode->SetActualTraversalRank(visitCount); visitCount++; }); for (size_t i = 0; i < nodeList.size(); i++) { ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), nodeList[i]->GetActualTraversalRank()) << "Node at index " << i << " was hit out of order."; } } struct AssignSearchNodeTypesWithLastLeafAsNeedle { RefPtr& node; void operator()(SearchTestNodeForward* aNode) { aNode->SetType(SearchNodeType::Hay); if (aNode->IsLeaf()) { node = aNode; } } }; struct AssignSearchNodeTypesAllHay { void operator()(SearchTestNode* aNode) { aNode->SetType(SearchNodeType::Hay); } }; struct AssignSearchNodeTypesWithFirstLeafAsNeedle { RefPtr& needleNode; void operator()(SearchTestNodeReverse* aNode) { if (!needleNode && aNode->IsLeaf()) { needleNode = aNode; } aNode->SetType(SearchNodeType::Hay); } }; struct AssignSearchNodeValuesAllFalseValuesReverse { int falseValue; RefPtr& needleNode; void operator()(SearchTestNodeReverse* aNode) { aNode->SetValue(falseValue); if (!needleNode && aNode->IsLeaf()) { needleNode = aNode; } } }; struct AssignSearchNodeValuesAllFalseValuesForward { int falseValue; RefPtr& needleNode; void operator()(SearchTestNodeForward* aNode) { aNode->SetValue(falseValue); needleNode = aNode; } }; struct AllocateUnitRegionsToLeavesOnly { int& xWrap; int& squareCount; void operator()(ForEachTestNode* aNode) { if (aNode->IsLeaf()) { int x = squareCount % xWrap; int y = squareCount / xWrap; aNode->SetRegion(nsRegion(nsRect(x, y, 1, 1))); squareCount++; } } }; template static RefPtr DepthFirstSearchForwardRecursive(RefPtr aNode) { if (aNode->GetType() == SearchNodeType::Needle) { return aNode; } for (RefPtr node = aNode->GetFirstChild(); node != nullptr; node = node->GetNextSibling()) { if (RefPtr foundNode = DepthFirstSearchForwardRecursive(node)) { return foundNode; } } return nullptr; } template static RefPtr DepthFirstSearchCaptureVariablesForwardRecursive( RefPtr aNode, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int& n, int& o, int& p, int& q, int& r, int& s, int& t, int& u, int& v, int& w, int& x, int& y, int& z) { if (aNode->GetValue() == a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z) { return aNode; } for (RefPtr node = aNode->GetFirstChild(); node != nullptr; node = node->GetNextSibling()) { if (RefPtr foundNode = DepthFirstSearchCaptureVariablesForwardRecursive( node, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)) { return foundNode; } } return nullptr; } template static RefPtr DepthFirstSearchPostOrderForwardRecursive( RefPtr aNode) { for (RefPtr node = aNode->GetFirstChild(); node != nullptr; node = node->GetNextSibling()) { if (RefPtr foundNode = DepthFirstSearchPostOrderForwardRecursive(node)) { return foundNode; } } if (aNode->GetType() == SearchNodeType::Needle) { return aNode; } return nullptr; } template static RefPtr BreadthFirstSearchForwardQueue(RefPtr aNode) { std::queue> nodes; nodes.push(aNode); while (!nodes.empty()) { RefPtr node = nodes.front(); nodes.pop(); if (node->GetType() == SearchNodeType::Needle) { return node; } for (RefPtr childNode = node->GetFirstChild(); childNode != nullptr; childNode = childNode->GetNextSibling()) { nodes.push(childNode); } } return nullptr; } template static RefPtr DepthFirstSearchReverseRecursive(RefPtr aNode) { if (aNode->GetType() == SearchNodeType::Needle) { return aNode; } for (RefPtr node = aNode->GetLastChild(); node != nullptr; node = node->GetPrevSibling()) { if (RefPtr foundNode = DepthFirstSearchReverseRecursive(node)) { return foundNode; } } return nullptr; } template static RefPtr DepthFirstSearchCaptureVariablesReverseRecursive( RefPtr aNode, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int& n, int& o, int& p, int& q, int& r, int& s, int& t, int& u, int& v, int& w, int& x, int& y, int& z) { if (aNode->GetValue() == a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p + q + r + s + t + u + v + w + x + y + z) { return aNode; } for (RefPtr node = aNode->GetLastChild(); node != nullptr; node = node->GetPrevSibling()) { if (RefPtr foundNode = DepthFirstSearchCaptureVariablesReverseRecursive( node, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z)) { return foundNode; } } return nullptr; } template static RefPtr DepthFirstSearchPostOrderReverseRecursive( RefPtr aNode) { for (RefPtr node = aNode->GetLastChild(); node != nullptr; node = node->GetPrevSibling()) { if (RefPtr foundNode = DepthFirstSearchPostOrderReverseRecursive(node)) { return foundNode; } } if (aNode->GetType() == SearchNodeType::Needle) { return aNode; } return nullptr; } template static RefPtr BreadthFirstSearchReverseQueue(RefPtr aNode) { std::queue> nodes; nodes.push(aNode); while (!nodes.empty()) { RefPtr node = nodes.front(); nodes.pop(); if (node->GetType() == SearchNodeType::Needle) { return node; } for (RefPtr childNode = node->GetLastChild(); childNode != nullptr; childNode = childNode->GetPrevSibling()) { nodes.push(childNode); } } return nullptr; }