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