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.
7 #include "base/basictypes.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 "third_party/leveldatabase/src/helpers/memenv/memenv.h"
31 #include "third_party/leveldatabase/src/include/leveldb/env.h"
32 #include "webkit/browser/fileapi/file_system_context.h"
34 using content::BrowserThread;
35 using fileapi::FileSystemURL;
36 using fileapi::FileSystemURLSet;
37 using ::testing::AnyNumber;
38 using ::testing::AtLeast;
39 using ::testing::InSequence;
40 using ::testing::InvokeWithoutArgs;
41 using ::testing::Return;
42 using ::testing::StrictMock;
45 namespace sync_file_system {
49 const char kOrigin[] = "http://example.com";
51 template <typename R> struct AssignTrait {
52 typedef const R& ArgumentType;
55 template <> struct AssignTrait<SyncFileStatus> {
56 typedef SyncFileStatus ArgumentType;
60 void AssignValueAndQuit(base::RunLoop* run_loop,
61 SyncStatusCode* status_out, R* value_out,
62 SyncStatusCode status,
63 typename AssignTrait<R>::ArgumentType value) {
72 // This is called on IO thread.
73 void VerifyFileError(base::RunLoop* run_loop,
74 base::File::Error error) {
76 EXPECT_EQ(base::File::FILE_OK, error);
82 class MockSyncEventObserver : public SyncEventObserver {
84 MockSyncEventObserver() {}
85 virtual ~MockSyncEventObserver() {}
87 MOCK_METHOD3(OnSyncStateUpdated,
88 void(const GURL& app_origin,
89 SyncServiceState state,
90 const std::string& description));
91 MOCK_METHOD4(OnFileSynced,
92 void(const fileapi::FileSystemURL& url,
93 SyncFileStatus status,
95 SyncDirection direction));
98 ACTION_P3(NotifyStateAndCallback,
99 mock_remote_service, service_state, operation_status) {
100 mock_remote_service->NotifyRemoteServiceStateUpdated(
101 service_state, "Test event.");
102 base::MessageLoopProxy::current()->PostTask(
103 FROM_HERE, base::Bind(arg1, operation_status));
106 ACTION_P(RecordState, states) {
107 states->push_back(arg1);
110 ACTION_P(MockStatusCallback, status) {
111 base::MessageLoopProxy::current()->PostTask(
112 FROM_HERE, base::Bind(arg4, status));
115 ACTION_P2(MockSyncFileCallback, status, url) {
116 base::MessageLoopProxy::current()->PostTask(
117 FROM_HERE, base::Bind(arg0, status, url));
120 class SyncFileSystemServiceTest : public testing::Test {
122 SyncFileSystemServiceTest()
123 : thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD |
124 content::TestBrowserThreadBundle::REAL_IO_THREAD) {}
126 virtual void SetUp() OVERRIDE {
127 in_memory_env_.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
128 file_system_.reset(new CannedSyncableFileSystem(
130 in_memory_env_.get(),
131 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO),
132 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)));
134 scoped_ptr<LocalFileSyncService> local_service =
135 LocalFileSyncService::CreateForTesting(&profile_, in_memory_env_.get());
136 remote_service_ = new StrictMock<MockRemoteFileSyncService>;
137 sync_service_.reset(new SyncFileSystemService(&profile_));
139 EXPECT_CALL(*mock_remote_service(),
140 AddServiceObserver(_)).Times(1);
141 EXPECT_CALL(*mock_remote_service(),
142 AddFileStatusObserver(sync_service_.get())).Times(1);
143 EXPECT_CALL(*mock_remote_service(),
144 GetLocalChangeProcessor())
145 .WillRepeatedly(Return(&local_change_processor_));
146 EXPECT_CALL(*mock_remote_service(),
147 SetRemoteChangeProcessor(local_service.get())).Times(1);
149 sync_service_->Initialize(
150 local_service.Pass(),
151 scoped_ptr<RemoteFileSyncService>(remote_service_));
153 // Disable auto sync by default.
154 EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(false)).Times(1);
155 sync_service_->SetSyncEnabledForTesting(false);
157 file_system_->SetUp(CannedSyncableFileSystem::QUOTA_ENABLED);
160 virtual void TearDown() OVERRIDE {
161 sync_service_->Shutdown();
162 file_system_->TearDown();
163 RevokeSyncableFileSystem();
164 content::RunAllPendingInMessageLoop(BrowserThread::FILE);
167 void InitializeApp() {
168 base::RunLoop run_loop;
169 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
171 EXPECT_CALL(*mock_remote_service(),
172 RegisterOrigin(GURL(kOrigin), _)).Times(1);
174 // GetCurrentState may be called when a remote or local sync is scheduled
175 // by change notifications or by a timer.
176 EXPECT_CALL(*mock_remote_service(), GetCurrentState())
178 .WillRepeatedly(Return(REMOTE_SERVICE_OK));
180 sync_service_->InitializeForApp(
181 file_system_->file_system_context(),
183 AssignAndQuitCallback(&run_loop, &status));
186 EXPECT_EQ(SYNC_STATUS_OK, status);
187 EXPECT_EQ(base::File::FILE_OK, file_system_->OpenFileSystem());
190 // Calls InitializeForApp after setting up the mock remote service to
191 // perform following when RegisterOrigin is called:
192 // 1. Notify RemoteFileSyncService's observers of |state_to_notify|
193 // 2. Run the given callback with |status_to_return|.
195 // ..and verifies if following conditions are met:
196 // 1. The SyncEventObserver of the service is called with
197 // |expected_states| service state values.
198 // 2. InitializeForApp's callback is called with |expected_status|
199 void InitializeAppForObserverTest(
200 RemoteServiceState state_to_notify,
201 SyncStatusCode status_to_return,
202 const std::vector<SyncServiceState>& expected_states,
203 SyncStatusCode expected_status) {
204 StrictMock<MockSyncEventObserver> event_observer;
205 sync_service_->AddSyncEventObserver(&event_observer);
209 EXPECT_CALL(*mock_remote_service(), GetCurrentState())
211 .WillRepeatedly(Return(state_to_notify));
213 EXPECT_CALL(*mock_remote_service(),
214 RegisterOrigin(GURL(kOrigin), _))
215 .WillOnce(NotifyStateAndCallback(mock_remote_service(),
219 std::vector<SyncServiceState> actual_states;
220 EXPECT_CALL(event_observer, OnSyncStateUpdated(GURL(), _, _))
221 .WillRepeatedly(RecordState(&actual_states));
223 SyncStatusCode actual_status = SYNC_STATUS_UNKNOWN;
224 base::RunLoop run_loop;
225 sync_service_->InitializeForApp(
226 file_system_->file_system_context(),
228 AssignAndQuitCallback(&run_loop, &actual_status));
231 EXPECT_EQ(expected_status, actual_status);
232 ASSERT_EQ(expected_states.size(), actual_states.size());
233 for (size_t i = 0; i < actual_states.size(); ++i)
234 EXPECT_EQ(expected_states[i], actual_states[i]);
236 sync_service_->RemoveSyncEventObserver(&event_observer);
239 FileSystemURL URL(const std::string& path) const {
240 return file_system_->URL(path);
243 StrictMock<MockRemoteFileSyncService>* mock_remote_service() {
244 return remote_service_;
247 StrictMock<MockLocalChangeProcessor>* mock_local_change_processor() {
248 return &local_change_processor_;
252 EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(true)).Times(1);
253 sync_service_->SetSyncEnabledForTesting(true);
256 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_;
258 content::TestBrowserThreadBundle thread_bundle_;
259 scoped_ptr<leveldb::Env> in_memory_env_;
260 TestingProfile profile_;
261 scoped_ptr<CannedSyncableFileSystem> file_system_;
263 // Their ownerships are transferred to SyncFileSystemService.
264 StrictMock<MockRemoteFileSyncService>* remote_service_;
265 StrictMock<MockLocalChangeProcessor> local_change_processor_;
267 scoped_ptr<SyncFileSystemService> sync_service_;
270 TEST_F(SyncFileSystemServiceTest, InitializeForApp) {
274 TEST_F(SyncFileSystemServiceTest, InitializeForAppSuccess) {
275 std::vector<SyncServiceState> expected_states;
276 expected_states.push_back(SYNC_SERVICE_RUNNING);
278 InitializeAppForObserverTest(
285 TEST_F(SyncFileSystemServiceTest, InitializeForAppWithNetworkFailure) {
286 std::vector<SyncServiceState> expected_states;
287 expected_states.push_back(SYNC_SERVICE_TEMPORARY_UNAVAILABLE);
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,
296 SYNC_STATUS_NETWORK_ERROR);
299 TEST_F(SyncFileSystemServiceTest, InitializeForAppWithError) {
300 std::vector<SyncServiceState> expected_states;
301 expected_states.push_back(SYNC_SERVICE_DISABLED);
303 // Notify REMOTE_SERVICE_DISABLED and callback with
304 // SYNC_STATUS_FAILED. This should let the InitializeApp fail.
305 InitializeAppForObserverTest(
306 REMOTE_SERVICE_DISABLED,
312 TEST_F(SyncFileSystemServiceTest, SimpleLocalSyncFlow) {
315 StrictMock<MockSyncStatusObserver> status_observer;
318 file_system_->backend()->sync_context()->
319 set_mock_notify_changes_duration_in_sec(0);
320 file_system_->AddSyncStatusObserver(&status_observer);
322 // We'll test one local sync for this file.
323 const FileSystemURL kFile(file_system_->URL("foo"));
325 base::RunLoop run_loop;
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
333 EXPECT_CALL(status_observer, OnWriteEnabled(kFile))
337 ::testing::InSequence sequence;
338 EXPECT_CALL(status_observer, OnSyncEnabled(kFile))
340 EXPECT_CALL(status_observer, OnSyncEnabled(kFile))
341 .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
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));
352 EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile));
356 file_system_->RemoveSyncStatusObserver(&status_observer);
359 TEST_F(SyncFileSystemServiceTest, SimpleRemoteSyncFlow) {
364 base::RunLoop run_loop;
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));
370 // This should trigger a remote sync.
371 mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
376 TEST_F(SyncFileSystemServiceTest, SimpleSyncFlowWithFileBusy) {
380 file_system_->backend()->sync_context()->
381 set_mock_notify_changes_duration_in_sec(0);
383 const FileSystemURL kFile(file_system_->URL("foo"));
385 base::RunLoop run_loop;
390 // Return with SYNC_STATUS_FILE_BUSY once.
391 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
392 .WillOnce(MockSyncFileCallback(SYNC_STATUS_FILE_BUSY,
395 // ProcessRemoteChange should be called again when the becomes
397 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_))
398 .WillOnce(InvokeWithoutArgs(&run_loop, &base::RunLoop::Quit));
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, _))
407 // This should trigger a remote sync.
408 mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
410 // Start a local operation on the same file (to make it BUSY).
411 base::RunLoop verify_file_error_run_loop;
412 BrowserThread::PostTask(
415 base::Bind(&CannedSyncableFileSystem::DoCreateFile,
416 base::Unretained(file_system_.get()),
417 kFile, base::Bind(&VerifyFileError,
418 &verify_file_error_run_loop)));
422 mock_remote_service()->NotifyRemoteChangeQueueUpdated(0);
424 verify_file_error_run_loop.Run();
427 #if defined(THREAD_SANITIZER)
428 // SyncFileSystemServiceTest.GetFileSyncStatus fails under ThreadSanitizer,
429 // see http://crbug.com/294904.
430 #define MAYBE_GetFileSyncStatus DISABLED_GetFileSyncStatus
432 #define MAYBE_GetFileSyncStatus GetFileSyncStatus
434 TEST_F(SyncFileSystemServiceTest, MAYBE_GetFileSyncStatus) {
437 const FileSystemURL kFile(file_system_->URL("foo"));
439 SyncStatusCode status;
440 SyncFileStatus sync_file_status;
442 // 1. The file is synced state.
444 base::RunLoop run_loop;
445 status = SYNC_STATUS_UNKNOWN;
446 sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
447 sync_service_->GetFileSyncStatus(
449 base::Bind(&AssignValueAndQuit<SyncFileStatus>,
450 &run_loop, &status, &sync_file_status));
453 EXPECT_EQ(SYNC_STATUS_OK, status);
454 EXPECT_EQ(SYNC_FILE_STATUS_SYNCED, sync_file_status);
457 // 2. The file has pending local changes.
459 base::RunLoop run_loop;
460 EXPECT_EQ(base::File::FILE_OK, file_system_->CreateFile(kFile));
462 status = SYNC_STATUS_UNKNOWN;
463 sync_file_status = SYNC_FILE_STATUS_UNKNOWN;
464 sync_service_->GetFileSyncStatus(
466 base::Bind(&AssignValueAndQuit<SyncFileStatus>,
467 &run_loop, &status, &sync_file_status));
470 EXPECT_EQ(SYNC_STATUS_OK, status);
471 EXPECT_EQ(SYNC_FILE_STATUS_HAS_PENDING_CHANGES, sync_file_status);
475 } // namespace sync_file_system