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