#include #include "mozilla/RefPtr.h" #include "gtest/gtest.h" #include "TreeTraversal.h" using namespace mozilla::layers; using namespace mozilla; enum class SearchNodeType {Needle, Hay}; enum class ForEachNodeType {Continue, Skip}; template class TestNode { public: NS_INLINE_DECL_REFCOUNTING(TestNode); explicit TestNode(T aType, int aExpectedTraversalRank = -1); TestNode* GetLastChild(); TestNode* GetPrevSibling(); void SetPrevSibling(RefPtr> aNode); void AddChild(RefPtr> aNode); void SetActualTraversalRank(int aRank); int GetExpectedTraversalRank(); int GetActualTraversalRank(); T GetType(); private: RefPtr> mPreviousNode; RefPtr> mLastChildNode; int mExpectedTraversalRank; int mActualTraversalRank; T mType; ~TestNode() {}; }; template TestNode::TestNode(T aType, int aExpectedTraversalRank) : mExpectedTraversalRank(aExpectedTraversalRank), mActualTraversalRank(-1), mType(aType) { } template void TestNode::AddChild(RefPtr> aNode) { aNode->SetPrevSibling(mLastChildNode); mLastChildNode = aNode; } template void TestNode::SetPrevSibling(RefPtr> aNode) { mPreviousNode = aNode; } template TestNode* TestNode::GetLastChild() { return mLastChildNode; } template TestNode* TestNode::GetPrevSibling() { return mPreviousNode; } template int TestNode::GetActualTraversalRank() { return mActualTraversalRank; } template void TestNode::SetActualTraversalRank(int aRank) { mActualTraversalRank = aRank; } template int TestNode::GetExpectedTraversalRank() { return mExpectedTraversalRank; } template T TestNode::GetType() { return mType; } typedef TestNode SearchTestNode; typedef TestNode ForEachTestNode; TEST(TreeTraversal, DepthFirstSearchNull) { RefPtr nullNode; RefPtr result = DepthFirstSearch(nullNode.get(), [](SearchTestNode* 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; for (size_t i = 0; i < 10; i++) { if (i == expectedNeedleTraversalRank) { needleNode = new SearchTestNode(SearchNodeType::Needle, i); nodeList.push_back(needleNode); } else if (i < expectedNeedleTraversalRank) { nodeList.push_back(new SearchTestNode(SearchNodeType::Hay, i)); } else { nodeList.push_back(new SearchTestNode(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](SearchTestNode* 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 SearchTestNode(SearchNodeType::Needle, 0); RefPtr childNode1= new SearchTestNode(SearchNodeType::Hay); RefPtr childNode2 = new SearchTestNode(SearchNodeType::Hay); int visitCount = 0; RefPtr result = DepthFirstSearch(root.get(), [&visitCount](SearchTestNode* 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; for (int i = 0; i < 10; i++) { nodeList.push_back(new SearchTestNode(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](SearchTestNode* 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(), [](SearchTestNode* 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 SearchTestNode(SearchNodeType::Needle, i); nodeList.push_back(needleNode); } else if (i < expectedNeedleTraversalRank) { nodeList.push_back(new SearchTestNode(SearchNodeType::Hay, i)); } else { nodeList.push_back(new SearchTestNode(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](SearchTestNode* 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 SearchTestNode(SearchNodeType::Needle, 0); RefPtr childNode1= new SearchTestNode(SearchNodeType::Hay); RefPtr childNode2 = new SearchTestNode(SearchNodeType::Hay); int visitCount = 0; RefPtr result = DepthFirstSearchPostOrder(root.get(), [&visitCount](SearchTestNode* 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; for (int i = 0; i < 10; i++) { nodeList.push_back(new SearchTestNode(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](SearchTestNode* 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(), [](SearchTestNode* 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 SearchTestNode(SearchNodeType::Needle, 0); RefPtr childNode1= new SearchTestNode(SearchNodeType::Hay); RefPtr childNode2 = new SearchTestNode(SearchNodeType::Hay); int visitCount = 0; RefPtr result = BreadthFirstSearch(root.get(), [&visitCount](SearchTestNode* 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; for (size_t i = 0; i < 10; i++) { if (i == expectedNeedleTraversalRank) { needleNode = new SearchTestNode(SearchNodeType::Needle, i); nodeList.push_back(needleNode); } else if (i < expectedNeedleTraversalRank) { nodeList.push_back(new SearchTestNode(SearchNodeType::Hay, i)); } else { nodeList.push_back(new SearchTestNode(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](SearchTestNode* 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; for (int i = 0; i < 10; i++) { nodeList.push_back(new SearchTestNode(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](SearchTestNode* 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(), [](ForEachTestNode* aNode) { return TraversalFlag::Continue; }); } TEST(TreeTraversal, ForEachNodeAllEligible) { std::vector> nodeList; int visitCount = 0; for (int i = 0; i < 10; i++) { nodeList.push_back(new ForEachTestNode(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](ForEachTestNode* 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 ForEachTestNode(ForEachNodeType::Continue, 0)); expectedVisitedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Skip, 1)); expectedVisitedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Continue, 2)); expectedVisitedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Skip, 3)); expectedSkippedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Continue)); expectedSkippedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Continue)); expectedSkippedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Skip)); expectedSkippedNodeList.push_back(new ForEachTestNode(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](ForEachTestNode* 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 ForEachTestNode(ForEachNodeType::Skip, 0); RefPtr childNode1 = new ForEachTestNode(ForEachNodeType::Continue); RefPtr chlidNode2 = new ForEachTestNode(ForEachNodeType::Skip); ForEachNode(root.get(), [&visitCount](ForEachTestNode* 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; int visitCount = 0; for (int i = 0; i < 10; i++) { if (i == 1 || i == 9) { nodeList.push_back(new ForEachTestNode(ForEachNodeType::Skip, i)); } else { nodeList.push_back(new ForEachTestNode(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](ForEachTestNode* 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; int visitCount = 0; for (int i = 0; i < 10; i++) { nodeList.push_back(new ForEachTestNode(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](ForEachTestNode* 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."; } }