• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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