sync_file_system_service_unittest.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2012 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 <vector>
6
7#include "base/basictypes.h"
8#include "base/bind.h"
9#include "base/run_loop.h"
10#include "base/stl_util.h"
11#include "base/synchronization/waitable_event.h"
12#include "chrome/browser/sync_file_system/local_file_sync_service.h"
13#include "chrome/browser/sync_file_system/mock_remote_file_sync_service.h"
14#include "chrome/browser/sync_file_system/sync_event_observer.h"
15#include "chrome/browser/sync_file_system/sync_file_system_service.h"
16#include "chrome/browser/sync_file_system/sync_file_system_test_util.h"
17#include "chrome/test/base/testing_profile.h"
18#include "testing/gtest/include/gtest/gtest.h"
19#include "webkit/browser/fileapi/file_system_context.h"
20#include "webkit/browser/fileapi/syncable/canned_syncable_file_system.h"
21#include "webkit/browser/fileapi/syncable/local_file_sync_context.h"
22#include "webkit/browser/fileapi/syncable/mock_sync_status_observer.h"
23#include "webkit/browser/fileapi/syncable/sync_callbacks.h"
24#include "webkit/browser/fileapi/syncable/sync_file_metadata.h"
25#include "webkit/browser/fileapi/syncable/sync_status_code.h"
26#include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
27
28using fileapi::FileSystemURL;
29using fileapi::FileSystemURLSet;
30using ::testing::AnyNumber;
31using ::testing::AtLeast;
32using ::testing::InSequence;
33using ::testing::InvokeWithoutArgs;
34using ::testing::Return;
35using ::testing::StrictMock;
36using ::testing::_;
37
38namespace sync_file_system {
39
40namespace {
41
42const char kOrigin[] = "http://example.com";
43
44template <typename R> struct AssignTrait {
45  typedef const R& ArgumentType;
46};
47
48template <> struct AssignTrait<SyncFileStatus> {
49  typedef SyncFileStatus ArgumentType;
50};
51
52template <typename R>
53void AssignValueAndQuit(base::RunLoop* run_loop,
54                        SyncStatusCode* status_out, R* value_out,
55                        SyncStatusCode status,
56                        typename AssignTrait<R>::ArgumentType value) {
57  DCHECK(status_out);
58  DCHECK(value_out);
59  DCHECK(run_loop);
60  *status_out = status;
61  *value_out = value;
62  run_loop->Quit();
63}
64
65// This is called on IO thread.
66void VerifyFileError(base::WaitableEvent* event,
67                     base::PlatformFileError error) {
68  DCHECK(event);
69  EXPECT_EQ(base::PLATFORM_FILE_OK, error);
70  event->Signal();
71}
72
73}  // namespace
74
75class MockSyncEventObserver : public SyncEventObserver {
76 public:
77  MockSyncEventObserver() {}
78  virtual ~MockSyncEventObserver() {}
79
80  MOCK_METHOD3(OnSyncStateUpdated,
81               void(const GURL& app_origin,
82                    SyncServiceState state,
83                    const std::string& description));
84  MOCK_METHOD4(OnFileSynced,
85               void(const fileapi::FileSystemURL& url,
86                    SyncFileStatus status,
87                    SyncAction action,
88                    SyncDirection direction));
89};
90
91ACTION_P3(NotifyStateAndCallback,
92          mock_remote_service, service_state, operation_status) {
93  mock_remote_service->NotifyRemoteServiceStateUpdated(
94      service_state, "Test event.");
95  base::MessageLoopProxy::current()->PostTask(
96      FROM_HERE, base::Bind(arg1, operation_status));
97}
98
99ACTION_P(RecordState, states) {
100  states->push_back(arg1);
101}
102
103ACTION_P(MockStatusCallback, status) {
104  base::MessageLoopProxy::current()->PostTask(
105      FROM_HERE, base::Bind(arg4, status));
106}
107
108ACTION_P2(MockSyncFileCallback, status, url) {
109  base::MessageLoopProxy::current()->PostTask(
110      FROM_HERE, base::Bind(arg0, status, url));
111}
112
113class SyncFileSystemServiceTest : public testing::Test {
114 protected:
115  SyncFileSystemServiceTest() {}
116  virtual ~SyncFileSystemServiceTest() {}
117
118  virtual void SetUp() OVERRIDE {
119    thread_helper_.SetUp();
120
121    file_system_.reset(new CannedSyncableFileSystem(
122        GURL(kOrigin),
123        thread_helper_.io_task_runner(),
124        thread_helper_.file_task_runner()));
125
126    local_service_ = new LocalFileSyncService(&profile_);
127    remote_service_ = new StrictMock<MockRemoteFileSyncService>;
128    sync_service_.reset(new SyncFileSystemService(&profile_));
129
130    EXPECT_CALL(*mock_remote_service(),
131                AddServiceObserver(sync_service_.get())).Times(1);
132    EXPECT_CALL(*mock_remote_service(),
133                AddFileStatusObserver(sync_service_.get())).Times(1);
134    EXPECT_CALL(*mock_remote_service(),
135                GetLocalChangeProcessor())
136        .WillOnce(Return(&local_change_processor_));
137    EXPECT_CALL(*mock_remote_service(),
138                SetRemoteChangeProcessor(local_service_)).Times(1);
139
140    sync_service_->Initialize(
141        make_scoped_ptr(local_service_),
142        scoped_ptr<RemoteFileSyncService>(remote_service_));
143
144    // Disable auto sync by default.
145    EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(false)).Times(1);
146    sync_service_->SetSyncEnabledForTesting(false);
147
148    file_system_->SetUp();
149  }
150
151  virtual void TearDown() OVERRIDE {
152    sync_service_->Shutdown();
153
154    file_system_->TearDown();
155    RevokeSyncableFileSystem();
156    thread_helper_.TearDown();
157  }
158
159  void InitializeApp() {
160    base::RunLoop run_loop;
161    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
162
163    EXPECT_CALL(*mock_remote_service(),
164                RegisterOriginForTrackingChanges(GURL(kOrigin), _)).Times(1);
165
166    sync_service_->InitializeForApp(
167        file_system_->file_system_context(),
168        GURL(kOrigin),
169        AssignAndQuitCallback(&run_loop, &status));
170    run_loop.Run();
171
172    EXPECT_EQ(SYNC_STATUS_OK, status);
173    EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->OpenFileSystem());
174  }
175
176  // Calls InitializeForApp after setting up the mock remote service to
177  // perform following when RegisterOriginForTrackingChanges is called:
178  //  1. Notify RemoteFileSyncService's observers of |state_to_notify|
179  //  2. Run the given callback with |status_to_return|.
180  //
181  // ..and verifies if following conditions are met:
182  //  1. The SyncEventObserver of the service is called with
183  //     |expected_states| service state values.
184  //  2. InitializeForApp's callback is called with |expected_status|
185  //  3. GetCurrentState() is called at least |expected_current_state_calls|
186  //     times (which means that the sync service tried to start sync).
187  void InitializeAppForObserverTest(
188      RemoteServiceState state_to_notify,
189      SyncStatusCode status_to_return,
190      const std::vector<SyncServiceState> expected_states,
191      SyncStatusCode expected_status) {
192    StrictMock<MockSyncEventObserver> event_observer;
193    sync_service_->AddSyncEventObserver(&event_observer);
194
195    EnableSync();
196
197    EXPECT_CALL(*mock_remote_service(),
198                RegisterOriginForTrackingChanges(GURL(kOrigin), _))
199        .WillOnce(NotifyStateAndCallback(mock_remote_service(),
200                                         state_to_notify,
201                                         status_to_return));
202
203    std::vector<SyncServiceState> actual_states;
204    EXPECT_CALL(event_observer, OnSyncStateUpdated(GURL(), _, _))
205        .WillRepeatedly(RecordState(&actual_states));
206
207    SyncStatusCode actual_status = SYNC_STATUS_UNKNOWN;
208    base::RunLoop run_loop;
209    sync_service_->InitializeForApp(
210        file_system_->file_system_context(),
211        GURL(kOrigin),
212        AssignAndQuitCallback(&run_loop, &actual_status));
213    run_loop.Run();
214
215    EXPECT_EQ(expected_status, actual_status);
216    ASSERT_EQ(expected_states.size(), actual_states.size());
217    for (size_t i = 0; i < actual_states.size(); ++i)
218      EXPECT_EQ(expected_states[i], actual_states[i]);
219  }
220
221  FileSystemURL URL(const std::string& path) const {
222    return file_system_->URL(path);
223  }
224
225  StrictMock<MockRemoteFileSyncService>* mock_remote_service() {
226    return remote_service_;
227  }
228
229  StrictMock<MockLocalChangeProcessor>* mock_local_change_processor() {
230    return &local_change_processor_;
231  }
232
233  void EnableSync() {
234    EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(true)).Times(1);
235    sync_service_->SetSyncEnabledForTesting(true);
236  }
237
238  MultiThreadTestHelper thread_helper_;
239  TestingProfile profile_;
240  scoped_ptr<CannedSyncableFileSystem> file_system_;
241
242  // Their ownerships are transferred to SyncFileSystemService.
243  LocalFileSyncService* local_service_;
244  StrictMock<MockRemoteFileSyncService>* remote_service_;
245  StrictMock<MockLocalChangeProcessor> local_change_processor_;
246
247  scoped_ptr<SyncFileSystemService> sync_service_;
248};
249
250TEST_F(SyncFileSystemServiceTest, InitializeForApp) {
251  InitializeApp();
252}
253
254TEST_F(SyncFileSystemServiceTest, InitializeForAppSuccess) {
255  std::vector<SyncServiceState> expected_states;
256  expected_states.push_back(SYNC_SERVICE_RUNNING);
257
258  InitializeAppForObserverTest(
259      REMOTE_SERVICE_OK,
260      SYNC_STATUS_OK,
261      expected_states,
262      SYNC_STATUS_OK);
263}
264
265TEST_F(SyncFileSystemServiceTest, InitializeForAppWithNetworkFailure) {
266  std::vector<SyncServiceState> expected_states;
267  expected_states.push_back(SYNC_SERVICE_TEMPORARY_UNAVAILABLE);
268
269  // Notify REMOTE_SERVICE_TEMPORARY_UNAVAILABLE and callback with
270  // SYNC_STATUS_NETWORK_ERROR.  This should let the
271  // InitializeApp fail.
272  InitializeAppForObserverTest(
273      REMOTE_SERVICE_TEMPORARY_UNAVAILABLE,
274      SYNC_STATUS_NETWORK_ERROR,
275      expected_states,
276      SYNC_STATUS_NETWORK_ERROR);
277}
278
279TEST_F(SyncFileSystemServiceTest, InitializeForAppWithError) {
280  std::vector<SyncServiceState> expected_states;
281  expected_states.push_back(SYNC_SERVICE_DISABLED);
282
283  // Notify REMOTE_SERVICE_DISABLED and callback with
284  // SYNC_STATUS_FAILED.  This should let the InitializeApp fail.
285  InitializeAppForObserverTest(
286      REMOTE_SERVICE_DISABLED,
287      SYNC_STATUS_FAILED,
288      expected_states,
289      SYNC_STATUS_FAILED);
290}
291
292// Flaky.  http://crbug.com/237710
293TEST_F(SyncFileSystemServiceTest, DISABLED_SimpleLocalSyncFlow) {
294  InitializeApp();
295
296  StrictMock<MockSyncStatusObserver> status_observer;
297
298  EnableSync();
299  file_system_->file_system_context()->sync_context()->
300      set_mock_notify_changes_duration_in_sec(0);
301  file_system_->AddSyncStatusObserver(&status_observer);
302
303  // We'll test one local sync for this file.
304  const FileSystemURL kFile(file_system_->URL("foo"));
305
306  base::RunLoop run_loop;
307
308  // We should get called OnSyncEnabled and OnWriteEnabled on kFile.
309  // (We quit the run loop when OnWriteEnabled is called on kFile)
310  EXPECT_CALL(status_observer, OnSyncEnabled(kFile))
311      .Times(AtLeast(1));
312  EXPECT_CALL(status_observer, OnWriteEnabled(kFile))
313      .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
314
315  // We expect a set of method calls for starting a local sync.
316  EXPECT_CALL(*mock_remote_service(), GetCurrentState())
317      .Times(AtLeast(2))
318      .WillRepeatedly(Return(REMOTE_SERVICE_OK));
319
320  // The local_change_processor's ApplyLocalChange should be called once
321  // with ADD_OR_UPDATE change for TYPE_FILE.
322  const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
323                          SYNC_FILE_TYPE_FILE);
324  EXPECT_CALL(*mock_local_change_processor(),
325              ApplyLocalChange(change, _, _, kFile, _))
326      .WillOnce(MockStatusCallback(SYNC_STATUS_OK));
327
328  EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile));
329
330  run_loop.Run();
331}
332
333TEST_F(SyncFileSystemServiceTest, SimpleRemoteSyncFlow) {
334  InitializeApp();
335
336  EnableSync();
337
338  base::RunLoop run_loop;
339
340  // We expect a set of method calls for starting a remote sync.
341  EXPECT_CALL(*mock_remote_service(), GetCurrentState())
342      .Times(AtLeast(1))
343      .WillRepeatedly(Return(REMOTE_SERVICE_OK));
344  EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
345      .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
346
347  // This should trigger a remote sync.
348  mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
349
350  run_loop.Run();
351}
352
353TEST_F(SyncFileSystemServiceTest, SimpleSyncFlowWithFileBusy) {
354  InitializeApp();
355
356  EnableSync();
357  file_system_->file_system_context()->sync_context()->
358      set_mock_notify_changes_duration_in_sec(0);
359
360  const FileSystemURL kFile(file_system_->URL("foo"));
361
362  base::RunLoop run_loop;
363
364  // We expect a set of method calls for starting a remote sync.
365  EXPECT_CALL(*mock_remote_service(), GetCurrentState())
366      .Times(AtLeast(3))
367      .WillRepeatedly(Return(REMOTE_SERVICE_OK));
368
369  {
370    InSequence sequence;
371
372    // Return with SYNC_STATUS_FILE_BUSY once.
373    EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
374        .WillOnce(MockSyncFileCallback(SYNC_STATUS_FILE_BUSY,
375                                       kFile));
376
377    // ProcessRemoteChange should be called again when the becomes
378    // not busy.
379    EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
380        .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
381  }
382
383  // We might also see an activity for local sync as we're going to make
384  // a local write operation on kFile.
385  EXPECT_CALL(*mock_local_change_processor(),
386              ApplyLocalChange(_, _, _, kFile, _))
387      .Times(AnyNumber());
388
389  // This should trigger a remote sync.
390  mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
391
392  // Start a local operation on the same file (to make it BUSY).
393  base::WaitableEvent event(false, false);
394  thread_helper_.io_task_runner()->PostTask(
395      FROM_HERE, base::Bind(&CannedSyncableFileSystem::DoCreateFile,
396                            base::Unretained(file_system_.get()),
397                            kFile, base::Bind(&VerifyFileError, &event)));
398
399  run_loop.Run();
400
401  mock_remote_service()->NotifyRemoteChangeQueueUpdated(0);
402
403  event.Wait();
404}
405
406TEST_F(SyncFileSystemServiceTest, GetFileSyncStatus) {
407  InitializeApp();
408
409  const FileSystemURL kFile(file_system_->URL("foo"));
410
411  SyncStatusCode status;
412  SyncFileStatus sync_file_status;
413
414  // 1. The file is not in conflicting nor in pending change state.
415  {
416    base::RunLoop run_loop;
417    EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile))
418        .WillOnce(Return(false));
419
420    status = SYNC_STATUS_UNKNOWN;
421    sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
422    sync_service_->GetFileSyncStatus(
423        kFile,
424        base::Bind(&AssignValueAndQuit<SyncFileStatus>,
425                   &run_loop, &status, &sync_file_status));
426    run_loop.Run();
427
428    EXPECT_EQ(SYNC_STATUS_OK, status);
429    EXPECT_EQ(SYNC_FILE_STATUS_SYNCED, sync_file_status);
430  }
431
432  // 2. Conflicting case.
433  {
434    base::RunLoop run_loop;
435    EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile))
436        .WillOnce(Return(true));
437
438    status = SYNC_STATUS_UNKNOWN;
439    sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
440    sync_service_->GetFileSyncStatus(
441        kFile,
442        base::Bind(&AssignValueAndQuit<SyncFileStatus>,
443                   &run_loop, &status, &sync_file_status));
444    run_loop.Run();
445
446    EXPECT_EQ(SYNC_STATUS_OK, status);
447    EXPECT_EQ(SYNC_FILE_STATUS_CONFLICTING, sync_file_status);
448  }
449
450  // 3. The file has pending local changes.
451  {
452    EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile));
453
454    base::RunLoop run_loop;
455    EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile))
456        .WillOnce(Return(false));
457
458    status = SYNC_STATUS_UNKNOWN;
459    sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
460    sync_service_->GetFileSyncStatus(
461        kFile,
462        base::Bind(&AssignValueAndQuit<SyncFileStatus>,
463                   &run_loop, &status, &sync_file_status));
464    run_loop.Run();
465
466    EXPECT_EQ(SYNC_STATUS_OK, status);
467    EXPECT_EQ(SYNC_FILE_STATUS_HAS_PENDING_CHANGES, sync_file_status);
468  }
469
470  // 4. The file has a conflict and pending local changes. In this case
471  // we return SYNC_FILE_STATUS_CONFLICTING.
472  {
473    EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->TruncateFile(kFile, 1U));
474
475    base::RunLoop run_loop;
476    EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile))
477        .WillOnce(Return(true));
478
479    status = SYNC_STATUS_UNKNOWN;
480    sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
481    sync_service_->GetFileSyncStatus(
482        kFile,
483        base::Bind(&AssignValueAndQuit<SyncFileStatus>,
484                   &run_loop, &status, &sync_file_status));
485    run_loop.Run();
486
487    EXPECT_EQ(SYNC_STATUS_OK, status);
488    EXPECT_EQ(SYNC_FILE_STATUS_CONFLICTING, sync_file_status);
489  }
490}
491
492}  // namespace sync_file_system
493