| // Copyright 2016 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_scheduler/scheduler_worker_stack.h" | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/memory/ptr_util.h" | 
 | #include "base/memory/ref_counted.h" | 
 | #include "base/task_scheduler/scheduler_worker.h" | 
 | #include "base/task_scheduler/sequence.h" | 
 | #include "base/task_scheduler/task_tracker.h" | 
 | #include "base/test/gtest_util.h" | 
 | #include "base/threading/platform_thread.h" | 
 | #include "base/time/time.h" | 
 | #include "testing/gtest/include/gtest/gtest.h" | 
 |  | 
 | namespace base { | 
 | namespace internal { | 
 |  | 
 | namespace { | 
 |  | 
 | class MockSchedulerWorkerDelegate : public SchedulerWorker::Delegate { | 
 |  public: | 
 |   void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override { | 
 |     ADD_FAILURE() << "Unexpected call to OnCanScheduleSequence()."; | 
 |   } | 
 |   SchedulerWorker::ThreadLabel GetThreadLabel() const override { | 
 |     return SchedulerWorker::ThreadLabel::DEDICATED; | 
 |   } | 
 |   void OnMainEntry(const SchedulerWorker* worker) override {} | 
 |   scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override { | 
 |     return nullptr; | 
 |   } | 
 |   void DidRunTask() override { | 
 |     ADD_FAILURE() << "Unexpected call to DidRunTask()"; | 
 |   } | 
 |   void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override { | 
 |     ADD_FAILURE() << "Unexpected call to ReEnqueueSequence()"; | 
 |   } | 
 |   TimeDelta GetSleepTimeout() override { | 
 |     return TimeDelta::Max(); | 
 |   } | 
 | }; | 
 |  | 
 | class TaskSchedulerWorkerStackTest : public testing::Test { | 
 |  protected: | 
 |   void SetUp() override { | 
 |     worker_a_ = MakeRefCounted<SchedulerWorker>( | 
 |         ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate), | 
 |         task_tracker_.GetTrackedRef()); | 
 |     ASSERT_TRUE(worker_a_); | 
 |     worker_b_ = MakeRefCounted<SchedulerWorker>( | 
 |         ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate), | 
 |         task_tracker_.GetTrackedRef()); | 
 |     ASSERT_TRUE(worker_b_); | 
 |     worker_c_ = MakeRefCounted<SchedulerWorker>( | 
 |         ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate), | 
 |         task_tracker_.GetTrackedRef()); | 
 |     ASSERT_TRUE(worker_c_); | 
 |   } | 
 |  | 
 |  private: | 
 |   TaskTracker task_tracker_ = {"Test"}; | 
 |  | 
 |  protected: | 
 |   scoped_refptr<SchedulerWorker> worker_a_; | 
 |   scoped_refptr<SchedulerWorker> worker_b_; | 
 |   scoped_refptr<SchedulerWorker> worker_c_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | // Verify that Push() and Pop() add/remove values in FIFO order. | 
 | TEST_F(TaskSchedulerWorkerStackTest, PushPop) { | 
 |   SchedulerWorkerStack stack; | 
 |   EXPECT_EQ(nullptr, stack.Pop()); | 
 |  | 
 |   EXPECT_TRUE(stack.IsEmpty()); | 
 |   EXPECT_EQ(0U, stack.Size()); | 
 |  | 
 |   stack.Push(worker_a_.get()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(1U, stack.Size()); | 
 |  | 
 |   stack.Push(worker_b_.get()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(2U, stack.Size()); | 
 |  | 
 |   stack.Push(worker_c_.get()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(3U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(worker_c_.get(), stack.Pop()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(2U, stack.Size()); | 
 |  | 
 |   stack.Push(worker_c_.get()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(3U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(worker_c_.get(), stack.Pop()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(2U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(worker_b_.get(), stack.Pop()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(1U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(worker_a_.get(), stack.Pop()); | 
 |   EXPECT_TRUE(stack.IsEmpty()); | 
 |   EXPECT_EQ(0U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(nullptr, stack.Pop()); | 
 | } | 
 |  | 
 | // Verify that Peek() returns the correct values in FIFO order. | 
 | TEST_F(TaskSchedulerWorkerStackTest, PeekPop) { | 
 |   SchedulerWorkerStack stack; | 
 |   EXPECT_EQ(nullptr, stack.Peek()); | 
 |  | 
 |   EXPECT_TRUE(stack.IsEmpty()); | 
 |   EXPECT_EQ(0U, stack.Size()); | 
 |  | 
 |   stack.Push(worker_a_.get()); | 
 |   EXPECT_EQ(worker_a_.get(), stack.Peek()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(1U, stack.Size()); | 
 |  | 
 |   stack.Push(worker_b_.get()); | 
 |   EXPECT_EQ(worker_b_.get(), stack.Peek()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(2U, stack.Size()); | 
 |  | 
 |   stack.Push(worker_c_.get()); | 
 |   EXPECT_EQ(worker_c_.get(), stack.Peek()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(3U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(worker_c_.get(), stack.Pop()); | 
 |   EXPECT_EQ(worker_b_.get(), stack.Peek()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(2U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(worker_b_.get(), stack.Pop()); | 
 |   EXPECT_EQ(worker_a_.get(), stack.Peek()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(1U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(worker_a_.get(), stack.Pop()); | 
 |   EXPECT_TRUE(stack.IsEmpty()); | 
 |   EXPECT_EQ(0U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(nullptr, stack.Peek()); | 
 | } | 
 |  | 
 | // Verify that Contains() returns true for workers on the stack. | 
 | TEST_F(TaskSchedulerWorkerStackTest, Contains) { | 
 |   SchedulerWorkerStack stack; | 
 |   EXPECT_FALSE(stack.Contains(worker_a_.get())); | 
 |   EXPECT_FALSE(stack.Contains(worker_b_.get())); | 
 |   EXPECT_FALSE(stack.Contains(worker_c_.get())); | 
 |  | 
 |   stack.Push(worker_a_.get()); | 
 |   EXPECT_TRUE(stack.Contains(worker_a_.get())); | 
 |   EXPECT_FALSE(stack.Contains(worker_b_.get())); | 
 |   EXPECT_FALSE(stack.Contains(worker_c_.get())); | 
 |  | 
 |   stack.Push(worker_b_.get()); | 
 |   EXPECT_TRUE(stack.Contains(worker_a_.get())); | 
 |   EXPECT_TRUE(stack.Contains(worker_b_.get())); | 
 |   EXPECT_FALSE(stack.Contains(worker_c_.get())); | 
 |  | 
 |   stack.Push(worker_c_.get()); | 
 |   EXPECT_TRUE(stack.Contains(worker_a_.get())); | 
 |   EXPECT_TRUE(stack.Contains(worker_b_.get())); | 
 |   EXPECT_TRUE(stack.Contains(worker_c_.get())); | 
 |  | 
 |   stack.Pop(); | 
 |   EXPECT_TRUE(stack.Contains(worker_a_.get())); | 
 |   EXPECT_TRUE(stack.Contains(worker_b_.get())); | 
 |   EXPECT_FALSE(stack.Contains(worker_c_.get())); | 
 |  | 
 |   stack.Pop(); | 
 |   EXPECT_TRUE(stack.Contains(worker_a_.get())); | 
 |   EXPECT_FALSE(stack.Contains(worker_b_.get())); | 
 |   EXPECT_FALSE(stack.Contains(worker_c_.get())); | 
 |  | 
 |   stack.Pop(); | 
 |   EXPECT_FALSE(stack.Contains(worker_a_.get())); | 
 |   EXPECT_FALSE(stack.Contains(worker_b_.get())); | 
 |   EXPECT_FALSE(stack.Contains(worker_c_.get())); | 
 | } | 
 |  | 
 | // Verify that a value can be removed by Remove(). | 
 | TEST_F(TaskSchedulerWorkerStackTest, Remove) { | 
 |   SchedulerWorkerStack stack; | 
 |   EXPECT_TRUE(stack.IsEmpty()); | 
 |   EXPECT_EQ(0U, stack.Size()); | 
 |  | 
 |   stack.Push(worker_a_.get()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(1U, stack.Size()); | 
 |  | 
 |   stack.Push(worker_b_.get()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(2U, stack.Size()); | 
 |  | 
 |   stack.Push(worker_c_.get()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(3U, stack.Size()); | 
 |  | 
 |   stack.Remove(worker_b_.get()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(2U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(worker_c_.get(), stack.Pop()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |   EXPECT_EQ(1U, stack.Size()); | 
 |  | 
 |   EXPECT_EQ(worker_a_.get(), stack.Pop()); | 
 |   EXPECT_TRUE(stack.IsEmpty()); | 
 |   EXPECT_EQ(0U, stack.Size()); | 
 | } | 
 |  | 
 | // Verify that a value can be pushed again after it has been removed. | 
 | TEST_F(TaskSchedulerWorkerStackTest, PushAfterRemove) { | 
 |   SchedulerWorkerStack stack; | 
 |   EXPECT_EQ(0U, stack.Size()); | 
 |   EXPECT_TRUE(stack.IsEmpty()); | 
 |  | 
 |   stack.Push(worker_a_.get()); | 
 |   EXPECT_EQ(1U, stack.Size()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 |  | 
 |   stack.Remove(worker_a_.get()); | 
 |   EXPECT_EQ(0U, stack.Size()); | 
 |   EXPECT_TRUE(stack.IsEmpty()); | 
 |  | 
 |   stack.Push(worker_a_.get()); | 
 |   EXPECT_EQ(1U, stack.Size()); | 
 |   EXPECT_FALSE(stack.IsEmpty()); | 
 | } | 
 |  | 
 | // Verify that Push() DCHECKs when a value is inserted twice. | 
 | TEST_F(TaskSchedulerWorkerStackTest, PushTwice) { | 
 |   SchedulerWorkerStack stack; | 
 |   stack.Push(worker_a_.get()); | 
 |   EXPECT_DCHECK_DEATH({ stack.Push(worker_a_.get()); }); | 
 | } | 
 |  | 
 | }  // namespace internal | 
 | }  // namespace base |