#include #include "mozilla/RefPtr.h" #include "gtest/gtest.h" #include "gtest/MozGTestBench.h" #include "nsRegion.h" #include "nsRect.h" #include "TreeTraversal.h" #include #include const int PERFORMANCE_TREE_DEPTH = 20; const int PERFORMANCE_TREE_CHILD_COUNT = 2; const int PERFORMANCE_TREE_LEAF_COUNT = 1048576; // 2 ** 20 const int PERFORMANCE_REGION_XWRAP = 1024; 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() {}; }; 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() {}; }; 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() {}; }; 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() { } 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; template void CreateBenchmarkTreeRecursive(RefPtr aNode, int aDepth, int aChildrenCount, Action aAction) { if (aDepth > 0) { for (int i = 0; i < aChildrenCount; i++) { RefPtr newNode = new Node(); aNode->AddChild(newNode); CreateBenchmarkTreeRecursive(newNode, aDepth-1, aChildrenCount, aAction); } } aAction(aNode); } template RefPtr CreateBenchmarkTree(int aDepth, int aChildrenCount, Action aAction) { RefPtr rootNode = new Node(); CreateBenchmarkTreeRecursive(rootNode, aDepth, aChildrenCount, aAction); return rootNode; } 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; 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; } } }; bool FindNeedle(SearchTestNode* aNode) { return aNode->GetType() == SearchNodeType::Needle; } 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++; } } }; void ForEachNodeDoNothing(ForEachTestNode* aNode) {} 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; } static void Plain_ForwardDepthFirstSearchPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesWithLastLeafAsNeedle{needleNode}); needleNode->SetType(SearchNodeType::Needle); RefPtr foundNode = DepthFirstSearchForwardRecursive(root.get()); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardDepthFirstSearchPerformance, &Plain_ForwardDepthFirstSearchPerformance); static void TreeTraversal_ForwardDepthFirstSearchPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesWithLastLeafAsNeedle{needleNode}); needleNode->SetType(SearchNodeType::Needle); RefPtr foundNode = DepthFirstSearch(root.get(), &FindNeedle); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardDepthFirstSearchPerformance, &TreeTraversal_ForwardDepthFirstSearchPerformance); 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; } static void Plain_ForwardDepthFirstSearchCaptureVariablesPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int a = 1; int b = 1; int c = 1; int d = 1; int e = 1; int f = 1; int g = 1; int h = 1; int i = 1; int j = 1; int k = 1; int l = 1; int m = 1; int n = 1; int o = 1; int p = 1; int q = 1; int r = 1; int s = 1; int t = 1; int u = 1; int v = 1; int w = 1; int x = 1; int y = 1; int z = 1; int needleTotal = 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; int hayTotal = 0; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeValuesAllFalseValuesForward{hayTotal, needleNode}); needleNode->SetValue(needleTotal); RefPtr foundNode = DepthFirstSearchCaptureVariablesForwardRecursive(root.get(), 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); ASSERT_EQ(foundNode->GetValue(), needleTotal); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardDepthFirstSearchCaptureVariablesPerformance, &Plain_ForwardDepthFirstSearchCaptureVariablesPerformance); static void TreeTraversal_ForwardDepthFirstSearchCaptureVariablesPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int a = 1; int b = 1; int c = 1; int d = 1; int e = 1; int f = 1; int g = 1; int h = 1; int i = 1; int j = 1; int k = 1; int l = 1; int m = 1; int n = 1; int o = 1; int p = 1; int q = 1; int r = 1; int s = 1; int t = 1; int u = 1; int v = 1; int w = 1; int x = 1; int y = 1; int z = 1; int needleTotal = 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; int hayTotal = 0; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeValuesAllFalseValuesForward{hayTotal, needleNode}); needleNode->SetValue(needleTotal); RefPtr foundNode = DepthFirstSearch(root.get(), [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] (SearchTestNodeForward* aNode) { return 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; }); ASSERT_EQ(foundNode->GetValue(), needleTotal); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardDepthFirstSearchCaptureVariablesPerformance, &TreeTraversal_ForwardDepthFirstSearchCaptureVariablesPerformance); 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; } static void Plain_ForwardDepthFirstSearchPostOrderPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesAllHay{}); root->SetType(SearchNodeType::Needle); RefPtr foundNode = DepthFirstSearchPostOrderForwardRecursive(root.get()); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(root, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardDepthFirstSearchPostOrderPerformance, &Plain_ForwardDepthFirstSearchPostOrderPerformance); static void TreeTraversal_ForwardDepthFirstSearchPostOrderPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesAllHay{}); root->SetType(SearchNodeType::Needle); RefPtr foundNode = DepthFirstSearchPostOrder(root.get(), &FindNeedle); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(root, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardDepthFirstSearchPostOrderPerformance, &TreeTraversal_ForwardDepthFirstSearchPostOrderPerformance); template static RefPtr BreadthFirstSearchForwardQueue(RefPtr aNode) { 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; } static void Plain_ForwardBreadthFirstSearchPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesWithLastLeafAsNeedle{needleNode}); needleNode->SetType(SearchNodeType::Needle); RefPtr foundNode = BreadthFirstSearchForwardQueue(root.get()); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardBreadthFirstSearchPerformance, &Plain_ForwardBreadthFirstSearchPerformance); static void TreeTraversal_ForwardBreadthFirstSearchPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesWithLastLeafAsNeedle{needleNode}); needleNode->SetType(SearchNodeType::Needle); RefPtr foundNode = BreadthFirstSearch(root.get(), &FindNeedle); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardBreadthFirstSearchPerformance, &TreeTraversal_ForwardBreadthFirstSearchPerformance); // This test ((Plain|TreeTraversal)_ForwardForEachNodePostOrderPerformance) // uses the following benchmark: // // Starting with a tree whose leaves only are augmented with region data // (arranged as a series of 1x1 blocks stacked in rows of 100000), calculate // each ancestor's region as the union of its child regions. template static void ForEachNodePostOrderForwardRecursive(RefPtr aNode) { if (!aNode->IsLeaf()) { nsRegion newRegion; for (RefPtr node = aNode->GetFirstChild(); node != nullptr; node = node->GetNextSibling()) { ForEachNodePostOrderForwardRecursive(node); nsRegion childRegion = node->GetRegion(); newRegion.OrWith(childRegion); } aNode->SetRegion(newRegion); } } static void Plain_ForwardForEachNodePostOrderPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int squareCount = 0; int xWrap = PERFORMANCE_REGION_XWRAP; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AllocateUnitRegionsToLeavesOnly{xWrap, squareCount}); ForEachNodePostOrderForwardRecursive(root); ASSERT_EQ(root->GetRegion(), nsRegion(nsRect(0, 0, PERFORMANCE_REGION_XWRAP, PERFORMANCE_REGION_XWRAP))); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardForEachNodePostOrderPerformance, &Plain_ForwardForEachNodePostOrderPerformance); static void TreeTraversal_ForwardForEachNodePostOrderPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int squareCount = 0; int xWrap = PERFORMANCE_REGION_XWRAP; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AllocateUnitRegionsToLeavesOnly{xWrap, squareCount}); ForEachNodePostOrder(root.get(), [](ForEachTestNodeForward* aNode) { if (!aNode->IsLeaf()) { nsRegion newRegion; for (RefPtr node = aNode->GetFirstChild(); node != nullptr; node = node->GetNextSibling()) { nsRegion childRegion = node->GetRegion(); newRegion.OrWith(childRegion); } aNode->SetRegion(newRegion); } }); ASSERT_EQ(root->GetRegion(), nsRegion(nsRect(0, 0, PERFORMANCE_REGION_XWRAP, PERFORMANCE_REGION_XWRAP))); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardForEachNodePostOrderPerformance, &TreeTraversal_ForwardForEachNodePostOrderPerformance); // This test ((Plain|TreeTraversal)_ForwardForEachNodePerformance) uses the // following benchmark: // // Starting with a tree whose root has a rectangular region of size // PERFORMANCE_TREE_LEAF_COUNT x 1, for each node, split the region into // PERFORMANCE_TREE_CHILD_COUNT separate regions of equal width and assign to // each child left-to-right. In the end, every node's region should equal the // sum of its childrens' regions, and each level of depth's regions should sum // to the root's region. template static void ForEachNodeForwardRecursive(RefPtr aNode) { if (!aNode->IsLeaf()) { int nChildren = 0; for (RefPtr node = aNode->GetFirstChild(); node != nullptr; node = node->GetNextSibling()) { nChildren++; } nsRect bounds = aNode->GetRegion().GetBounds(); int childWidth = bounds.width / nChildren; int x = bounds.x; for (RefPtr node = aNode->GetFirstChild(); node != nullptr; node = node->GetNextSibling()) { node->SetRegion(nsRegion(nsRect(x, 0, childWidth, 1))); ForEachNodeForwardRecursive(node); x += childWidth; } } } static void Plain_ForwardForEachNodePerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT; RefPtr root = CreateBenchmarkTree(depth, childrenCount, &ForEachNodeDoNothing); root->SetRegion(nsRegion(nsRect(0, 0, rectangleWidth, 1))); ForEachNodeForwardRecursive(root); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardForEachNodePerformance, &Plain_ForwardForEachNodePerformance); static void TreeTraversal_ForwardForEachNodePerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT; RefPtr root = CreateBenchmarkTree(depth, childrenCount, &ForEachNodeDoNothing); root->SetRegion(nsRegion(nsRect(0, 0, rectangleWidth, 1))); ForEachNode(root.get(), [](ForEachTestNodeForward* aNode) { if (!aNode->IsLeaf()) { int nChildren = 0; for (RefPtr node = aNode->GetFirstChild(); node != nullptr; node = node->GetNextSibling()) { nChildren++; } nsRect bounds = aNode->GetRegion().GetBounds(); int childWidth = bounds.width / nChildren; int x = bounds.x; for (RefPtr node = aNode->GetFirstChild(); node != nullptr; node = node->GetNextSibling()) { node->SetRegion(nsRegion(nsRect(x, 0, childWidth, 1))); x += childWidth; } } }); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardForEachNodePerformance, &TreeTraversal_ForwardForEachNodePerformance); // This test ((Plain|TreeTraversal)_ForwardForEachNodeStackPerformance) uses // the following benchmark: // // Starting with an unattached region equal to PERFORMANCE_TREE_LEAF_COUNT x 1, // a starting width of PERFORMANCE_TREE_LEAF_COUNT, and an empty tree, create a // tree with the same conditions as // ((Plain|TreeTraversal)_ForwardForEachNodePerformance) by assigning regions // of the current width, starting from the min x and min y coordinates. For // each level of depth, decrease the current width by a factor of // PERFORMANCE_TREE_CHILD_COUNT, and maintain a stack of ancestor regions. // Use the stack to track the portion of each region still available to assign // to children, which determines the aforementioned min x and min y coordinates. // Compare this to using the program stack. template static void ForEachNodeForwardStackRecursive(RefPtr aNode, int& aRectangleWidth, nsRegion aRegion, int aChildrenCount) { nsRect parentRect = aRegion.GetBounds(); nsRect newRectangle(parentRect.x, parentRect.y, aRectangleWidth, 1); nsRegion newRegion(newRectangle); aNode->SetRegion(nsRegion(newRegion)); aRectangleWidth /= aChildrenCount; for (RefPtr node = aNode->GetFirstChild(); node != nullptr; node = node->GetNextSibling()) { ForEachNodeForwardStackRecursive(node, aRectangleWidth, newRegion, aChildrenCount); newRegion.SubOut(node->GetRegion()); } // Handle case where rectangle width is truncated if power falls below 0, // so we dont lose the regions in future iterations if (aRectangleWidth == 0) { aRectangleWidth = 1; } else { aRectangleWidth *= aChildrenCount; } } static void Plain_ForwardForEachNodeStackPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT; RefPtr root = CreateBenchmarkTree(depth, childrenCount, &ForEachNodeDoNothing); nsRegion startRegion(nsRect(0, 0, rectangleWidth, 1)); ForEachNodeForwardStackRecursive(root, rectangleWidth, startRegion, childrenCount); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ForwardForEachNodeStackPerformance, &Plain_ForwardForEachNodeStackPerformance); static void TreeTraversal_ForwardForEachNodeStackPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT; stack regionStack; RefPtr root = CreateBenchmarkTree(depth, childrenCount, &ForEachNodeDoNothing); regionStack.push(nsRegion(nsRect(0, 0, rectangleWidth, 1))); ForEachNode(root.get(), [®ionStack, &rectangleWidth, childrenCount](ForEachTestNodeForward* aNode) { nsRegion parentRegion = regionStack.top(); nsRect parentRect = parentRegion.GetBounds(); nsRect newRect(parentRect.x, parentRect.y, rectangleWidth, 1); nsRegion newRegion(newRect); aNode->SetRegion(newRegion); regionStack.top().SubOut(newRegion); regionStack.push(newRegion); rectangleWidth /= childrenCount; }, [®ionStack, &rectangleWidth, childrenCount](ForEachTestNodeForward* aNode) { regionStack.pop(); // Handle case where rectangle width is truncated if power falls below 0, // so we dont lose the regions in future iterations if (rectangleWidth == 0) { rectangleWidth = 1; } else { rectangleWidth *= childrenCount; } }); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ForwardForEachNodeStackPerformance, &TreeTraversal_ForwardForEachNodeStackPerformance); 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; } static void Plain_ReverseDepthFirstSearchPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesWithFirstLeafAsNeedle{needleNode}); needleNode->SetType(SearchNodeType::Needle); RefPtr foundNode = DepthFirstSearchReverseRecursive(root.get()); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseDepthFirstSearchPerformance, &Plain_ReverseDepthFirstSearchPerformance); static void TreeTraversal_ReverseDepthFirstSearchPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesWithFirstLeafAsNeedle{needleNode}); needleNode->SetType(SearchNodeType::Needle); RefPtr foundNode = DepthFirstSearch(root.get(), &FindNeedle); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseDepthFirstSearchPerformance, &TreeTraversal_ReverseDepthFirstSearchPerformance); 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; } static void Plain_ReverseDepthFirstSearchCaptureVariablesPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int a = 1; int b = 1; int c = 1; int d = 1; int e = 1; int f = 1; int g = 1; int h = 1; int i = 1; int j = 1; int k = 1; int l = 1; int m = 1; int n = 1; int o = 1; int p = 1; int q = 1; int r = 1; int s = 1; int t = 1; int u = 1; int v = 1; int w = 1; int x = 1; int y = 1; int z = 1; int needleTotal = 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; int hayTotal = 0; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeValuesAllFalseValuesReverse{hayTotal, needleNode}); needleNode->SetValue(needleTotal); RefPtr foundNode = DepthFirstSearchCaptureVariablesReverseRecursive(root.get(), 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); ASSERT_EQ(foundNode->GetValue(), needleTotal); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseDepthFirstSearchCaptureVariablesPerformance, &Plain_ReverseDepthFirstSearchCaptureVariablesPerformance); static void TreeTraversal_ReverseDepthFirstSearchCaptureVariablesPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int a = 1; int b = 1; int c = 1; int d = 1; int e = 1; int f = 1; int g = 1; int h = 1; int i = 1; int j = 1; int k = 1; int l = 1; int m = 1; int n = 1; int o = 1; int p = 1; int q = 1; int r = 1; int s = 1; int t = 1; int u = 1; int v = 1; int w = 1; int x = 1; int y = 1; int z = 1; int needleTotal = 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; int hayTotal = 0; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeValuesAllFalseValuesReverse{hayTotal, needleNode}); needleNode->SetValue(needleTotal); RefPtr foundNode = DepthFirstSearch(root.get(), [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] (SearchTestNodeReverse* aNode) { return 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; }); ASSERT_EQ(foundNode->GetValue(), needleTotal); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseDepthFirstSearchCaptureVariablesPerformance, &TreeTraversal_ReverseDepthFirstSearchCaptureVariablesPerformance); 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; } static void Plain_ReverseDepthFirstSearchPostOrderPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesAllHay{}); root->SetType(SearchNodeType::Needle); RefPtr foundNode = DepthFirstSearchPostOrderReverseRecursive(root.get()); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(root, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseDepthFirstSearchPostOrderPerformance, &Plain_ReverseDepthFirstSearchPostOrderPerformance); static void TreeTraversal_ReverseDepthFirstSearchPostOrderPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesAllHay{}); root->SetType(SearchNodeType::Needle); RefPtr foundNode = DepthFirstSearchPostOrder(root.get(), &FindNeedle); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(root, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseDepthFirstSearchPostOrderPerformance, &TreeTraversal_ReverseDepthFirstSearchPostOrderPerformance); template static RefPtr BreadthFirstSearchReverseQueue(RefPtr aNode) { 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; } static void Plain_ReverseBreadthFirstSearchPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesWithFirstLeafAsNeedle{needleNode}); needleNode->SetType(SearchNodeType::Needle); RefPtr foundNode = BreadthFirstSearchReverseQueue(root.get()); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseBreadthFirstSearchPerformance, &Plain_ReverseBreadthFirstSearchPerformance); static void TreeTraversal_ReverseBreadthFirstSearchPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; RefPtr needleNode; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AssignSearchNodeTypesWithFirstLeafAsNeedle{needleNode}); needleNode->SetType(SearchNodeType::Needle); RefPtr foundNode = BreadthFirstSearch(root.get(), &FindNeedle); ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle); ASSERT_EQ(needleNode, foundNode); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseBreadthFirstSearchPerformance, &TreeTraversal_ReverseBreadthFirstSearchPerformance); // This test ((Plain|TreeTraversal)_ReverseForEachNodePostOrderPerformance) // uses the following benchmark: // // Starting with a tree whose leaves only are augmented with region data // (arranged as a series of 1x1 blocks stacked in rows of 100000), calculate // each ancestor's region as the union of its child regions. template static void ForEachNodePostOrderReverseRecursive(RefPtr aNode) { if (!aNode->IsLeaf()) { nsRegion newRegion; for (RefPtr node = aNode->GetLastChild(); node != nullptr; node = node->GetPrevSibling()) { ForEachNodePostOrderReverseRecursive(node); nsRegion childRegion = node->GetRegion(); newRegion.OrWith(childRegion); } aNode->SetRegion(newRegion); } } static void Plain_ReverseForEachNodePostOrderPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int squareCount = 0; int xWrap = PERFORMANCE_REGION_XWRAP; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AllocateUnitRegionsToLeavesOnly{xWrap, squareCount}); ForEachNodePostOrderReverseRecursive(root); ASSERT_EQ(root->GetRegion(), nsRegion(nsRect(0, 0, PERFORMANCE_REGION_XWRAP, PERFORMANCE_REGION_XWRAP))); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseForEachNodePostOrderPerformance, &Plain_ReverseForEachNodePostOrderPerformance); static void TreeTraversal_ReverseForEachNodePostOrderPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int squareCount = 0; int xWrap = PERFORMANCE_REGION_XWRAP; RefPtr root = CreateBenchmarkTree(depth, childrenCount, AllocateUnitRegionsToLeavesOnly{xWrap, squareCount}); ForEachNodePostOrder(root.get(), [](ForEachTestNodeReverse* aNode) { if (!aNode->IsLeaf()) { nsRegion newRegion; for (RefPtr node = aNode->GetLastChild(); node != nullptr; node = node->GetPrevSibling()) { nsRegion childRegion = node->GetRegion(); newRegion.OrWith(childRegion); } aNode->SetRegion(newRegion); } }); ASSERT_EQ(root->GetRegion(), nsRegion(nsRect(0, 0, PERFORMANCE_REGION_XWRAP, PERFORMANCE_REGION_XWRAP))); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseForEachNodePostOrderPerformance, &TreeTraversal_ReverseForEachNodePostOrderPerformance); // This test ((Plain|TreeTraversal)_ReverseForEachNodePerformance) uses the // following benchmark: // // Starting with a tree whose root has a rectangular region of size // PERFORMANCE_TREE_LEAF_COUNT x 1, for each node, split the region into // PERFORMANCE_TREE_CHILD_COUNT separate regions of equal width and assign to // each child left-to-right. In the end, every node's region should equal the // sum of its childrens' regions, and each level of depth's regions should sum // to the root's region. template static void ForEachNodeReverseRecursive(RefPtr aNode) { if (!aNode->IsLeaf()) { int nChildren = 0; for (RefPtr node = aNode->GetLastChild(); node != nullptr; node = node->GetPrevSibling()) { nChildren++; } nsRect bounds = aNode->GetRegion().GetBounds(); int childWidth = bounds.width / nChildren; int x = bounds.x; for (RefPtr node = aNode->GetLastChild(); node != nullptr; node = node->GetPrevSibling()) { node->SetRegion(nsRegion(nsRect(x, 0, childWidth, 1))); ForEachNodeReverseRecursive(node); x += childWidth; } } } static void Plain_ReverseForEachNodePerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT; RefPtr root = CreateBenchmarkTree(depth, childrenCount, &ForEachNodeDoNothing); root->SetRegion(nsRegion(nsRect(0, 0, rectangleWidth, 1))); ForEachNodeReverseRecursive(root); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseForEachNodePerformance, &Plain_ReverseForEachNodePerformance); static void TreeTraversal_ReverseForEachNodePerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT; RefPtr root = CreateBenchmarkTree(depth, childrenCount, &ForEachNodeDoNothing); root->SetRegion(nsRegion(nsRect(0, 0, rectangleWidth, 1))); ForEachNode(root.get(), [](ForEachTestNodeReverse* aNode) { if (!aNode->IsLeaf()) { int nChildren = 0; for (RefPtr node = aNode->GetLastChild(); node != nullptr; node = node->GetPrevSibling()) { nChildren++; } nsRect bounds = aNode->GetRegion().GetBounds(); int childWidth = bounds.width / nChildren; int x = bounds.x; for (RefPtr node = aNode->GetLastChild(); node != nullptr; node = node->GetPrevSibling()) { node->SetRegion(nsRegion(nsRect(x, 0, childWidth, 1))); x += childWidth; } } }); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseForEachNodePerformance, &TreeTraversal_ReverseForEachNodePerformance); // This test ((Plain|TreeTraversal)_ReverseForEachNodeStackPerformance) uses // the following benchmark: // // Starting with an unattached region equal to PERFORMANCE_TREE_LEAF_COUNT x 1, // a starting width of PERFORMANCE_TREE_LEAF_COUNT, and an empty tree, create a // tree with the same conditions as // ((Plain|TreeTraversal)_ReverseForEachNodePerformance) by assigning regions // of the current width, starting from the min x and min y coordinates. For // each level of depth, decrease the current width by a factor of // PERFORMANCE_TREE_CHILD_COUNT, and maintain a stack of ancestor regions. // Use the stack to track the portion of each region still available to assign // to children, which determines the aforementioned min x and min y coordinates. // Compare this to using the program stack. template static void ForEachNodeReverseStackRecursive(RefPtr aNode, int& aRectangleWidth, nsRegion aRegion, int aChildrenCount) { nsRect parentRect = aRegion.GetBounds(); nsRect newRectangle(parentRect.x, parentRect.y, aRectangleWidth, 1); nsRegion newRegion(newRectangle); aNode->SetRegion(nsRegion(newRegion)); aRectangleWidth /= aChildrenCount; for (RefPtr node = aNode->GetLastChild(); node != nullptr; node = node->GetPrevSibling()) { ForEachNodeReverseStackRecursive(node, aRectangleWidth, newRegion, aChildrenCount); newRegion.SubOut(node->GetRegion()); } // Handle case where rectangle width is truncated if power falls below 0, // so we dont lose the regions in future iterations if (aRectangleWidth == 0) { aRectangleWidth = 1; } else { aRectangleWidth *= aChildrenCount; } } static void Plain_ReverseForEachNodeStackPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT; RefPtr root = CreateBenchmarkTree(depth, childrenCount, &ForEachNodeDoNothing); nsRegion startRegion(nsRect(0, 0, rectangleWidth, 1)); ForEachNodeReverseStackRecursive(root, rectangleWidth, startRegion, childrenCount); } MOZ_GTEST_BENCH(TreeTraversal, Plain_ReverseForEachNodeStackPerformance, &Plain_ReverseForEachNodeStackPerformance); static void TreeTraversal_ReverseForEachNodeStackPerformance() { int depth = PERFORMANCE_TREE_DEPTH; int childrenCount = PERFORMANCE_TREE_CHILD_COUNT; int rectangleWidth = PERFORMANCE_TREE_LEAF_COUNT; stack regionStack; RefPtr root = CreateBenchmarkTree(depth, childrenCount, &ForEachNodeDoNothing); regionStack.push(nsRegion(nsRect(0, 0, rectangleWidth, 1))); ForEachNode(root.get(), [®ionStack, &rectangleWidth, childrenCount](ForEachTestNodeReverse* aNode) { nsRegion parentRegion = regionStack.top(); nsRect parentRect = parentRegion.GetBounds(); nsRect newRect(parentRect.x, parentRect.y, rectangleWidth, 1); nsRegion newRegion(newRect); aNode->SetRegion(newRegion); regionStack.top().SubOut(newRegion); regionStack.push(newRegion); rectangleWidth /= childrenCount; }, [®ionStack, &rectangleWidth, childrenCount](ForEachTestNodeReverse* aNode) { regionStack.pop(); // Handle case where rectangle width is truncated if power falls below 0, // so we dont lose the regions in future iterations if (rectangleWidth == 0) { rectangleWidth = 1; } else { rectangleWidth *= childrenCount; } }); } MOZ_GTEST_BENCH(TreeTraversal, TreeTraversal_ReverseForEachNodeStackPerformance, &TreeTraversal_ReverseForEachNodeStackPerformance);