sync_task_manager_unittest.cc revision effb81e5f8246d0db0270817048dc992db66e9fb
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 "base/basictypes.h"
6#include "base/bind.h"
7#include "base/memory/weak_ptr.h"
8#include "base/message_loop/message_loop.h"
9#include "chrome/browser/sync_file_system/drive_backend/sync_task_manager.h"
10#include "chrome/browser/sync_file_system/drive_backend/sync_task_token.h"
11#include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
12#include "testing/gtest/include/gtest/gtest.h"
13#include "webkit/common/fileapi/file_system_util.h"
14
15#define MAKE_PATH(path)                                       \
16  base::FilePath(fileapi::VirtualPath::GetNormalizedFilePath( \
17      base::FilePath(FILE_PATH_LITERAL(path))))
18
19namespace sync_file_system {
20namespace drive_backend {
21
22namespace {
23
24void DumbTask(SyncStatusCode status,
25              const SyncStatusCallback& callback) {
26  base::MessageLoop::current()->PostTask(
27      FROM_HERE, base::Bind(callback, status));
28}
29
30void IncrementAndAssign(int expected_before_counter,
31                        int* counter,
32                        SyncStatusCode* status_out,
33                        SyncStatusCode status) {
34  EXPECT_EQ(expected_before_counter, *counter);
35  ++(*counter);
36  *status_out = status;
37}
38
39template <typename T>
40void IncrementAndAssignWithOwnedPointer(T* object,
41                                        int* counter,
42                                        SyncStatusCode* status_out,
43                                        SyncStatusCode status) {
44  ++(*counter);
45  *status_out = status;
46}
47
48class TaskManagerClient
49    : public SyncTaskManager::Client,
50      public base::SupportsWeakPtr<TaskManagerClient> {
51 public:
52  explicit TaskManagerClient(int64 maximum_background_task)
53      : maybe_schedule_next_task_count_(0),
54        task_scheduled_count_(0),
55        idle_task_scheduled_count_(0),
56        last_operation_status_(SYNC_STATUS_OK) {
57    task_manager_.reset(new SyncTaskManager(
58        AsWeakPtr(), maximum_background_task));
59    task_manager_->Initialize(SYNC_STATUS_OK);
60    maybe_schedule_next_task_count_ = 0;
61  }
62  virtual ~TaskManagerClient() {}
63
64  // DriveFileSyncManager::Client overrides.
65  virtual void MaybeScheduleNextTask() OVERRIDE {
66    ++maybe_schedule_next_task_count_;
67  }
68  virtual void NotifyLastOperationStatus(
69      SyncStatusCode last_operation_status,
70      bool last_operation_used_network) OVERRIDE {
71    last_operation_status_ = last_operation_status;
72  }
73
74  void ScheduleTask(SyncStatusCode status_to_return,
75                    const SyncStatusCallback& callback) {
76    task_manager_->ScheduleTask(
77        FROM_HERE,
78        base::Bind(&TaskManagerClient::DoTask, AsWeakPtr(),
79                   status_to_return, false /* idle */),
80        SyncTaskManager::PRIORITY_MED,
81        callback);
82  }
83
84  void ScheduleTaskIfIdle(SyncStatusCode status_to_return) {
85    task_manager_->ScheduleTaskIfIdle(
86        FROM_HERE,
87        base::Bind(&TaskManagerClient::DoTask, AsWeakPtr(),
88                   status_to_return, true /* idle */),
89        SyncStatusCallback());
90  }
91
92  int maybe_schedule_next_task_count() const {
93    return maybe_schedule_next_task_count_;
94  }
95  int task_scheduled_count() const { return task_scheduled_count_; }
96  int idle_task_scheduled_count() const { return idle_task_scheduled_count_; }
97  SyncStatusCode last_operation_status() const {
98    return last_operation_status_;
99  }
100
101 private:
102  void DoTask(SyncStatusCode status_to_return,
103              bool is_idle_task,
104              const SyncStatusCallback& callback) {
105    ++task_scheduled_count_;
106    if (is_idle_task)
107      ++idle_task_scheduled_count_;
108    base::MessageLoop::current()->PostTask(
109        FROM_HERE, base::Bind(callback, status_to_return));
110  }
111
112  scoped_ptr<SyncTaskManager> task_manager_;
113
114  int maybe_schedule_next_task_count_;
115  int task_scheduled_count_;
116  int idle_task_scheduled_count_;
117
118  SyncStatusCode last_operation_status_;
119
120  DISALLOW_COPY_AND_ASSIGN(TaskManagerClient);
121};
122
123class MultihopSyncTask : public SequentialSyncTask {
124 public:
125  MultihopSyncTask(bool* task_started,
126                   bool* task_completed)
127      : task_started_(task_started),
128        task_completed_(task_completed),
129        weak_ptr_factory_(this) {
130    DCHECK(task_started_);
131    DCHECK(task_completed_);
132  }
133
134  virtual ~MultihopSyncTask() {}
135
136  virtual void RunSequential(const SyncStatusCallback& callback) OVERRIDE {
137    DCHECK(!*task_started_);
138    *task_started_ = true;
139    base::MessageLoop::current()->PostTask(
140        FROM_HERE, base::Bind(&MultihopSyncTask::CompleteTask,
141                              weak_ptr_factory_.GetWeakPtr(), callback));
142  }
143
144 private:
145  void CompleteTask(const SyncStatusCallback& callback) {
146    DCHECK(*task_started_);
147    DCHECK(!*task_completed_);
148    *task_completed_ = true;
149    callback.Run(SYNC_STATUS_OK);
150  }
151
152  bool* task_started_;
153  bool* task_completed_;
154  base::WeakPtrFactory<MultihopSyncTask> weak_ptr_factory_;
155
156  DISALLOW_COPY_AND_ASSIGN(MultihopSyncTask);
157};
158
159class BackgroundTask : public SyncTask {
160 public:
161  struct Stats {
162    int64 running_background_task;
163    int64 finished_task;
164    int64 max_parallel_task;
165
166    Stats()
167        : running_background_task(0),
168          finished_task(0),
169          max_parallel_task(0) {}
170  };
171
172  BackgroundTask(const std::string& app_id,
173                 const base::FilePath& path,
174                 Stats* stats)
175      : app_id_(app_id),
176        path_(path),
177        stats_(stats),
178        weak_ptr_factory_(this) {
179  }
180
181  virtual ~BackgroundTask() {
182  }
183
184  virtual void Run(scoped_ptr<SyncTaskToken> token) OVERRIDE {
185    scoped_ptr<BlockingFactor> blocking_factor(new BlockingFactor);
186    blocking_factor->app_id = app_id_;
187    blocking_factor->paths.push_back(path_);
188
189    SyncTaskManager::MoveTaskToBackground(
190        token.Pass(), blocking_factor.Pass(),
191        base::Bind(&BackgroundTask::RunAsBackgroundTask,
192                   weak_ptr_factory_.GetWeakPtr()));
193  }
194
195 private:
196  void RunAsBackgroundTask(scoped_ptr<SyncTaskToken> token) {
197    ++(stats_->running_background_task);
198    if (stats_->max_parallel_task < stats_->running_background_task)
199      stats_->max_parallel_task = stats_->running_background_task;
200
201    base::MessageLoop::current()->PostTask(
202        FROM_HERE,
203        base::Bind(&BackgroundTask::CompleteTask,
204                   weak_ptr_factory_.GetWeakPtr(),
205                   base::Passed(&token)));
206  }
207
208  void CompleteTask(scoped_ptr<SyncTaskToken> token) {
209    ++(stats_->finished_task);
210    --(stats_->running_background_task);
211    SyncTaskManager::NotifyTaskDone(token.Pass(), SYNC_STATUS_OK);
212  }
213
214  std::string app_id_;
215  base::FilePath path_;
216  Stats* stats_;
217
218  base::WeakPtrFactory<BackgroundTask> weak_ptr_factory_;
219
220  DISALLOW_COPY_AND_ASSIGN(BackgroundTask);
221};
222
223// Arbitrary non-default status values for testing.
224const SyncStatusCode kStatus1 = static_cast<SyncStatusCode>(-1);
225const SyncStatusCode kStatus2 = static_cast<SyncStatusCode>(-2);
226const SyncStatusCode kStatus3 = static_cast<SyncStatusCode>(-3);
227const SyncStatusCode kStatus4 = static_cast<SyncStatusCode>(-4);
228const SyncStatusCode kStatus5 = static_cast<SyncStatusCode>(-5);
229
230}  // namespace
231
232TEST(SyncTaskManagerTest, ScheduleTask) {
233  base::MessageLoop message_loop;
234  TaskManagerClient client(0 /* maximum_background_task */);
235  int callback_count = 0;
236  SyncStatusCode callback_status = SYNC_STATUS_OK;
237
238  client.ScheduleTask(kStatus1, base::Bind(&IncrementAndAssign, 0,
239                                           &callback_count,
240                                           &callback_status));
241  message_loop.RunUntilIdle();
242
243  EXPECT_EQ(kStatus1, callback_status);
244  EXPECT_EQ(kStatus1, client.last_operation_status());
245
246  EXPECT_EQ(1, callback_count);
247  EXPECT_EQ(1, client.maybe_schedule_next_task_count());
248  EXPECT_EQ(1, client.task_scheduled_count());
249  EXPECT_EQ(0, client.idle_task_scheduled_count());
250}
251
252TEST(SyncTaskManagerTest, ScheduleTwoTasks) {
253  base::MessageLoop message_loop;
254  TaskManagerClient client(0 /* maximum_background_task */);
255  int callback_count = 0;
256  SyncStatusCode callback_status = SYNC_STATUS_OK;
257
258  client.ScheduleTask(kStatus1, base::Bind(&IncrementAndAssign, 0,
259                                           &callback_count,
260                                           &callback_status));
261  client.ScheduleTask(kStatus2, base::Bind(&IncrementAndAssign, 1,
262                                           &callback_count,
263                                           &callback_status));
264  message_loop.RunUntilIdle();
265
266  EXPECT_EQ(kStatus2, callback_status);
267  EXPECT_EQ(kStatus2, client.last_operation_status());
268
269  EXPECT_EQ(2, callback_count);
270  EXPECT_EQ(1, client.maybe_schedule_next_task_count());
271  EXPECT_EQ(2, client.task_scheduled_count());
272  EXPECT_EQ(0, client.idle_task_scheduled_count());
273}
274
275TEST(SyncTaskManagerTest, ScheduleIdleTask) {
276  base::MessageLoop message_loop;
277  TaskManagerClient client(0 /* maximum_background_task */);
278
279  client.ScheduleTaskIfIdle(kStatus1);
280  message_loop.RunUntilIdle();
281
282  EXPECT_EQ(kStatus1, client.last_operation_status());
283
284  EXPECT_EQ(1, client.maybe_schedule_next_task_count());
285  EXPECT_EQ(1, client.task_scheduled_count());
286  EXPECT_EQ(1, client.idle_task_scheduled_count());
287}
288
289TEST(SyncTaskManagerTest, ScheduleIdleTaskWhileNotIdle) {
290  base::MessageLoop message_loop;
291  TaskManagerClient client(0 /* maximum_background_task */);
292  int callback_count = 0;
293  SyncStatusCode callback_status = SYNC_STATUS_OK;
294
295  client.ScheduleTask(kStatus1, base::Bind(&IncrementAndAssign, 0,
296                                           &callback_count,
297                                           &callback_status));
298  client.ScheduleTaskIfIdle(kStatus2);
299  message_loop.RunUntilIdle();
300
301  // Idle task must not have run.
302  EXPECT_EQ(kStatus1, callback_status);
303  EXPECT_EQ(kStatus1, client.last_operation_status());
304
305  EXPECT_EQ(1, callback_count);
306  EXPECT_EQ(1, client.maybe_schedule_next_task_count());
307  EXPECT_EQ(1, client.task_scheduled_count());
308  EXPECT_EQ(0, client.idle_task_scheduled_count());
309}
310
311TEST(SyncTaskManagerTest, ScheduleAndCancelSyncTask) {
312  base::MessageLoop message_loop;
313
314  int callback_count = 0;
315  SyncStatusCode status = SYNC_STATUS_UNKNOWN;
316
317  bool task_started = false;
318  bool task_completed = false;
319
320  {
321    SyncTaskManager task_manager(base::WeakPtr<SyncTaskManager::Client>(),
322                                 0 /* maximum_background_task */);
323    task_manager.Initialize(SYNC_STATUS_OK);
324    task_manager.ScheduleSyncTask(
325        FROM_HERE,
326        scoped_ptr<SyncTask>(new MultihopSyncTask(
327            &task_started, &task_completed)),
328        SyncTaskManager::PRIORITY_MED,
329        base::Bind(&IncrementAndAssign, 0, &callback_count, &status));
330  }
331
332  message_loop.RunUntilIdle();
333  EXPECT_EQ(0, callback_count);
334  EXPECT_EQ(SYNC_STATUS_UNKNOWN, status);
335  EXPECT_TRUE(task_started);
336  EXPECT_FALSE(task_completed);
337}
338
339TEST(SyncTaskManagerTest, ScheduleTaskAtPriority) {
340  base::MessageLoop message_loop;
341  SyncTaskManager task_manager(base::WeakPtr<SyncTaskManager::Client>(),
342                               0 /* maximum_background_task */);
343  task_manager.Initialize(SYNC_STATUS_OK);
344
345  int callback_count = 0;
346  SyncStatusCode callback_status1 = SYNC_STATUS_OK;
347  SyncStatusCode callback_status2 = SYNC_STATUS_OK;
348  SyncStatusCode callback_status3 = SYNC_STATUS_OK;
349  SyncStatusCode callback_status4 = SYNC_STATUS_OK;
350  SyncStatusCode callback_status5 = SYNC_STATUS_OK;
351
352  // This will run first even if its priority is low, since there're no
353  // pending tasks.
354  task_manager.ScheduleTask(
355      FROM_HERE,
356      base::Bind(&DumbTask, kStatus1),
357      SyncTaskManager::PRIORITY_LOW,
358      base::Bind(&IncrementAndAssign, 0, &callback_count, &callback_status1));
359
360  // This runs last (expected counter == 4).
361  task_manager.ScheduleTask(
362      FROM_HERE,
363      base::Bind(&DumbTask, kStatus2),
364      SyncTaskManager::PRIORITY_LOW,
365      base::Bind(&IncrementAndAssign, 4, &callback_count, &callback_status2));
366
367  // This runs second (expected counter == 1).
368  task_manager.ScheduleTask(
369      FROM_HERE,
370      base::Bind(&DumbTask, kStatus3),
371      SyncTaskManager::PRIORITY_HIGH,
372      base::Bind(&IncrementAndAssign, 1, &callback_count, &callback_status3));
373
374  // This runs fourth (expected counter == 3).
375  task_manager.ScheduleTask(
376      FROM_HERE,
377      base::Bind(&DumbTask, kStatus4),
378      SyncTaskManager::PRIORITY_MED,
379      base::Bind(&IncrementAndAssign, 3, &callback_count, &callback_status4));
380
381  // This runs third (expected counter == 2).
382  task_manager.ScheduleTask(
383      FROM_HERE,
384      base::Bind(&DumbTask, kStatus5),
385      SyncTaskManager::PRIORITY_HIGH,
386      base::Bind(&IncrementAndAssign, 2, &callback_count, &callback_status5));
387
388  message_loop.RunUntilIdle();
389
390  EXPECT_EQ(kStatus1, callback_status1);
391  EXPECT_EQ(kStatus2, callback_status2);
392  EXPECT_EQ(kStatus3, callback_status3);
393  EXPECT_EQ(kStatus4, callback_status4);
394  EXPECT_EQ(kStatus5, callback_status5);
395  EXPECT_EQ(5, callback_count);
396}
397
398TEST(SyncTaskManagerTest, BackgroundTask_Sequential) {
399  base::MessageLoop message_loop;
400  SyncTaskManager task_manager(base::WeakPtr<SyncTaskManager::Client>(),
401                               10 /* maximum_background_task */);
402  task_manager.Initialize(SYNC_STATUS_OK);
403
404  SyncStatusCode status = SYNC_STATUS_FAILED;
405  BackgroundTask::Stats stats;
406  task_manager.ScheduleSyncTask(
407      FROM_HERE,
408      scoped_ptr<SyncTask>(new BackgroundTask(
409          "app_id", MAKE_PATH("/hoge/fuga"),
410          &stats)),
411      SyncTaskManager::PRIORITY_MED,
412      CreateResultReceiver(&status));
413
414  task_manager.ScheduleSyncTask(
415      FROM_HERE,
416      scoped_ptr<SyncTask>(new BackgroundTask(
417          "app_id", MAKE_PATH("/hoge"),
418          &stats)),
419      SyncTaskManager::PRIORITY_MED,
420      CreateResultReceiver(&status));
421
422  task_manager.ScheduleSyncTask(
423      FROM_HERE,
424      scoped_ptr<SyncTask>(new BackgroundTask(
425          "app_id", MAKE_PATH("/hoge/fuga/piyo"),
426          &stats)),
427      SyncTaskManager::PRIORITY_MED,
428      CreateResultReceiver(&status));
429
430  message_loop.RunUntilIdle();
431
432  EXPECT_EQ(SYNC_STATUS_OK, status);
433  EXPECT_EQ(0, stats.running_background_task);
434  EXPECT_EQ(3, stats.finished_task);
435  EXPECT_EQ(1, stats.max_parallel_task);
436}
437
438TEST(SyncTaskManagerTest, BackgroundTask_Parallel) {
439  base::MessageLoop message_loop;
440  SyncTaskManager task_manager(base::WeakPtr<SyncTaskManager::Client>(),
441                               10 /* maximum_background_task */);
442  task_manager.Initialize(SYNC_STATUS_OK);
443
444  SyncStatusCode status = SYNC_STATUS_FAILED;
445  BackgroundTask::Stats stats;
446  task_manager.ScheduleSyncTask(
447      FROM_HERE,
448      scoped_ptr<SyncTask>(new BackgroundTask(
449          "app_id", MAKE_PATH("/hoge"),
450          &stats)),
451      SyncTaskManager::PRIORITY_MED,
452      CreateResultReceiver(&status));
453
454  task_manager.ScheduleSyncTask(
455      FROM_HERE,
456      scoped_ptr<SyncTask>(new BackgroundTask(
457          "app_id", MAKE_PATH("/fuga"),
458          &stats)),
459      SyncTaskManager::PRIORITY_MED,
460      CreateResultReceiver(&status));
461
462  task_manager.ScheduleSyncTask(
463      FROM_HERE,
464      scoped_ptr<SyncTask>(new BackgroundTask(
465          "app_id", MAKE_PATH("/piyo"),
466          &stats)),
467      SyncTaskManager::PRIORITY_MED,
468      CreateResultReceiver(&status));
469
470  message_loop.RunUntilIdle();
471
472  EXPECT_EQ(SYNC_STATUS_OK, status);
473  EXPECT_EQ(0, stats.running_background_task);
474  EXPECT_EQ(3, stats.finished_task);
475  EXPECT_EQ(3, stats.max_parallel_task);
476}
477
478TEST(SyncTaskManagerTest, BackgroundTask_Throttled) {
479  base::MessageLoop message_loop;
480  SyncTaskManager task_manager(base::WeakPtr<SyncTaskManager::Client>(),
481                               2 /* maximum_background_task */);
482  task_manager.Initialize(SYNC_STATUS_OK);
483
484  SyncStatusCode status = SYNC_STATUS_FAILED;
485  BackgroundTask::Stats stats;
486  task_manager.ScheduleSyncTask(
487      FROM_HERE,
488      scoped_ptr<SyncTask>(new BackgroundTask(
489          "app_id", MAKE_PATH("/hoge"),
490          &stats)),
491      SyncTaskManager::PRIORITY_MED,
492      CreateResultReceiver(&status));
493
494  task_manager.ScheduleSyncTask(
495      FROM_HERE,
496      scoped_ptr<SyncTask>(new BackgroundTask(
497          "app_id", MAKE_PATH("/fuga"),
498          &stats)),
499      SyncTaskManager::PRIORITY_MED,
500      CreateResultReceiver(&status));
501
502  task_manager.ScheduleSyncTask(
503      FROM_HERE,
504      scoped_ptr<SyncTask>(new BackgroundTask(
505          "app_id", MAKE_PATH("/piyo"),
506          &stats)),
507      SyncTaskManager::PRIORITY_MED,
508      CreateResultReceiver(&status));
509
510  message_loop.RunUntilIdle();
511
512  EXPECT_EQ(SYNC_STATUS_OK, status);
513  EXPECT_EQ(0, stats.running_background_task);
514  EXPECT_EQ(3, stats.finished_task);
515  EXPECT_EQ(2, stats.max_parallel_task);
516}
517
518}  // namespace drive_backend
519}  // namespace sync_file_system
520