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