text: // Copyright 2019 PDFium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "core/fxcrt/retained_tree_node.h"
  
  #include "core/fxcrt/observed_ptr.h"
  #include "core/fxcrt/retain_ptr.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  namespace fxcrt {
  namespace {
  
  class ObservableRetainedTreeNodeForTest
      : public RetainedTreeNode<ObservableRetainedTreeNodeForTest>,
        public Observable {
   public:
    template <typename T, typename... Args>
    friend RetainPtr<T> pdfium::MakeRetain(Args&&... args);
  
   private:
    ObservableRetainedTreeNodeForTest() = default;
  };
  
  void AddClutterToFront(
      const RetainPtr<ObservableRetainedTreeNodeForTest>& parent) {
    for (int i = 0; i < 4; ++i) {
      parent->AppendFirstChild(
          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>());
    }
  }
  
  void AddClutterToBack(
      const RetainPtr<ObservableRetainedTreeNodeForTest>& parent) {
    for (int i = 0; i < 4; ++i) {
      parent->AppendLastChild(
          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>());
    }
  }
  
  }  // namespace
  
  TEST(RetainedTreeNode, NoParent) {
    ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
    {
      RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
      EXPECT_FALSE(ptr->HasChild(ptr.Get()));
      watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
      EXPECT_TRUE(watcher.Get());
    }
    EXPECT_FALSE(watcher.Get());
  }
  
  TEST(RetainedTreeNode, FirstHasParent) {
    ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
    RetainPtr<ObservableRetainedTreeNodeForTest> parent =
        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
    {
      RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
      watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
      parent->AppendFirstChild(ptr);
      EXPECT_FALSE(parent->HasChild(parent.Get()));
      EXPECT_TRUE(parent->HasChild(ptr.Get()));
      EXPECT_TRUE(watcher.Get());
    }
    EXPECT_TRUE(watcher.Get());
    parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
    EXPECT_FALSE(watcher.Get());
    // Now add some clutter.
    {
      RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
      watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
      parent->AppendFirstChild(ptr);
      AddClutterToFront(parent);
      AddClutterToBack(parent);
      EXPECT_TRUE(watcher.Get());
    }
    EXPECT_TRUE(watcher.Get());
    parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
    EXPECT_FALSE(watcher.Get());
  }
  
  TEST(RetainedTreeNode, LastHasParent) {
    ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
    RetainPtr<ObservableRetainedTreeNodeForTest> parent =
        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
    {
      RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
      watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
      parent->AppendLastChild(ptr);
      EXPECT_FALSE(parent->HasChild(parent.Get()));
      EXPECT_TRUE(parent->HasChild(ptr.Get()));
      EXPECT_TRUE(watcher.Get());
    }
    {
      // Now add some clutter.
      RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
      watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
      parent->AppendLastChild(ptr);
      AddClutterToFront(parent);
      AddClutterToBack(parent);
      EXPECT_TRUE(watcher.Get());
    }
    EXPECT_TRUE(watcher.Get());
    parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
    EXPECT_FALSE(watcher.Get());
  }
  
  TEST(RetainedTreeNode, GrandChildCleanedUp) {
    ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
    RetainPtr<ObservableRetainedTreeNodeForTest> grandparent =
        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
    {
      RetainPtr<ObservableRetainedTreeNodeForTest> parent =
          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
      grandparent->AppendFirstChild(parent);
      {
        RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
            pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
        watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
        parent->AppendFirstChild(ptr);
        EXPECT_TRUE(watcher.Get());
      }
      grandparent->RemoveChild(parent);
      EXPECT_TRUE(watcher.Get());
    }
    EXPECT_FALSE(watcher.Get());
  }
  
  TEST(RetainedTreeNode, RemoveSelf) {
    ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
    auto parent = pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
    {
      auto child = pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
      watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(child.Get());
      parent->AppendFirstChild(child);
    }
    EXPECT_TRUE(watcher.Get());
    watcher->RemoveSelfIfParented();
    EXPECT_FALSE(watcher.Get());
  }
  
  TEST(RetainedTreeNode, InsertBeforeAfter) {
    ObservedPtr<ObservableRetainedTreeNodeForTest> watcher;
    RetainPtr<ObservableRetainedTreeNodeForTest> parent =
        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
  
    AddClutterToFront(parent);
    {
      RetainPtr<ObservableRetainedTreeNodeForTest> ptr =
          pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
      watcher = ObservedPtr<ObservableRetainedTreeNodeForTest>(ptr.Get());
      parent->AppendFirstChild(ptr);
      parent->InsertBefore(pdfium::WrapRetain(parent->GetFirstChild()),
                           parent->GetLastChild());
      parent->InsertAfter(pdfium::WrapRetain(parent->GetLastChild()),
                          parent->GetFirstChild());
      EXPECT_TRUE(watcher.Get());
    }
    parent->RemoveChild(pdfium::WrapRetain(watcher.Get()));
    EXPECT_FALSE(watcher.Get());
  }
  
  TEST(RetainedTreeNode, Traversal) {
    RetainPtr<ObservableRetainedTreeNodeForTest> parent =
        pdfium::MakeRetain<ObservableRetainedTreeNodeForTest>();
  
    AddClutterToFront(parent);
    int count = 0;
    for (ObservableRetainedTreeNodeForTest* pNode = parent->GetFirstChild();
         pNode; pNode = pNode->GetNextSibling()) {
      ++count;
    }
    EXPECT_EQ(4, count);
    count = 0;
    for (ObservableRetainedTreeNodeForTest* pNode = parent->GetLastChild(); pNode;
         pNode = pNode->GetPrevSibling()) {
      ++count;
    }
    EXPECT_EQ(4, count);
  }
  
  }  // namespace fxcrt
  