1 // Copyright 2014 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 "sync/internal_api/public/attachments/task_queue.h"
6
7 #include <vector>
8
9 #include "base/bind.h"
10 #include "base/memory/weak_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/timer/mock_timer.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15
16 using base::TimeDelta;
17
18 namespace syncer {
19
20 namespace {
21
22 const TimeDelta kZero;
23
24 } // namespace
25
26 class TaskQueueTest : public testing::Test {
27 protected:
TaskQueueTest()28 TaskQueueTest() : weak_ptr_factory_(this) {
29 queue_.reset(new TaskQueue<int>(
30 base::Bind(&TaskQueueTest::Process, weak_ptr_factory_.GetWeakPtr()),
31 TimeDelta::FromMinutes(1),
32 TimeDelta::FromMinutes(8)));
33 }
34
RunLoop()35 void RunLoop() {
36 base::RunLoop run_loop;
37 run_loop.RunUntilIdle();
38 }
39
Process(const int & task)40 void Process(const int& task) { dispatched_.push_back(task); }
41
42 base::MessageLoop message_loop_;
43 scoped_ptr<TaskQueue<int> > queue_;
44 std::vector<int> dispatched_;
45 base::WeakPtrFactory<TaskQueueTest> weak_ptr_factory_;
46 };
47
48 // See that at most one task is dispatched at a time.
TEST_F(TaskQueueTest,AddToQueue_NoConcurrentTasks)49 TEST_F(TaskQueueTest, AddToQueue_NoConcurrentTasks) {
50 queue_->AddToQueue(1);
51 queue_->AddToQueue(2);
52 RunLoop();
53
54 // Only one has been dispatched.
55 ASSERT_EQ(1U, dispatched_.size());
56 EXPECT_EQ(1, dispatched_.front());
57 RunLoop();
58
59 // Still only one.
60 ASSERT_EQ(1U, dispatched_.size());
61 EXPECT_EQ(1, dispatched_.front());
62 dispatched_.clear();
63 queue_->MarkAsSucceeded(1);
64 RunLoop();
65
66 ASSERT_EQ(1U, dispatched_.size());
67 EXPECT_EQ(2, dispatched_.front());
68 dispatched_.clear();
69 queue_->MarkAsSucceeded(2);
70 RunLoop();
71
72 ASSERT_TRUE(dispatched_.empty());
73 }
74
75 // See that that the queue ignores duplicate adds.
TEST_F(TaskQueueTest,AddToQueue_NoDuplicates)76 TEST_F(TaskQueueTest, AddToQueue_NoDuplicates) {
77 queue_->AddToQueue(1);
78 queue_->AddToQueue(1);
79 queue_->AddToQueue(2);
80 queue_->AddToQueue(1);
81 ASSERT_TRUE(dispatched_.empty());
82 RunLoop();
83
84 ASSERT_EQ(1U, dispatched_.size());
85 EXPECT_EQ(1, dispatched_.front());
86 dispatched_.clear();
87 queue_->MarkAsSucceeded(1);
88 RunLoop();
89
90 ASSERT_EQ(1U, dispatched_.size());
91 EXPECT_EQ(2, dispatched_.front());
92 dispatched_.clear();
93 queue_->MarkAsSucceeded(2);
94 RunLoop();
95
96 ASSERT_TRUE(dispatched_.empty());
97 }
98
99 // See that Retry works as expected.
TEST_F(TaskQueueTest,Retry)100 TEST_F(TaskQueueTest, Retry) {
101 scoped_ptr<base::MockTimer> timer_to_pass(new base::MockTimer(false, false));
102 base::MockTimer* mock_timer = timer_to_pass.get();
103 queue_->SetTimerForTest(timer_to_pass.PassAs<base::Timer>());
104
105 // 1st attempt.
106 queue_->AddToQueue(1);
107 ASSERT_TRUE(mock_timer->IsRunning());
108 ASSERT_EQ(kZero, mock_timer->GetCurrentDelay());
109 TimeDelta last_delay = mock_timer->GetCurrentDelay();
110 mock_timer->Fire();
111 RunLoop();
112
113 // 2nd attempt.
114 ASSERT_FALSE(mock_timer->IsRunning());
115 ASSERT_EQ(1U, dispatched_.size());
116 EXPECT_EQ(1, dispatched_.front());
117 dispatched_.clear();
118 queue_->MarkAsFailed(1);
119 queue_->AddToQueue(1);
120 ASSERT_TRUE(mock_timer->IsRunning());
121 EXPECT_GT(mock_timer->GetCurrentDelay(), last_delay);
122 EXPECT_LE(mock_timer->GetCurrentDelay(), TimeDelta::FromMinutes(1));
123 last_delay = mock_timer->GetCurrentDelay();
124 mock_timer->Fire();
125 RunLoop();
126
127 // 3rd attempt.
128 ASSERT_FALSE(mock_timer->IsRunning());
129 ASSERT_EQ(1U, dispatched_.size());
130 EXPECT_EQ(1, dispatched_.front());
131 dispatched_.clear();
132 queue_->MarkAsFailed(1);
133 queue_->AddToQueue(1);
134 ASSERT_TRUE(mock_timer->IsRunning());
135 EXPECT_GT(mock_timer->GetCurrentDelay(), last_delay);
136 last_delay = mock_timer->GetCurrentDelay();
137 mock_timer->Fire();
138 RunLoop();
139
140 // Give up.
141 ASSERT_FALSE(mock_timer->IsRunning());
142 ASSERT_EQ(1U, dispatched_.size());
143 EXPECT_EQ(1, dispatched_.front());
144 dispatched_.clear();
145 queue_->Cancel(1);
146 ASSERT_FALSE(mock_timer->IsRunning());
147
148 // Try a different task. See the timer remains unchanged because the previous
149 // task was cancelled.
150 ASSERT_TRUE(dispatched_.empty());
151 queue_->AddToQueue(2);
152 ASSERT_TRUE(mock_timer->IsRunning());
153 EXPECT_GE(last_delay, mock_timer->GetCurrentDelay());
154 last_delay = mock_timer->GetCurrentDelay();
155 mock_timer->Fire();
156 RunLoop();
157
158 // Mark this one as succeeding, which will clear the backoff delay.
159 ASSERT_FALSE(mock_timer->IsRunning());
160 ASSERT_EQ(1U, dispatched_.size());
161 EXPECT_EQ(2, dispatched_.front());
162 dispatched_.clear();
163 queue_->MarkAsSucceeded(2);
164 ASSERT_FALSE(mock_timer->IsRunning());
165
166 // Add one last task and see that it's dispatched without delay because the
167 // previous one succeeded.
168 ASSERT_TRUE(dispatched_.empty());
169 queue_->AddToQueue(3);
170 ASSERT_TRUE(mock_timer->IsRunning());
171 EXPECT_LT(mock_timer->GetCurrentDelay(), last_delay);
172 last_delay = mock_timer->GetCurrentDelay();
173 mock_timer->Fire();
174 RunLoop();
175
176 // Clean up.
177 ASSERT_EQ(1U, dispatched_.size());
178 EXPECT_EQ(3, dispatched_.front());
179 dispatched_.clear();
180 queue_->MarkAsSucceeded(3);
181 ASSERT_FALSE(mock_timer->IsRunning());
182 }
183
TEST_F(TaskQueueTest,Cancel)184 TEST_F(TaskQueueTest, Cancel) {
185 queue_->AddToQueue(1);
186 RunLoop();
187
188 ASSERT_EQ(1U, dispatched_.size());
189 EXPECT_EQ(1, dispatched_.front());
190 dispatched_.clear();
191 queue_->Cancel(1);
192 RunLoop();
193
194 ASSERT_TRUE(dispatched_.empty());
195 }
196
197 // See that ResetBackoff resets the backoff delay.
TEST_F(TaskQueueTest,ResetBackoff)198 TEST_F(TaskQueueTest, ResetBackoff) {
199 scoped_ptr<base::MockTimer> timer_to_pass(new base::MockTimer(false, false));
200 base::MockTimer* mock_timer = timer_to_pass.get();
201 queue_->SetTimerForTest(timer_to_pass.PassAs<base::Timer>());
202
203 // Add an item, mark it as failed, re-add it and see that we now have a
204 // backoff delay.
205 queue_->AddToQueue(1);
206 ASSERT_TRUE(mock_timer->IsRunning());
207 ASSERT_EQ(kZero, mock_timer->GetCurrentDelay());
208 mock_timer->Fire();
209 RunLoop();
210 ASSERT_FALSE(mock_timer->IsRunning());
211 ASSERT_EQ(1U, dispatched_.size());
212 EXPECT_EQ(1, dispatched_.front());
213 dispatched_.clear();
214 queue_->MarkAsFailed(1);
215 queue_->AddToQueue(1);
216 ASSERT_TRUE(mock_timer->IsRunning());
217 EXPECT_GT(mock_timer->GetCurrentDelay(), kZero);
218 EXPECT_LE(mock_timer->GetCurrentDelay(), TimeDelta::FromMinutes(1));
219
220 // Call ResetBackoff and see that there is no longer a delay.
221 queue_->ResetBackoff();
222 ASSERT_TRUE(mock_timer->IsRunning());
223 ASSERT_EQ(kZero, mock_timer->GetCurrentDelay());
224 mock_timer->Fire();
225 RunLoop();
226 ASSERT_FALSE(mock_timer->IsRunning());
227 ASSERT_EQ(1U, dispatched_.size());
228 EXPECT_EQ(1, dispatched_.front());
229 dispatched_.clear();
230 queue_->MarkAsSucceeded(1);
231 }
232
233 } // namespace syncer
234