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