text: // Copyright 2019 The Chromium 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 "base/task/promise/dependent_list.h"
  
  #include <cstdint>
  #include <limits>
  
  #include "base/memory/scoped_refptr.h"
  #include "base/task/promise/abstract_promise.h"
  #include "base/test/do_nothing_promise.h"
  #include "testing/gmock/include/gmock/gmock.h"
  #include "testing/gtest/include/gtest/gtest.h"
  
  namespace base {
  namespace internal {
  namespace {
  
  class PushBackVisitor : public DependentList::Visitor {
   public:
    void Visit(scoped_refptr<AbstractPromise> dependent) override {
      dependents_.push_back(dependent.get());
    }
  
    const std::vector<AbstractPromise*> visited_dependents() const {
      return dependents_;
    }
  
   private:
    std::vector<AbstractPromise*> dependents_;
  };
  
  class FailTestVisitor : public DependentList::Visitor {
   public:
    void Visit(scoped_refptr<AbstractPromise> dependent) override {
      ADD_FAILURE();
    }
  };
  
  }  // namespace
  
  using ::testing::ElementsAre;
  
  TEST(DependentList, ConstructUnresolved) {
    DependentList list(DependentList::ConstructUnresolved{});
    DependentList::Node node;
    EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node));
    EXPECT_FALSE(list.IsRejectedForTesting());
    EXPECT_FALSE(list.IsCanceled());
    EXPECT_FALSE(list.IsResolvedForTesting());
    EXPECT_FALSE(list.IsSettled());
  }
  
  TEST(DependentList, ConstructResolved) {
    DependentList list(DependentList::ConstructResolved{});
    DependentList::Node node;
    EXPECT_EQ(DependentList::InsertResult::FAIL_PROMISE_RESOLVED,
              list.Insert(&node));
    EXPECT_TRUE(list.IsResolved());
    EXPECT_FALSE(list.IsRejected());
    EXPECT_FALSE(list.IsCanceled());
    EXPECT_TRUE(list.IsSettled());
  }
  
  TEST(DependentList, ConstructRejected) {
    DependentList list(DependentList::ConstructRejected{});
    DependentList::Node node;
    EXPECT_EQ(DependentList::InsertResult::FAIL_PROMISE_REJECTED,
              list.Insert(&node));
    EXPECT_TRUE(list.IsRejected());
    EXPECT_FALSE(list.IsCanceled());
    EXPECT_FALSE(list.IsResolved());
    EXPECT_TRUE(list.IsSettled());
  }
  
  TEST(DependentList, ResolveAndConsumeAllDependents) {
    DependentList list(DependentList::ConstructUnresolved{});
    DependentList::Node node1;
    DependentList::Node node2;
    DependentList::Node node3;
    EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node1));
    EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node2));
    EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node3));
  
    EXPECT_FALSE(list.IsResolvedForTesting());
    EXPECT_FALSE(list.IsSettled());
  
    std::vector<AbstractPromise*> expected_dependants = {node1.dependent().get(),
                                                         node2.dependent().get(),
                                                         node3.dependent().get()};
  
    PushBackVisitor visitor;
    list.ResolveAndConsumeAllDependents(&visitor);
    EXPECT_TRUE(list.IsResolved());
    EXPECT_TRUE(list.IsSettled());
  
    EXPECT_EQ(expected_dependants, visitor.visited_dependents());
  
    // Can't insert any more nodes.
    DependentList::Node node4;
    EXPECT_EQ(DependentList::InsertResult::FAIL_PROMISE_RESOLVED,
              list.Insert(&node4));
  }
  
  TEST(DependentList, RejectAndConsumeAllDependents) {
    DependentList list(DependentList::ConstructUnresolved{});
    DependentList::Node node1;
    DependentList::Node node2;
    DependentList::Node node3;
    EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node1));
    EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node2));
    EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node3));
  
    EXPECT_FALSE(list.IsResolvedForTesting());
    EXPECT_FALSE(list.IsSettled());
    std::vector<AbstractPromise*> expected_dependants = {node1.dependent().get(),
                                                         node2.dependent().get(),
                                                         node3.dependent().get()};
  
    PushBackVisitor visitor;
    list.RejectAndConsumeAllDependents(&visitor);
    EXPECT_TRUE(list.IsRejected());
    EXPECT_TRUE(list.IsSettled());
  
    EXPECT_EQ(expected_dependants, visitor.visited_dependents());
  
    // Can't insert any more nodes.
    DependentList::Node node4;
    EXPECT_EQ(DependentList::InsertResult::FAIL_PROMISE_REJECTED,
              list.Insert(&node4));
  }
  
  TEST(DependentList, CancelAndConsumeAllDependents) {
    DependentList list(DependentList::ConstructUnresolved{});
    DependentList::Node node1;
    DependentList::Node node2;
    DependentList::Node node3;
    EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node1));
    EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node2));
    EXPECT_EQ(DependentList::InsertResult::SUCCESS, list.Insert(&node3));
  
    EXPECT_FALSE(list.IsResolvedForTesting());
    EXPECT_FALSE(list.IsSettled());
    std::vector<AbstractPromise*> expected_dependants = {node1.dependent().get(),
                                                         node2.dependent().get(),
                                                         node3.dependent().get()};
  
    PushBackVisitor visitor;
    EXPECT_TRUE(list.CancelAndConsumeAllDependents(&visitor));
    EXPECT_TRUE(list.IsCanceled());
    EXPECT_TRUE(list.IsSettled());
  
    EXPECT_EQ(expected_dependants, visitor.visited_dependents());
  
    // Can't insert any more nodes.
    DependentList::Node node4;
    EXPECT_EQ(DependentList::InsertResult::FAIL_PROMISE_CANCELED,
              list.Insert(&node4));
  }
  
  TEST(DependentList, CancelAndConsumeAllDependentsFailsIfAlreadySettled) {
    DependentList list(DependentList::ConstructUnresolved{});
  
    FailTestVisitor visitor;
    list.ResolveAndConsumeAllDependents(&visitor);
  
    EXPECT_FALSE(list.CancelAndConsumeAllDependents(&visitor));
  
    EXPECT_FALSE(list.IsCanceled());
    EXPECT_TRUE(list.IsResolved());
  }
  
  TEST(DependentList, NextPowerOfTwo) {
    static_assert(NextPowerOfTwo(0) == 1u, "");
    static_assert(NextPowerOfTwo(1) == 2u, "");
    static_assert(NextPowerOfTwo(2) == 4u, "");
    static_assert(NextPowerOfTwo(3) == 4u, "");
    static_assert(NextPowerOfTwo(4) == 8u, "");
    static_assert(NextPowerOfTwo((1ull << 21) + (1ull << 19)) == 1ull << 22, "");
    static_assert(NextPowerOfTwo(std::numeric_limits<uintptr_t>::max() >> 1) ==
                      1ull << (sizeof(uintptr_t) * 8 - 1),
                  "");
    static_assert(NextPowerOfTwo(std::numeric_limits<uintptr_t>::max()) == 0u,
                  "");
  }
  
  TEST(DependentListNode, Simple) {
    DependentList::Node node;
    EXPECT_EQ(nullptr, node.prerequisite());
  
    scoped_refptr<AbstractPromise> p = DoNothingPromiseBuilder(FROM_HERE);
    EXPECT_TRUE(p->HasOneRef());
    node.SetPrerequisite(p.get());
    EXPECT_EQ(p.get(), node.prerequisite());
    EXPECT_TRUE(p->HasOneRef());
  
    EXPECT_TRUE(p->HasOneRef());
    node.RetainSettledPrerequisite();
    EXPECT_EQ(p.get(), node.prerequisite());
    EXPECT_FALSE(p->HasOneRef());
  
    node.ClearPrerequisite();
    EXPECT_EQ(nullptr, node.prerequisite());
    EXPECT_TRUE(p->HasOneRef());
  }
  
  }  // namespace internal
  }  // namespace base
  