Home | History | Annotate | Download | only in sync_file_system
      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 "chrome/browser/sync_file_system/sync_process_runner.h"
      6 
      7 #include <queue>
      8 
      9 #include "base/memory/scoped_ptr.h"
     10 #include "testing/gtest/include/gtest/gtest.h"
     11 
     12 namespace sync_file_system {
     13 
     14 namespace {
     15 
     16 class FakeClient : public SyncProcessRunner::Client {
     17  public:
     18   FakeClient() : service_state_(SYNC_SERVICE_RUNNING) {}
     19   virtual ~FakeClient() {}
     20 
     21   virtual SyncServiceState GetSyncServiceState() OVERRIDE {
     22     return service_state_;
     23   }
     24 
     25   virtual SyncFileSystemService* GetSyncService() OVERRIDE {
     26     return NULL;
     27   }
     28 
     29   void set_service_state(SyncServiceState service_state) {
     30     service_state_ = service_state;
     31   }
     32 
     33  private:
     34   SyncServiceState service_state_;
     35 
     36   DISALLOW_COPY_AND_ASSIGN(FakeClient);
     37 };
     38 
     39 class FakeTimerHelper : public SyncProcessRunner::TimerHelper {
     40  public:
     41   FakeTimerHelper() {}
     42   virtual ~FakeTimerHelper() {}
     43 
     44   virtual bool IsRunning() OVERRIDE {
     45     return !timer_task_.is_null();
     46   }
     47 
     48   virtual void Start(const tracked_objects::Location& from_here,
     49                      const base::TimeDelta& delay,
     50                      const base::Closure& closure) OVERRIDE {
     51     scheduled_time_ = current_time_ + delay;
     52     timer_task_ = closure;
     53   }
     54 
     55   virtual base::TimeTicks Now() const OVERRIDE {
     56     return current_time_;
     57   }
     58 
     59   void SetCurrentTime(const base::TimeTicks& current_time) {
     60     current_time_ = current_time;
     61     if (current_time_ < scheduled_time_ || timer_task_.is_null())
     62       return;
     63 
     64     base::Closure task = timer_task_;
     65     timer_task_.Reset();
     66     task.Run();
     67   }
     68 
     69   void AdvanceToScheduledTime() {
     70     SetCurrentTime(scheduled_time_);
     71   }
     72 
     73   int64 GetCurrentDelay() {
     74     EXPECT_FALSE(timer_task_.is_null());
     75     return (scheduled_time_ - current_time_).InMilliseconds();
     76   }
     77 
     78  private:
     79   base::TimeTicks current_time_;
     80   base::TimeTicks scheduled_time_;
     81   base::Closure timer_task_;
     82 
     83   DISALLOW_COPY_AND_ASSIGN(FakeTimerHelper);
     84 };
     85 
     86 class FakeSyncProcessRunner : public SyncProcessRunner {
     87  public:
     88   FakeSyncProcessRunner(SyncProcessRunner::Client* client,
     89                         scoped_ptr<TimerHelper> timer_helper,
     90                         size_t max_parallel_task)
     91       : SyncProcessRunner("FakeSyncProcess",
     92                           client, timer_helper.Pass(),
     93                           max_parallel_task),
     94         max_parallel_task_(max_parallel_task) {
     95   }
     96 
     97   virtual void StartSync(const SyncStatusCallback& callback) OVERRIDE {
     98     EXPECT_LT(running_tasks_.size(), max_parallel_task_);
     99     running_tasks_.push(callback);
    100   }
    101 
    102   virtual ~FakeSyncProcessRunner() {
    103   }
    104 
    105   void UpdateChanges(int num_changes) {
    106     OnChangesUpdated(num_changes);
    107   }
    108 
    109   void CompleteTask(SyncStatusCode status) {
    110     ASSERT_FALSE(running_tasks_.empty());
    111     SyncStatusCallback task = running_tasks_.front();
    112     running_tasks_.pop();
    113     task.Run(status);
    114   }
    115 
    116   bool HasRunningTask() const {
    117     return !running_tasks_.empty();
    118   }
    119 
    120  private:
    121   size_t max_parallel_task_;
    122   std::queue<SyncStatusCallback> running_tasks_;
    123 
    124   DISALLOW_COPY_AND_ASSIGN(FakeSyncProcessRunner);
    125 };
    126 
    127 }  // namespace
    128 
    129 TEST(SyncProcessRunnerTest, SingleTaskBasicTest) {
    130   FakeClient fake_client;
    131   FakeTimerHelper* fake_timer = new FakeTimerHelper();
    132   FakeSyncProcessRunner fake_runner(
    133       &fake_client,
    134       scoped_ptr<SyncProcessRunner::TimerHelper>(fake_timer),
    135       1 /* max_parallel_task */);
    136 
    137   base::TimeTicks base_time = base::TimeTicks::Now();
    138   fake_timer->SetCurrentTime(base_time);
    139 
    140   // SyncProcessRunner is expected not to run a task  initially.
    141   EXPECT_FALSE(fake_timer->IsRunning());
    142 
    143   // As soon as SyncProcessRunner gets a new update, it should start running
    144   // the timer to run a synchronization task.
    145   fake_runner.UpdateChanges(100);
    146   EXPECT_TRUE(fake_timer->IsRunning());
    147   EXPECT_EQ(SyncProcessRunner::kSyncDelayFastInMilliseconds,
    148             fake_timer->GetCurrentDelay());
    149 
    150   // When the time has come, the timer should fire the scheduled task.
    151   fake_timer->AdvanceToScheduledTime();
    152   EXPECT_FALSE(fake_timer->IsRunning());
    153   EXPECT_TRUE(fake_runner.HasRunningTask());
    154 
    155   // Successful completion of the task fires next synchronization task.
    156   fake_runner.CompleteTask(SYNC_STATUS_OK);
    157   EXPECT_TRUE(fake_timer->IsRunning());
    158   EXPECT_FALSE(fake_runner.HasRunningTask());
    159   EXPECT_EQ(SyncProcessRunner::kSyncDelayFastInMilliseconds,
    160             fake_timer->GetCurrentDelay());
    161 
    162   // Turn |service_state| to TEMPORARY_UNAVAILABLE and let the task fail.
    163   // |fake_runner| should schedule following tasks with longer delay.
    164   fake_timer->AdvanceToScheduledTime();
    165   fake_client.set_service_state(SYNC_SERVICE_TEMPORARY_UNAVAILABLE);
    166   fake_runner.CompleteTask(SYNC_STATUS_FAILED);
    167   EXPECT_EQ(SyncProcessRunner::kSyncDelaySlowInMilliseconds,
    168             fake_timer->GetCurrentDelay());
    169 
    170   // Repeated failure makes the task delay back off.
    171   fake_timer->AdvanceToScheduledTime();
    172   fake_runner.CompleteTask(SYNC_STATUS_FAILED);
    173   EXPECT_EQ(2 * SyncProcessRunner::kSyncDelaySlowInMilliseconds,
    174             fake_timer->GetCurrentDelay());
    175 
    176   // After |service_state| gets back to normal state, SyncProcessRunner should
    177   // restart rapid task invocation.
    178   fake_client.set_service_state(SYNC_SERVICE_RUNNING);
    179   fake_timer->AdvanceToScheduledTime();
    180   fake_runner.CompleteTask(SYNC_STATUS_OK);
    181   EXPECT_EQ(SyncProcessRunner::kSyncDelayFastInMilliseconds,
    182             fake_timer->GetCurrentDelay());
    183 
    184   // There's no item to sync anymore, SyncProcessRunner should schedules the
    185   // next with the longest delay.
    186   fake_runner.UpdateChanges(0);
    187   fake_timer->AdvanceToScheduledTime();
    188   fake_runner.CompleteTask(SYNC_STATUS_OK);
    189   EXPECT_EQ(SyncProcessRunner::kSyncDelayMaxInMilliseconds,
    190             fake_timer->GetCurrentDelay());
    191 
    192   // Schedule the next with the longest delay if the client is persistently
    193   // unavailable.
    194   fake_client.set_service_state(SYNC_SERVICE_AUTHENTICATION_REQUIRED);
    195   fake_runner.UpdateChanges(100);
    196   EXPECT_EQ(SyncProcessRunner::kSyncDelayMaxInMilliseconds,
    197             fake_timer->GetCurrentDelay());
    198 }
    199 
    200 TEST(SyncProcessRunnerTest, MultiTaskBasicTest) {
    201   FakeClient fake_client;
    202   FakeTimerHelper* fake_timer = new FakeTimerHelper();
    203   FakeSyncProcessRunner fake_runner(
    204       &fake_client,
    205       scoped_ptr<SyncProcessRunner::TimerHelper>(fake_timer),
    206       2 /* max_parallel_task */);
    207 
    208   base::TimeTicks base_time = base::TimeTicks::Now();
    209   fake_timer->SetCurrentTime(base_time);
    210 
    211   EXPECT_FALSE(fake_timer->IsRunning());
    212 
    213   fake_runner.UpdateChanges(100);
    214   EXPECT_TRUE(fake_timer->IsRunning());
    215   EXPECT_EQ(SyncProcessRunner::kSyncDelayFastInMilliseconds,
    216             fake_timer->GetCurrentDelay());
    217 
    218   // Even after a task starts running, SyncProcessRunner should schedule next
    219   // task until the number of running task reachs the limit.
    220   fake_timer->AdvanceToScheduledTime();
    221   EXPECT_TRUE(fake_timer->IsRunning());
    222   EXPECT_TRUE(fake_runner.HasRunningTask());
    223   EXPECT_EQ(SyncProcessRunner::kSyncDelayFastInMilliseconds,
    224             fake_timer->GetCurrentDelay());
    225 
    226   // After the second task starts running, SyncProcessRunner should stop
    227   // scheduling a task.
    228   fake_timer->AdvanceToScheduledTime();
    229   EXPECT_FALSE(fake_timer->IsRunning());
    230   EXPECT_TRUE(fake_runner.HasRunningTask());
    231 
    232   fake_runner.CompleteTask(SYNC_STATUS_OK);
    233   EXPECT_TRUE(fake_timer->IsRunning());
    234   EXPECT_TRUE(fake_runner.HasRunningTask());
    235   fake_runner.CompleteTask(SYNC_STATUS_OK);
    236   EXPECT_TRUE(fake_timer->IsRunning());
    237   EXPECT_FALSE(fake_runner.HasRunningTask());
    238 
    239   // Turn |service_state| to TEMPORARY_UNAVAILABLE and let the task fail.
    240   // |fake_runner| should schedule following tasks with longer delay.
    241   fake_timer->AdvanceToScheduledTime();
    242   fake_timer->AdvanceToScheduledTime();
    243   fake_client.set_service_state(SYNC_SERVICE_TEMPORARY_UNAVAILABLE);
    244   fake_runner.CompleteTask(SYNC_STATUS_FAILED);
    245   EXPECT_EQ(SyncProcessRunner::kSyncDelaySlowInMilliseconds,
    246             fake_timer->GetCurrentDelay());
    247 
    248   // Consecutive error reports shouldn't extend delay immediately.
    249   fake_runner.CompleteTask(SYNC_STATUS_FAILED);
    250   EXPECT_EQ(SyncProcessRunner::kSyncDelaySlowInMilliseconds,
    251             fake_timer->GetCurrentDelay());
    252 
    253   // The next task will run after throttle period is over.
    254   // And its failure should extend the throttle period by twice.
    255   fake_timer->AdvanceToScheduledTime();
    256   EXPECT_EQ(SyncProcessRunner::kSyncDelaySlowInMilliseconds,
    257             fake_timer->GetCurrentDelay());
    258   fake_runner.CompleteTask(SYNC_STATUS_FAILED);
    259   EXPECT_EQ(2 * SyncProcessRunner::kSyncDelaySlowInMilliseconds,
    260             fake_timer->GetCurrentDelay());
    261 
    262   // Next successful task should clear the throttling.
    263   fake_timer->AdvanceToScheduledTime();
    264   fake_client.set_service_state(SYNC_SERVICE_RUNNING);
    265   fake_runner.CompleteTask(SYNC_STATUS_OK);
    266   EXPECT_EQ(SyncProcessRunner::kSyncDelayFastInMilliseconds,
    267             fake_timer->GetCurrentDelay());
    268 
    269   // Then, following failing task should not extend throttling period.
    270   fake_timer->AdvanceToScheduledTime();
    271   fake_client.set_service_state(SYNC_SERVICE_TEMPORARY_UNAVAILABLE);
    272   fake_runner.CompleteTask(SYNC_STATUS_FAILED);
    273   EXPECT_EQ(SyncProcessRunner::kSyncDelaySlowInMilliseconds,
    274             fake_timer->GetCurrentDelay());
    275 }
    276 
    277 }  // namespace sync_file_system
    278