Home | History | Annotate | Download | only in attachments
      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:
     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.
     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.
     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.
    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 
    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.
    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