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
16using base::TimeDelta;
17
18namespace syncer {
19
20namespace {
21
22const TimeDelta kZero;
23
24}  // namespace
25
26class TaskQueueTest : public testing::Test {
27 protected:
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
35  void RunLoop() {
36    base::RunLoop run_loop;
37    run_loop.RunUntilIdle();
38  }
39
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.
49TEST_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.
76TEST_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.
100TEST_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
184TEST_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.
198TEST_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