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