Add unit test for xfa node iterators

The code under test is too complicate to be refactored
without first establishing tests.

Change-Id: Id7dc87ec67c513220e64df9d82afb8bcd6acef21
Reviewed-on: https://pdfium-review.googlesource.com/2914
Reviewed-by: Nicolás Peña <npm@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/xfa/fxfa/parser/xfa_utils_unittest.cpp b/xfa/fxfa/parser/xfa_utils_unittest.cpp
index 93ee3b9..4c7f47b 100644
--- a/xfa/fxfa/parser/xfa_utils_unittest.cpp
+++ b/xfa/fxfa/parser/xfa_utils_unittest.cpp
@@ -2,9 +2,14 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "testing/gtest/include/gtest/gtest.h"
 #include "xfa/fxfa/parser/xfa_utils.h"
 
+#include <memory>
+#include <vector>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/base/ptr_util.h"
+
 TEST(XfaUtilsImp, XFA_MapRotation) {
   struct TestCase {
     int input;
@@ -20,3 +25,246 @@
               XFA_MapRotation(TestCases[i].input));
   }
 }
+
+class XFANodeIteratorTest : public testing::Test {
+ public:
+  class Node {
+   public:
+    class Strategy {
+     public:
+      static Node* GetFirstChild(Node* pNode) {
+        return pNode && !pNode->children_.empty() ? pNode->children_.front()
+                                                  : nullptr;
+      }
+      static Node* GetNextSibling(Node* pNode) {
+        return pNode ? pNode->next_sibling_ : nullptr;
+      }
+      static Node* GetParent(Node* pNode) {
+        return pNode ? pNode->parent_ : nullptr;
+      }
+    };
+
+    explicit Node(Node* parent) : parent_(parent), next_sibling_(nullptr) {
+      if (parent) {
+        if (!parent->children_.empty())
+          parent->children_.back()->next_sibling_ = this;
+        parent->children_.push_back(this);
+      }
+    }
+
+   private:
+    Node* parent_;
+    Node* next_sibling_;
+    std::vector<Node*> children_;
+  };
+
+  using Iterator = CXFA_NodeIteratorTemplate<Node, Node::Strategy>;
+
+  // Builds a tree along the lines of:
+  //
+  //   root
+  //   |
+  //   child1--child2
+  //            |
+  //            child3------------child7--child9
+  //            |                 |
+  //            child4--child6    child8
+  //            |
+  //            child5
+  //
+  void SetUp() override {
+    root_ = pdfium::MakeUnique<Node>(nullptr);
+    child1_ = pdfium::MakeUnique<Node>(root_.get());
+    child2_ = pdfium::MakeUnique<Node>(root_.get());
+    child3_ = pdfium::MakeUnique<Node>(child2_.get());
+    child4_ = pdfium::MakeUnique<Node>(child3_.get());
+    child5_ = pdfium::MakeUnique<Node>(child4_.get());
+    child6_ = pdfium::MakeUnique<Node>(child3_.get());
+    child7_ = pdfium::MakeUnique<Node>(child2_.get());
+    child8_ = pdfium::MakeUnique<Node>(child7_.get());
+    child9_ = pdfium::MakeUnique<Node>(child2_.get());
+  }
+
+  Node* root() const { return root_.get(); }
+  Node* child1() const { return child1_.get(); }
+  Node* child2() const { return child2_.get(); }
+  Node* child3() const { return child3_.get(); }
+  Node* child4() const { return child4_.get(); }
+  Node* child5() const { return child5_.get(); }
+  Node* child6() const { return child6_.get(); }
+  Node* child7() const { return child7_.get(); }
+  Node* child8() const { return child8_.get(); }
+  Node* child9() const { return child9_.get(); }
+
+ protected:
+  std::unique_ptr<Node> root_;
+  std::unique_ptr<Node> child1_;
+  std::unique_ptr<Node> child2_;
+  std::unique_ptr<Node> child3_;
+  std::unique_ptr<Node> child4_;
+  std::unique_ptr<Node> child5_;
+  std::unique_ptr<Node> child6_;
+  std::unique_ptr<Node> child7_;
+  std::unique_ptr<Node> child8_;
+  std::unique_ptr<Node> child9_;
+};
+
+TEST_F(XFANodeIteratorTest, Empty) {
+  Iterator iter(nullptr);
+  EXPECT_EQ(nullptr, iter.GetRoot());
+  EXPECT_EQ(nullptr, iter.GetCurrent());
+  EXPECT_EQ(nullptr, iter.MoveToNext());
+  EXPECT_EQ(nullptr, iter.MoveToPrev());
+  EXPECT_EQ(nullptr, iter.SkipChildrenAndMoveToNext());
+}
+
+TEST_F(XFANodeIteratorTest, Root) {
+  Iterator iter(root());
+  EXPECT_EQ(root(), iter.GetRoot());
+  EXPECT_EQ(root(), iter.GetCurrent());
+  EXPECT_EQ(root(), iter.GetCurrent());
+}
+
+TEST_F(XFANodeIteratorTest, Current) {
+  Iterator iter(root());
+  iter.SetCurrent(child1());
+  EXPECT_EQ(root(), iter.GetRoot());
+  EXPECT_EQ(child1(), iter.GetCurrent());
+  EXPECT_EQ(child1(), iter.GetCurrent());
+}
+
+TEST_F(XFANodeIteratorTest, CurrentOutsideRoot) {
+  Iterator iter(child1());
+  iter.SetCurrent(root());
+  EXPECT_EQ(child1(), iter.GetRoot());
+  EXPECT_EQ(nullptr, iter.GetCurrent());
+}
+
+TEST_F(XFANodeIteratorTest, CurrentNull) {
+  Iterator iter(root());
+  EXPECT_EQ(child1(), iter.MoveToNext());
+
+  iter.SetCurrent(nullptr);
+  EXPECT_EQ(nullptr, iter.GetCurrent());
+
+  EXPECT_EQ(nullptr, iter.MoveToNext());
+  EXPECT_EQ(nullptr, iter.GetCurrent());
+}
+
+TEST_F(XFANodeIteratorTest, MoveToPrev) {
+  Iterator iter(root());
+  iter.SetCurrent(child8());
+
+  EXPECT_EQ(child7(), iter.MoveToPrev());
+  EXPECT_EQ(child7(), iter.GetCurrent());
+
+  EXPECT_EQ(child6(), iter.MoveToPrev());
+  EXPECT_EQ(child6(), iter.GetCurrent());
+
+  EXPECT_EQ(child5(), iter.MoveToPrev());
+  EXPECT_EQ(child5(), iter.GetCurrent());
+
+  EXPECT_EQ(child4(), iter.MoveToPrev());
+  EXPECT_EQ(child4(), iter.GetCurrent());
+
+  EXPECT_EQ(child3(), iter.MoveToPrev());
+  EXPECT_EQ(child3(), iter.GetCurrent());
+
+  EXPECT_EQ(child2(), iter.MoveToPrev());
+  EXPECT_EQ(child2(), iter.GetCurrent());
+
+  EXPECT_EQ(child1(), iter.MoveToPrev());
+  EXPECT_EQ(child1(), iter.GetCurrent());
+
+  EXPECT_EQ(root(), iter.MoveToPrev());
+  EXPECT_EQ(root(), iter.GetCurrent());
+
+  EXPECT_EQ(nullptr, iter.MoveToPrev());
+  EXPECT_EQ(root(), iter.GetCurrent());
+
+  EXPECT_EQ(nullptr, iter.MoveToPrev());
+  EXPECT_EQ(root(), iter.GetCurrent());
+}
+
+TEST_F(XFANodeIteratorTest, MoveToNext) {
+  Iterator iter(root());
+  iter.SetCurrent(child2());
+
+  EXPECT_EQ(child3(), iter.MoveToNext());
+  EXPECT_EQ(child3(), iter.GetCurrent());
+
+  EXPECT_EQ(child4(), iter.MoveToNext());
+  EXPECT_EQ(child4(), iter.GetCurrent());
+
+  EXPECT_EQ(child5(), iter.MoveToNext());
+  EXPECT_EQ(child5(), iter.GetCurrent());
+
+  EXPECT_EQ(child6(), iter.MoveToNext());
+  EXPECT_EQ(child6(), iter.GetCurrent());
+
+  EXPECT_EQ(child7(), iter.MoveToNext());
+  EXPECT_EQ(child7(), iter.GetCurrent());
+
+  EXPECT_EQ(child8(), iter.MoveToNext());
+  EXPECT_EQ(child8(), iter.GetCurrent());
+
+  EXPECT_EQ(child9(), iter.MoveToNext());
+  EXPECT_EQ(child9(), iter.GetCurrent());
+
+  EXPECT_EQ(nullptr, iter.MoveToNext());
+  EXPECT_EQ(nullptr, iter.GetCurrent());
+
+  EXPECT_EQ(nullptr, iter.MoveToNext());
+  EXPECT_EQ(nullptr, iter.GetCurrent());
+}
+
+TEST_F(XFANodeIteratorTest, SkipChildrenAndMoveToNext) {
+  Iterator iter(root());
+  iter.SetCurrent(child3());
+  EXPECT_EQ(child7(), iter.SkipChildrenAndMoveToNext());
+  EXPECT_EQ(child9(), iter.SkipChildrenAndMoveToNext());
+  EXPECT_EQ(nullptr, iter.SkipChildrenAndMoveToNext());
+}
+
+TEST_F(XFANodeIteratorTest, BackAndForth) {
+  Iterator iter(root());
+  EXPECT_EQ(child1(), iter.MoveToNext());
+  EXPECT_EQ(child2(), iter.MoveToNext());
+  EXPECT_EQ(child3(), iter.MoveToNext());
+  EXPECT_EQ(child4(), iter.MoveToNext());
+  EXPECT_EQ(child5(), iter.MoveToNext());
+  EXPECT_EQ(child4(), iter.MoveToPrev());
+  EXPECT_EQ(child3(), iter.MoveToPrev());
+  EXPECT_EQ(child2(), iter.MoveToPrev());
+  EXPECT_EQ(child1(), iter.MoveToPrev());
+}
+
+TEST_F(XFANodeIteratorTest, NextFromBeforeTheBeginning) {
+  Iterator iter(root());
+  EXPECT_EQ(nullptr, iter.MoveToPrev());
+  EXPECT_EQ(child1(), iter.MoveToNext());
+}
+
+TEST_F(XFANodeIteratorTest, PrevFromAfterTheEnd) {
+  Iterator iter(root());
+  iter.SetCurrent(child9());
+  EXPECT_EQ(nullptr, iter.MoveToNext());
+  EXPECT_EQ(child9(), iter.MoveToPrev());
+}
+
+TEST_F(XFANodeIteratorTest, ChildAsRootPrev) {
+  Iterator iter(child3());
+  EXPECT_EQ(nullptr, iter.MoveToPrev());
+
+  iter.SetCurrent(child4());
+  EXPECT_EQ(child3(), iter.MoveToPrev());
+  EXPECT_EQ(nullptr, iter.MoveToPrev());
+}
+
+TEST_F(XFANodeIteratorTest, ChildAsRootNext) {
+  Iterator iter(child3());
+  iter.SetCurrent(child4());
+  EXPECT_EQ(child5(), iter.MoveToNext());
+  EXPECT_EQ(child6(), iter.MoveToNext());
+  EXPECT_EQ(nullptr, iter.MoveToNext());
+}