sync_file_system_service_unittest.cc revision 3240926e260ce088908e02ac07a6cf7b0c0cbf44
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(sync_service_.get())).Times(1);
136    EXPECT_CALL(*mock_remote_service(),
137                AddFileStatusObserver(sync_service_.get())).Times(1);
138    EXPECT_CALL(*mock_remote_service(),
139                GetLocalChangeProcessor())
140        .WillOnce(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                RegisterOriginForTrackingChanges(GURL(kOrigin), _)).Times(1);
168
169    sync_service_->InitializeForApp(
170        file_system_->file_system_context(),
171        GURL(kOrigin),
172        AssignAndQuitCallback(&run_loop, &status));
173    run_loop.Run();
174
175    EXPECT_EQ(SYNC_STATUS_OK, status);
176    EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->OpenFileSystem());
177  }
178
179  // Calls InitializeForApp after setting up the mock remote service to
180  // perform following when RegisterOriginForTrackingChanges is called:
181  //  1. Notify RemoteFileSyncService's observers of |state_to_notify|
182  //  2. Run the given callback with |status_to_return|.
183  //
184  // ..and verifies if following conditions are met:
185  //  1. The SyncEventObserver of the service is called with
186  //     |expected_states| service state values.
187  //  2. InitializeForApp's callback is called with |expected_status|
188  //  3. GetCurrentState() is called at least |expected_current_state_calls|
189  //     times (which means that the sync service tried to start sync).
190  void InitializeAppForObserverTest(
191      RemoteServiceState state_to_notify,
192      SyncStatusCode status_to_return,
193      const std::vector<SyncServiceState>& expected_states,
194      SyncStatusCode expected_status) {
195    StrictMock<MockSyncEventObserver> event_observer;
196    sync_service_->AddSyncEventObserver(&event_observer);
197
198    EnableSync();
199
200    EXPECT_CALL(*mock_remote_service(),
201                RegisterOriginForTrackingChanges(GURL(kOrigin), _))
202        .WillOnce(NotifyStateAndCallback(mock_remote_service(),
203                                         state_to_notify,
204                                         status_to_return));
205
206    std::vector<SyncServiceState> actual_states;
207    EXPECT_CALL(event_observer, OnSyncStateUpdated(GURL(), _, _))
208        .WillRepeatedly(RecordState(&actual_states));
209
210    SyncStatusCode actual_status = SYNC_STATUS_UNKNOWN;
211    base::RunLoop run_loop;
212    sync_service_->InitializeForApp(
213        file_system_->file_system_context(),
214        GURL(kOrigin),
215        AssignAndQuitCallback(&run_loop, &actual_status));
216    run_loop.Run();
217
218    EXPECT_EQ(expected_status, actual_status);
219    ASSERT_EQ(expected_states.size(), actual_states.size());
220    for (size_t i = 0; i < actual_states.size(); ++i)
221      EXPECT_EQ(expected_states[i], actual_states[i]);
222  }
223
224  FileSystemURL URL(const std::string& path) const {
225    return file_system_->URL(path);
226  }
227
228  StrictMock<MockRemoteFileSyncService>* mock_remote_service() {
229    return remote_service_;
230  }
231
232  StrictMock<MockLocalChangeProcessor>* mock_local_change_processor() {
233    return &local_change_processor_;
234  }
235
236  void EnableSync() {
237    EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(true)).Times(1);
238    sync_service_->SetSyncEnabledForTesting(true);
239  }
240
241  ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
242
243  content::TestBrowserThreadBundle thread_bundle_;
244  TestingProfile profile_;
245  scoped_ptr<CannedSyncableFileSystem> file_system_;
246
247  // Their ownerships are transferred to SyncFileSystemService.
248  LocalFileSyncService* local_service_;
249  StrictMock<MockRemoteFileSyncService>* remote_service_;
250  StrictMock<MockLocalChangeProcessor> local_change_processor_;
251
252  scoped_ptr<SyncFileSystemService> sync_service_;
253};
254
255TEST_F(SyncFileSystemServiceTest, InitializeForApp) {
256  InitializeApp();
257}
258
259TEST_F(SyncFileSystemServiceTest, InitializeForAppSuccess) {
260  std::vector<SyncServiceState> expected_states;
261  expected_states.push_back(SYNC_SERVICE_RUNNING);
262
263  InitializeAppForObserverTest(
264      REMOTE_SERVICE_OK,
265      SYNC_STATUS_OK,
266      expected_states,
267      SYNC_STATUS_OK);
268}
269
270TEST_F(SyncFileSystemServiceTest, InitializeForAppWithNetworkFailure) {
271  std::vector<SyncServiceState> expected_states;
272  expected_states.push_back(SYNC_SERVICE_TEMPORARY_UNAVAILABLE);
273
274  // Notify REMOTE_SERVICE_TEMPORARY_UNAVAILABLE and callback with
275  // SYNC_STATUS_NETWORK_ERROR.  This should let the
276  // InitializeApp fail.
277  InitializeAppForObserverTest(
278      REMOTE_SERVICE_TEMPORARY_UNAVAILABLE,
279      SYNC_STATUS_NETWORK_ERROR,
280      expected_states,
281      SYNC_STATUS_NETWORK_ERROR);
282}
283
284TEST_F(SyncFileSystemServiceTest, InitializeForAppWithError) {
285  std::vector<SyncServiceState> expected_states;
286  expected_states.push_back(SYNC_SERVICE_DISABLED);
287
288  // Notify REMOTE_SERVICE_DISABLED and callback with
289  // SYNC_STATUS_FAILED.  This should let the InitializeApp fail.
290  InitializeAppForObserverTest(
291      REMOTE_SERVICE_DISABLED,
292      SYNC_STATUS_FAILED,
293      expected_states,
294      SYNC_STATUS_FAILED);
295}
296
297// Flaky.  http://crbug.com/237710
298TEST_F(SyncFileSystemServiceTest, DISABLED_SimpleLocalSyncFlow) {
299  InitializeApp();
300
301  StrictMock<MockSyncStatusObserver> status_observer;
302
303  EnableSync();
304  file_system_->backend()->sync_context()->
305      set_mock_notify_changes_duration_in_sec(0);
306  file_system_->AddSyncStatusObserver(&status_observer);
307
308  // We'll test one local sync for this file.
309  const FileSystemURL kFile(file_system_->URL("foo"));
310
311  base::RunLoop run_loop;
312
313  // We should get called OnSyncEnabled and OnWriteEnabled on kFile.
314  // (We quit the run loop when OnWriteEnabled is called on kFile)
315  EXPECT_CALL(status_observer, OnSyncEnabled(kFile))
316      .Times(AtLeast(1));
317  EXPECT_CALL(status_observer, OnWriteEnabled(kFile))
318      .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
319
320  // We expect a set of method calls for starting a local sync.
321  EXPECT_CALL(*mock_remote_service(), GetCurrentState())
322      .Times(AtLeast(2))
323      .WillRepeatedly(Return(REMOTE_SERVICE_OK));
324
325  // The local_change_processor's ApplyLocalChange should be called once
326  // with ADD_OR_UPDATE change for TYPE_FILE.
327  const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
328                          SYNC_FILE_TYPE_FILE);
329  EXPECT_CALL(*mock_local_change_processor(),
330              ApplyLocalChange(change, _, _, kFile, _))
331      .WillOnce(MockStatusCallback(SYNC_STATUS_OK));
332
333  EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile));
334
335  run_loop.Run();
336}
337
338TEST_F(SyncFileSystemServiceTest, SimpleRemoteSyncFlow) {
339  InitializeApp();
340
341  EnableSync();
342
343  base::RunLoop run_loop;
344
345  // We expect a set of method calls for starting a remote sync.
346  EXPECT_CALL(*mock_remote_service(), GetCurrentState())
347      .Times(AtLeast(1))
348      .WillRepeatedly(Return(REMOTE_SERVICE_OK));
349  EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
350      .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
351
352  // This should trigger a remote sync.
353  mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
354
355  run_loop.Run();
356}
357
358TEST_F(SyncFileSystemServiceTest, SimpleSyncFlowWithFileBusy) {
359  InitializeApp();
360
361  EnableSync();
362  file_system_->backend()->sync_context()->
363      set_mock_notify_changes_duration_in_sec(0);
364
365  const FileSystemURL kFile(file_system_->URL("foo"));
366
367  base::RunLoop run_loop;
368
369  // We expect a set of method calls for starting a remote sync.
370  EXPECT_CALL(*mock_remote_service(), GetCurrentState())
371      .Times(AtLeast(3))
372      .WillRepeatedly(Return(REMOTE_SERVICE_OK));
373
374  {
375    InSequence sequence;
376
377    // Return with SYNC_STATUS_FILE_BUSY once.
378    EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
379        .WillOnce(MockSyncFileCallback(SYNC_STATUS_FILE_BUSY,
380                                       kFile));
381
382    // ProcessRemoteChange should be called again when the becomes
383    // not busy.
384    EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
385        .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
386  }
387
388  // We might also see an activity for local sync as we're going to make
389  // a local write operation on kFile.
390  EXPECT_CALL(*mock_local_change_processor(),
391              ApplyLocalChange(_, _, _, kFile, _))
392      .Times(AnyNumber());
393
394  // This should trigger a remote sync.
395  mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
396
397  // Start a local operation on the same file (to make it BUSY).
398  base::RunLoop verify_file_error_run_loop;
399  BrowserThread::PostTask(
400      BrowserThread::IO,
401      FROM_HERE,
402      base::Bind(&CannedSyncableFileSystem::DoCreateFile,
403                 base::Unretained(file_system_.get()),
404                 kFile, base::Bind(&VerifyFileError,
405                                   &verify_file_error_run_loop)));
406
407  run_loop.Run();
408
409  mock_remote_service()->NotifyRemoteChangeQueueUpdated(0);
410
411  verify_file_error_run_loop.Run();
412}
413
414TEST_F(SyncFileSystemServiceTest, GetFileSyncStatus) {
415  InitializeApp();
416
417  const FileSystemURL kFile(file_system_->URL("foo"));
418
419  SyncStatusCode status;
420  SyncFileStatus sync_file_status;
421
422  // 1. The file is not in conflicting nor in pending change state.
423  {
424    base::RunLoop run_loop;
425    EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile))
426        .WillOnce(Return(false));
427
428    status = SYNC_STATUS_UNKNOWN;
429    sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
430    sync_service_->GetFileSyncStatus(
431        kFile,
432        base::Bind(&AssignValueAndQuit<SyncFileStatus>,
433                   &run_loop, &status, &sync_file_status));
434    run_loop.Run();
435
436    EXPECT_EQ(SYNC_STATUS_OK, status);
437    EXPECT_EQ(SYNC_FILE_STATUS_SYNCED, sync_file_status);
438  }
439
440  // 2. Conflicting case.
441  {
442    base::RunLoop run_loop;
443    EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile))
444        .WillOnce(Return(true));
445
446    status = SYNC_STATUS_UNKNOWN;
447    sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
448    sync_service_->GetFileSyncStatus(
449        kFile,
450        base::Bind(&AssignValueAndQuit<SyncFileStatus>,
451                   &run_loop, &status, &sync_file_status));
452    run_loop.Run();
453
454    EXPECT_EQ(SYNC_STATUS_OK, status);
455    EXPECT_EQ(SYNC_FILE_STATUS_CONFLICTING, sync_file_status);
456  }
457
458  // 3. The file has pending local changes.
459  {
460    EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->CreateFile(kFile));
461
462    base::RunLoop run_loop;
463    EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile))
464        .WillOnce(Return(false));
465
466    status = SYNC_STATUS_UNKNOWN;
467    sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
468    sync_service_->GetFileSyncStatus(
469        kFile,
470        base::Bind(&AssignValueAndQuit<SyncFileStatus>,
471                   &run_loop, &status, &sync_file_status));
472    run_loop.Run();
473
474    EXPECT_EQ(SYNC_STATUS_OK, status);
475    EXPECT_EQ(SYNC_FILE_STATUS_HAS_PENDING_CHANGES, sync_file_status);
476  }
477
478  // 4. The file has a conflict and pending local changes. In this case
479  // we return SYNC_FILE_STATUS_CONFLICTING.
480  {
481    EXPECT_EQ(base::PLATFORM_FILE_OK, file_system_->TruncateFile(kFile, 1U));
482
483    base::RunLoop run_loop;
484    EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile))
485        .WillOnce(Return(true));
486
487    status = SYNC_STATUS_UNKNOWN;
488    sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
489    sync_service_->GetFileSyncStatus(
490        kFile,
491        base::Bind(&AssignValueAndQuit<SyncFileStatus>,
492                   &run_loop, &status, &sync_file_status));
493    run_loop.Run();
494
495    EXPECT_EQ(SYNC_STATUS_OK, status);
496    EXPECT_EQ(SYNC_FILE_STATUS_CONFLICTING, sync_file_status);
497  }
498}
499
500}  // namespace sync_file_system
501