1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/task_scheduler/scheduler_worker_stack.h"
6 
7 #include "base/logging.h"
8 #include "base/memory/ptr_util.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/task_scheduler/scheduler_worker.h"
11 #include "base/task_scheduler/sequence.h"
12 #include "base/task_scheduler/task_tracker.h"
13 #include "base/test/gtest_util.h"
14 #include "base/threading/platform_thread.h"
15 #include "base/time/time.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 
18 namespace base {
19 namespace internal {
20 
21 namespace {
22 
23 class MockSchedulerWorkerDelegate : public SchedulerWorker::Delegate {
24  public:
OnCanScheduleSequence(scoped_refptr<Sequence> sequence)25   void OnCanScheduleSequence(scoped_refptr<Sequence> sequence) override {
26     ADD_FAILURE() << "Unexpected call to OnCanScheduleSequence().";
27   }
GetThreadLabel() const28   SchedulerWorker::ThreadLabel GetThreadLabel() const override {
29     return SchedulerWorker::ThreadLabel::DEDICATED;
30   }
OnMainEntry(const SchedulerWorker * worker)31   void OnMainEntry(const SchedulerWorker* worker) override {}
GetWork(SchedulerWorker * worker)32   scoped_refptr<Sequence> GetWork(SchedulerWorker* worker) override {
33     return nullptr;
34   }
DidRunTask()35   void DidRunTask() override {
36     ADD_FAILURE() << "Unexpected call to DidRunTask()";
37   }
ReEnqueueSequence(scoped_refptr<Sequence> sequence)38   void ReEnqueueSequence(scoped_refptr<Sequence> sequence) override {
39     ADD_FAILURE() << "Unexpected call to ReEnqueueSequence()";
40   }
GetSleepTimeout()41   TimeDelta GetSleepTimeout() override {
42     return TimeDelta::Max();
43   }
44 };
45 
46 class TaskSchedulerWorkerStackTest : public testing::Test {
47  protected:
SetUp()48   void SetUp() override {
49     worker_a_ = MakeRefCounted<SchedulerWorker>(
50         ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate),
51         task_tracker_.GetTrackedRef());
52     ASSERT_TRUE(worker_a_);
53     worker_b_ = MakeRefCounted<SchedulerWorker>(
54         ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate),
55         task_tracker_.GetTrackedRef());
56     ASSERT_TRUE(worker_b_);
57     worker_c_ = MakeRefCounted<SchedulerWorker>(
58         ThreadPriority::NORMAL, WrapUnique(new MockSchedulerWorkerDelegate),
59         task_tracker_.GetTrackedRef());
60     ASSERT_TRUE(worker_c_);
61   }
62 
63  private:
64   TaskTracker task_tracker_ = {"Test"};
65 
66  protected:
67   scoped_refptr<SchedulerWorker> worker_a_;
68   scoped_refptr<SchedulerWorker> worker_b_;
69   scoped_refptr<SchedulerWorker> worker_c_;
70 };
71 
72 }  // namespace
73 
74 // Verify that Push() and Pop() add/remove values in FIFO order.
TEST_F(TaskSchedulerWorkerStackTest,PushPop)75 TEST_F(TaskSchedulerWorkerStackTest, PushPop) {
76   SchedulerWorkerStack stack;
77   EXPECT_EQ(nullptr, stack.Pop());
78 
79   EXPECT_TRUE(stack.IsEmpty());
80   EXPECT_EQ(0U, stack.Size());
81 
82   stack.Push(worker_a_.get());
83   EXPECT_FALSE(stack.IsEmpty());
84   EXPECT_EQ(1U, stack.Size());
85 
86   stack.Push(worker_b_.get());
87   EXPECT_FALSE(stack.IsEmpty());
88   EXPECT_EQ(2U, stack.Size());
89 
90   stack.Push(worker_c_.get());
91   EXPECT_FALSE(stack.IsEmpty());
92   EXPECT_EQ(3U, stack.Size());
93 
94   EXPECT_EQ(worker_c_.get(), stack.Pop());
95   EXPECT_FALSE(stack.IsEmpty());
96   EXPECT_EQ(2U, stack.Size());
97 
98   stack.Push(worker_c_.get());
99   EXPECT_FALSE(stack.IsEmpty());
100   EXPECT_EQ(3U, stack.Size());
101 
102   EXPECT_EQ(worker_c_.get(), stack.Pop());
103   EXPECT_FALSE(stack.IsEmpty());
104   EXPECT_EQ(2U, stack.Size());
105 
106   EXPECT_EQ(worker_b_.get(), stack.Pop());
107   EXPECT_FALSE(stack.IsEmpty());
108   EXPECT_EQ(1U, stack.Size());
109 
110   EXPECT_EQ(worker_a_.get(), stack.Pop());
111   EXPECT_TRUE(stack.IsEmpty());
112   EXPECT_EQ(0U, stack.Size());
113 
114   EXPECT_EQ(nullptr, stack.Pop());
115 }
116 
117 // Verify that Peek() returns the correct values in FIFO order.
TEST_F(TaskSchedulerWorkerStackTest,PeekPop)118 TEST_F(TaskSchedulerWorkerStackTest, PeekPop) {
119   SchedulerWorkerStack stack;
120   EXPECT_EQ(nullptr, stack.Peek());
121 
122   EXPECT_TRUE(stack.IsEmpty());
123   EXPECT_EQ(0U, stack.Size());
124 
125   stack.Push(worker_a_.get());
126   EXPECT_EQ(worker_a_.get(), stack.Peek());
127   EXPECT_FALSE(stack.IsEmpty());
128   EXPECT_EQ(1U, stack.Size());
129 
130   stack.Push(worker_b_.get());
131   EXPECT_EQ(worker_b_.get(), stack.Peek());
132   EXPECT_FALSE(stack.IsEmpty());
133   EXPECT_EQ(2U, stack.Size());
134 
135   stack.Push(worker_c_.get());
136   EXPECT_EQ(worker_c_.get(), stack.Peek());
137   EXPECT_FALSE(stack.IsEmpty());
138   EXPECT_EQ(3U, stack.Size());
139 
140   EXPECT_EQ(worker_c_.get(), stack.Pop());
141   EXPECT_EQ(worker_b_.get(), stack.Peek());
142   EXPECT_FALSE(stack.IsEmpty());
143   EXPECT_EQ(2U, stack.Size());
144 
145   EXPECT_EQ(worker_b_.get(), stack.Pop());
146   EXPECT_EQ(worker_a_.get(), stack.Peek());
147   EXPECT_FALSE(stack.IsEmpty());
148   EXPECT_EQ(1U, stack.Size());
149 
150   EXPECT_EQ(worker_a_.get(), stack.Pop());
151   EXPECT_TRUE(stack.IsEmpty());
152   EXPECT_EQ(0U, stack.Size());
153 
154   EXPECT_EQ(nullptr, stack.Peek());
155 }
156 
157 // Verify that Contains() returns true for workers on the stack.
TEST_F(TaskSchedulerWorkerStackTest,Contains)158 TEST_F(TaskSchedulerWorkerStackTest, Contains) {
159   SchedulerWorkerStack stack;
160   EXPECT_FALSE(stack.Contains(worker_a_.get()));
161   EXPECT_FALSE(stack.Contains(worker_b_.get()));
162   EXPECT_FALSE(stack.Contains(worker_c_.get()));
163 
164   stack.Push(worker_a_.get());
165   EXPECT_TRUE(stack.Contains(worker_a_.get()));
166   EXPECT_FALSE(stack.Contains(worker_b_.get()));
167   EXPECT_FALSE(stack.Contains(worker_c_.get()));
168 
169   stack.Push(worker_b_.get());
170   EXPECT_TRUE(stack.Contains(worker_a_.get()));
171   EXPECT_TRUE(stack.Contains(worker_b_.get()));
172   EXPECT_FALSE(stack.Contains(worker_c_.get()));
173 
174   stack.Push(worker_c_.get());
175   EXPECT_TRUE(stack.Contains(worker_a_.get()));
176   EXPECT_TRUE(stack.Contains(worker_b_.get()));
177   EXPECT_TRUE(stack.Contains(worker_c_.get()));
178 
179   stack.Pop();
180   EXPECT_TRUE(stack.Contains(worker_a_.get()));
181   EXPECT_TRUE(stack.Contains(worker_b_.get()));
182   EXPECT_FALSE(stack.Contains(worker_c_.get()));
183 
184   stack.Pop();
185   EXPECT_TRUE(stack.Contains(worker_a_.get()));
186   EXPECT_FALSE(stack.Contains(worker_b_.get()));
187   EXPECT_FALSE(stack.Contains(worker_c_.get()));
188 
189   stack.Pop();
190   EXPECT_FALSE(stack.Contains(worker_a_.get()));
191   EXPECT_FALSE(stack.Contains(worker_b_.get()));
192   EXPECT_FALSE(stack.Contains(worker_c_.get()));
193 }
194 
195 // Verify that a value can be removed by Remove().
TEST_F(TaskSchedulerWorkerStackTest,Remove)196 TEST_F(TaskSchedulerWorkerStackTest, Remove) {
197   SchedulerWorkerStack stack;
198   EXPECT_TRUE(stack.IsEmpty());
199   EXPECT_EQ(0U, stack.Size());
200 
201   stack.Push(worker_a_.get());
202   EXPECT_FALSE(stack.IsEmpty());
203   EXPECT_EQ(1U, stack.Size());
204 
205   stack.Push(worker_b_.get());
206   EXPECT_FALSE(stack.IsEmpty());
207   EXPECT_EQ(2U, stack.Size());
208 
209   stack.Push(worker_c_.get());
210   EXPECT_FALSE(stack.IsEmpty());
211   EXPECT_EQ(3U, stack.Size());
212 
213   stack.Remove(worker_b_.get());
214   EXPECT_FALSE(stack.IsEmpty());
215   EXPECT_EQ(2U, stack.Size());
216 
217   EXPECT_EQ(worker_c_.get(), stack.Pop());
218   EXPECT_FALSE(stack.IsEmpty());
219   EXPECT_EQ(1U, stack.Size());
220 
221   EXPECT_EQ(worker_a_.get(), stack.Pop());
222   EXPECT_TRUE(stack.IsEmpty());
223   EXPECT_EQ(0U, stack.Size());
224 }
225 
226 // Verify that a value can be pushed again after it has been removed.
TEST_F(TaskSchedulerWorkerStackTest,PushAfterRemove)227 TEST_F(TaskSchedulerWorkerStackTest, PushAfterRemove) {
228   SchedulerWorkerStack stack;
229   EXPECT_EQ(0U, stack.Size());
230 
231   stack.Push(worker_a_.get());
232   EXPECT_EQ(1U, stack.Size());
233 
234   // Need to also push worker B for this test as it's illegal to Remove() the
235   // top of the stack.
236   stack.Push(worker_b_.get());
237   EXPECT_EQ(2U, stack.Size());
238 
239   stack.Remove(worker_a_.get());
240   EXPECT_EQ(1U, stack.Size());
241 
242   stack.Push(worker_a_.get());
243   EXPECT_EQ(2U, stack.Size());
244 }
245 
246 // Verify that Push() DCHECKs when a value is inserted twice.
TEST_F(TaskSchedulerWorkerStackTest,PushTwice)247 TEST_F(TaskSchedulerWorkerStackTest, PushTwice) {
248   SchedulerWorkerStack stack;
249   stack.Push(worker_a_.get());
250   EXPECT_DCHECK_DEATH({ stack.Push(worker_a_.get()); });
251 }
252 
253 }  // namespace internal
254 }  // namespace base
255