sync_file_system_service_unittest.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 "storage/browser/fileapi/file_system_context.h"
32#include "testing/gtest/include/gtest/gtest.h"
33#include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
34#include "third_party/leveldatabase/src/include/leveldb/env.h"
35
36using content::BrowserThread;
37using storage::FileSystemURL;
38using storage::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 storage::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
122ACTION(InvokeCompletionClosure) {
123  base::ThreadTaskRunnerHandle::Get()->PostTask(
124      FROM_HERE, base::Bind(arg0));
125}
126
127class SyncFileSystemServiceTest : public testing::Test {
128 protected:
129  SyncFileSystemServiceTest()
130      : thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD |
131                       content::TestBrowserThreadBundle::REAL_IO_THREAD) {}
132
133  virtual void SetUp() OVERRIDE {
134    in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
135    file_system_.reset(new CannedSyncableFileSystem(
136        GURL(kOrigin),
137        in_memory_env_.get(),
138        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
139        BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)));
140
141    scoped_ptr<LocalFileSyncService> local_service =
142        LocalFileSyncService::CreateForTesting(&profile_, in_memory_env_.get());
143    remote_service_ = new StrictMock<MockRemoteFileSyncService>;
144    sync_service_.reset(new SyncFileSystemService(&profile_));
145
146    EXPECT_CALL(*mock_remote_service(),
147                AddServiceObserver(_)).Times(1);
148    EXPECT_CALL(*mock_remote_service(),
149                AddFileStatusObserver(sync_service_.get())).Times(1);
150    EXPECT_CALL(*mock_remote_service(),
151                GetLocalChangeProcessor())
152        .WillRepeatedly(Return(&local_change_processor_));
153    EXPECT_CALL(*mock_remote_service(),
154                SetRemoteChangeProcessor(local_service.get())).Times(1);
155
156    sync_service_->Initialize(
157        local_service.Pass(),
158        scoped_ptr<RemoteFileSyncService>(remote_service_));
159
160    // Disable auto sync by default.
161    EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(false)).Times(1);
162    sync_service_->SetSyncEnabledForTesting(false);
163
164    file_system_->SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
165  }
166
167  virtual void TearDown() OVERRIDE {
168    sync_service_->Shutdown();
169    file_system_->TearDown();
170    RevokeSyncableFileSystem();
171    content::RunAllPendingInMessageLoop(BrowserThread::FILE);
172  }
173
174  void InitializeApp() {
175    base::RunLoop run_loop;
176    SyncStatusCode status = SYNC_STATUS_UNKNOWN;
177
178    EXPECT_CALL(*mock_remote_service(),
179                RegisterOrigin(GURL(kOrigin), _)).Times(1);
180
181    // GetCurrentState may be called when a remote or local sync is scheduled
182    // by change notifications or by a timer.
183    EXPECT_CALL(*mock_remote_service(), GetCurrentState())
184        .Times(AnyNumber())
185        .WillRepeatedly(Return(REMOTE_SERVICE_OK));
186
187    sync_service_->InitializeForApp(
188        file_system_->file_system_context(),
189        GURL(kOrigin),
190        AssignAndQuitCallback(&run_loop, &status));
191    run_loop.Run();
192
193    EXPECT_EQ(SYNC_STATUS_OK, status);
194    EXPECT_EQ(base::File::FILE_OK, file_system_->OpenFileSystem());
195  }
196
197  // Calls InitializeForApp after setting up the mock remote service to
198  // perform following when RegisterOrigin is called:
199  //  1. Notify RemoteFileSyncService's observers of |state_to_notify|
200  //  2. Run the given callback with |status_to_return|.
201  //
202  // ..and verifies if following conditions are met:
203  //  1. The SyncEventObserver of the service is called with
204  //     |expected_states| service state values.
205  //  2. InitializeForApp's callback is called with |expected_status|
206  void InitializeAppForObserverTest(
207      RemoteServiceState state_to_notify,
208      SyncStatusCode status_to_return,
209      const std::vector<SyncServiceState>& expected_states,
210      SyncStatusCode expected_status) {
211    StrictMock<MockSyncEventObserver> event_observer;
212    sync_service_->AddSyncEventObserver(&event_observer);
213
214    EnableSync();
215
216    EXPECT_CALL(*mock_remote_service(), GetCurrentState())
217        .Times(AnyNumber())
218        .WillRepeatedly(Return(state_to_notify));
219
220    EXPECT_CALL(*mock_remote_service(),
221                RegisterOrigin(GURL(kOrigin), _))
222        .WillOnce(NotifyStateAndCallback(mock_remote_service(),
223                                         state_to_notify,
224                                         status_to_return));
225
226    std::vector<SyncServiceState> actual_states;
227    EXPECT_CALL(event_observer, OnSyncStateUpdated(GURL(), _, _))
228        .WillRepeatedly(RecordState(&actual_states));
229
230    SyncStatusCode actual_status = SYNC_STATUS_UNKNOWN;
231    base::RunLoop run_loop;
232    sync_service_->InitializeForApp(
233        file_system_->file_system_context(),
234        GURL(kOrigin),
235        AssignAndQuitCallback(&run_loop, &actual_status));
236    run_loop.Run();
237
238    EXPECT_EQ(expected_status, actual_status);
239    ASSERT_EQ(expected_states.size(), actual_states.size());
240    for (size_t i = 0; i < actual_states.size(); ++i)
241      EXPECT_EQ(expected_states[i], actual_states[i]);
242
243    sync_service_->RemoveSyncEventObserver(&event_observer);
244  }
245
246  FileSystemURL URL(const std::string& path) const {
247    return file_system_->URL(path);
248  }
249
250  StrictMock<MockRemoteFileSyncService>* mock_remote_service() {
251    return remote_service_;
252  }
253
254  StrictMock<MockLocalChangeProcessor>* mock_local_change_processor() {
255    return &local_change_processor_;
256  }
257
258  void EnableSync() {
259    EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(true)).Times(1);
260    sync_service_->SetSyncEnabledForTesting(true);
261  }
262
263  content::TestBrowserThreadBundle thread_bundle_;
264  scoped_ptr<leveldb::Env> in_memory_env_;
265  TestingProfile profile_;
266  scoped_ptr<CannedSyncableFileSystem> file_system_;
267
268  // Their ownerships are transferred to SyncFileSystemService.
269  StrictMock<MockRemoteFileSyncService>* remote_service_;
270  StrictMock<MockLocalChangeProcessor> local_change_processor_;
271
272  scoped_ptr<SyncFileSystemService> sync_service_;
273};
274
275TEST_F(SyncFileSystemServiceTest, InitializeForApp) {
276  InitializeApp();
277}
278
279TEST_F(SyncFileSystemServiceTest, InitializeForAppSuccess) {
280  std::vector<SyncServiceState> expected_states;
281  expected_states.push_back(SYNC_SERVICE_RUNNING);
282
283  InitializeAppForObserverTest(
284      REMOTE_SERVICE_OK,
285      SYNC_STATUS_OK,
286      expected_states,
287      SYNC_STATUS_OK);
288}
289
290TEST_F(SyncFileSystemServiceTest, InitializeForAppWithNetworkFailure) {
291  std::vector<SyncServiceState> expected_states;
292  expected_states.push_back(SYNC_SERVICE_TEMPORARY_UNAVAILABLE);
293
294  // Notify REMOTE_SERVICE_TEMPORARY_UNAVAILABLE and callback with
295  // SYNC_STATUS_NETWORK_ERROR.  This should let the
296  // InitializeApp fail.
297  InitializeAppForObserverTest(
298      REMOTE_SERVICE_TEMPORARY_UNAVAILABLE,
299      SYNC_STATUS_NETWORK_ERROR,
300      expected_states,
301      SYNC_STATUS_NETWORK_ERROR);
302}
303
304TEST_F(SyncFileSystemServiceTest, InitializeForAppWithError) {
305  std::vector<SyncServiceState> expected_states;
306  expected_states.push_back(SYNC_SERVICE_DISABLED);
307
308  // Notify REMOTE_SERVICE_DISABLED and callback with
309  // SYNC_STATUS_FAILED.  This should let the InitializeApp fail.
310  InitializeAppForObserverTest(
311      REMOTE_SERVICE_DISABLED,
312      SYNC_STATUS_FAILED,
313      expected_states,
314      SYNC_STATUS_FAILED);
315}
316
317TEST_F(SyncFileSystemServiceTest, SimpleLocalSyncFlow) {
318  InitializeApp();
319
320  StrictMock<MockSyncStatusObserver> status_observer;
321
322  EnableSync();
323  file_system_->backend()->sync_context()->
324      set_mock_notify_changes_duration_in_sec(0);
325  file_system_->AddSyncStatusObserver(&status_observer);
326
327  // We'll test one local sync for this file.
328  const FileSystemURL kFile(file_system_->URL("foo"));
329
330  base::RunLoop run_loop;
331
332  // We should get called OnSyncEnabled and OnWriteEnabled on kFile as in:
333  // 1. OnWriteEnabled when PrepareForSync(SYNC_SHARED) is finished and
334  //    the target file is unlocked for writing
335  // 2. OnSyncEnabled x 3 times; 1) when CreateFile is finished, 2) when
336  //    file is unlocked after PrepareForSync, and 3) when the sync is
337  //    finished.
338  EXPECT_CALL(status_observer, OnWriteEnabled(kFile))
339      .Times(AtLeast(1));
340
341  {
342    ::testing::InSequence sequence;
343    EXPECT_CALL(status_observer, OnSyncEnabled(kFile))
344        .Times(AtLeast(2));
345    EXPECT_CALL(status_observer, OnSyncEnabled(kFile))
346        .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
347  }
348
349  // The local_change_processor's ApplyLocalChange should be called once
350  // with ADD_OR_UPDATE change for TYPE_FILE.
351  const FileChange change(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
352                          SYNC_FILE_TYPE_FILE);
353  EXPECT_CALL(*mock_local_change_processor(),
354              ApplyLocalChange(change, _, _, kFile, _))
355      .WillOnce(MockStatusCallback(SYNC_STATUS_OK));
356  EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
357      .WillRepeatedly(MockSyncFileCallback(SYNC_STATUS_NO_CHANGE_TO_SYNC,
358                                           FileSystemURL()));
359
360  EXPECT_CALL(*mock_remote_service(), PromoteDemotedChanges(_))
361      .WillRepeatedly(InvokeCompletionClosure());
362
363  EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile));
364
365  run_loop.Run();
366
367  file_system_->RemoveSyncStatusObserver(&status_observer);
368}
369
370TEST_F(SyncFileSystemServiceTest, SimpleRemoteSyncFlow) {
371  InitializeApp();
372
373  EnableSync();
374
375  base::RunLoop run_loop;
376
377  // We expect a set of method calls for starting a remote sync.
378  EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
379      .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
380
381  // This should trigger a remote sync.
382  mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
383
384  run_loop.Run();
385}
386
387TEST_F(SyncFileSystemServiceTest, SimpleSyncFlowWithFileBusy) {
388  InitializeApp();
389
390  EnableSync();
391  file_system_->backend()->sync_context()->
392      set_mock_notify_changes_duration_in_sec(0);
393
394  const FileSystemURL kFile(file_system_->URL("foo"));
395
396  base::RunLoop run_loop;
397
398  {
399    InSequence sequence;
400
401    // Return with SYNC_STATUS_FILE_BUSY once.
402    EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
403        .WillOnce(MockSyncFileCallback(SYNC_STATUS_FILE_BUSY,
404                                       kFile));
405
406    // ProcessRemoteChange should be called again when the becomes
407    // not busy.
408    EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
409        .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
410  }
411
412  EXPECT_CALL(*mock_remote_service(), PromoteDemotedChanges(_))
413      .WillRepeatedly(InvokeCompletionClosure());
414
415  // We might also see an activity for local sync as we're going to make
416  // a local write operation on kFile.
417  EXPECT_CALL(*mock_local_change_processor(),
418              ApplyLocalChange(_, _, _, kFile, _))
419      .Times(AnyNumber());
420
421  // This should trigger a remote sync.
422  mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
423
424  // Start a local operation on the same file (to make it BUSY).
425  base::RunLoop verify_file_error_run_loop;
426  BrowserThread::PostTask(
427      BrowserThread::IO,
428      FROM_HERE,
429      base::Bind(&CannedSyncableFileSystem::DoCreateFile,
430                 base::Unretained(file_system_.get()),
431                 kFile, base::Bind(&VerifyFileError,
432                                   &verify_file_error_run_loop)));
433
434  run_loop.Run();
435
436  mock_remote_service()->NotifyRemoteChangeQueueUpdated(0);
437
438  verify_file_error_run_loop.Run();
439}
440
441#if defined(THREAD_SANITIZER)
442// SyncFileSystemServiceTest.GetFileSyncStatus fails under ThreadSanitizer,
443// see http://crbug.com/294904.
444#define MAYBE_GetFileSyncStatus DISABLED_GetFileSyncStatus
445#else
446#define MAYBE_GetFileSyncStatus GetFileSyncStatus
447#endif
448TEST_F(SyncFileSystemServiceTest, MAYBE_GetFileSyncStatus) {
449  InitializeApp();
450
451  const FileSystemURL kFile(file_system_->URL("foo"));
452
453  SyncStatusCode status;
454  SyncFileStatus sync_file_status;
455
456  // 1. The file is synced state.
457  {
458    base::RunLoop run_loop;
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_SYNCED, sync_file_status);
469  }
470
471  // 2. The file has pending local changes.
472  {
473    base::RunLoop run_loop;
474    EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile));
475
476    status = SYNC_STATUS_UNKNOWN;
477    sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
478    sync_service_->GetFileSyncStatus(
479        kFile,
480        base::Bind(&AssignValueAndQuit<SyncFileStatus>,
481                   &run_loop, &status, &sync_file_status));
482    run_loop.Run();
483
484    EXPECT_EQ(SYNC_STATUS_OK, status);
485    EXPECT_EQ(SYNC_FILE_STATUS_HAS_PENDING_CHANGES, sync_file_status);
486  }
487}
488
489}  // namespace sync_file_system
490