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