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/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"
36 using content::BrowserThread
;
37 using storage::FileSystemURL
;
38 using storage::FileSystemURLSet
;
39 using ::testing::AnyNumber
;
40 using ::testing::AtLeast
;
41 using ::testing::InSequence
;
42 using ::testing::InvokeWithoutArgs
;
43 using ::testing::Return
;
44 using ::testing::StrictMock
;
47 namespace sync_file_system
{
51 const char kOrigin
[] = "http://example.com";
53 template <typename R
> struct AssignTrait
{
54 typedef const R
& ArgumentType
;
57 template <> struct AssignTrait
<SyncFileStatus
> {
58 typedef SyncFileStatus ArgumentType
;
62 void AssignValueAndQuit(base::RunLoop
* run_loop
,
63 SyncStatusCode
* status_out
, R
* value_out
,
64 SyncStatusCode status
,
65 typename AssignTrait
<R
>::ArgumentType value
) {
74 // This is called on IO thread.
75 void VerifyFileError(base::RunLoop
* run_loop
,
76 base::File::Error error
) {
78 EXPECT_EQ(base::File::FILE_OK
, error
);
84 class MockSyncEventObserver
: public SyncEventObserver
{
86 MockSyncEventObserver() {}
87 virtual ~MockSyncEventObserver() {}
89 MOCK_METHOD3(OnSyncStateUpdated
,
90 void(const GURL
& app_origin
,
91 SyncServiceState state
,
92 const std::string
& description
));
93 MOCK_METHOD5(OnFileSynced
,
94 void(const storage::FileSystemURL
& url
,
95 SyncFileType file_type
,
96 SyncFileStatus status
,
98 SyncDirection direction
));
101 ACTION_P3(NotifyStateAndCallback
,
102 mock_remote_service
, service_state
, operation_status
) {
103 mock_remote_service
->NotifyRemoteServiceStateUpdated(
104 service_state
, "Test event.");
105 base::ThreadTaskRunnerHandle::Get()->PostTask(
106 FROM_HERE
, base::Bind(arg1
, operation_status
));
109 ACTION_P(RecordState
, states
) {
110 states
->push_back(arg1
);
113 ACTION_P(MockStatusCallback
, status
) {
114 base::ThreadTaskRunnerHandle::Get()->PostTask(
115 FROM_HERE
, base::Bind(arg4
, status
));
118 ACTION_P2(MockSyncFileCallback
, status
, url
) {
119 base::ThreadTaskRunnerHandle::Get()->PostTask(
120 FROM_HERE
, base::Bind(arg0
, status
, url
));
123 ACTION(InvokeCompletionClosure
) {
124 base::ThreadTaskRunnerHandle::Get()->PostTask(
125 FROM_HERE
, base::Bind(arg0
));
128 class SyncFileSystemServiceTest
: public testing::Test
{
130 SyncFileSystemServiceTest()
131 : thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD
|
132 content::TestBrowserThreadBundle::REAL_IO_THREAD
) {}
134 virtual void SetUp() override
{
135 in_memory_env_
.reset(leveldb::NewMemEnv(leveldb::Env::Default()));
136 file_system_
.reset(new CannedSyncableFileSystem(
138 in_memory_env_
.get(),
139 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
),
140 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)));
142 scoped_ptr
<LocalFileSyncService
> local_service
=
143 LocalFileSyncService::CreateForTesting(&profile_
, in_memory_env_
.get());
144 remote_service_
= new StrictMock
<MockRemoteFileSyncService
>;
145 sync_service_
.reset(new SyncFileSystemService(&profile_
));
147 EXPECT_CALL(*mock_remote_service(),
148 AddServiceObserver(_
)).Times(1);
149 EXPECT_CALL(*mock_remote_service(),
150 AddFileStatusObserver(sync_service_
.get())).Times(1);
151 EXPECT_CALL(*mock_remote_service(),
152 GetLocalChangeProcessor())
153 .WillRepeatedly(Return(&local_change_processor_
));
154 EXPECT_CALL(*mock_remote_service(),
155 SetRemoteChangeProcessor(local_service
.get())).Times(1);
157 sync_service_
->Initialize(
158 local_service
.Pass(),
159 scoped_ptr
<RemoteFileSyncService
>(remote_service_
));
161 // Disable auto sync by default.
162 EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(false)).Times(1);
163 sync_service_
->SetSyncEnabledForTesting(false);
165 file_system_
->SetUp(CannedSyncableFileSystem::QUOTA_ENABLED
);
168 virtual void TearDown() override
{
169 sync_service_
->Shutdown();
170 file_system_
->TearDown();
171 RevokeSyncableFileSystem();
172 content::RunAllPendingInMessageLoop(BrowserThread::FILE);
175 void InitializeApp() {
176 base::RunLoop run_loop
;
177 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
179 EXPECT_CALL(*mock_remote_service(),
180 RegisterOrigin(GURL(kOrigin
), _
)).Times(1);
182 // GetCurrentState may be called when a remote or local sync is scheduled
183 // by change notifications or by a timer.
184 EXPECT_CALL(*mock_remote_service(), GetCurrentState())
186 .WillRepeatedly(Return(REMOTE_SERVICE_OK
));
188 sync_service_
->InitializeForApp(
189 file_system_
->file_system_context(),
191 AssignAndQuitCallback(&run_loop
, &status
));
194 EXPECT_EQ(SYNC_STATUS_OK
, status
);
195 EXPECT_EQ(base::File::FILE_OK
, file_system_
->OpenFileSystem());
198 // Calls InitializeForApp after setting up the mock remote service to
199 // perform following when RegisterOrigin is called:
200 // 1. Notify RemoteFileSyncService's observers of |state_to_notify|
201 // 2. Run the given callback with |status_to_return|.
203 // ..and verifies if following conditions are met:
204 // 1. The SyncEventObserver of the service is called with
205 // |expected_states| service state values.
206 // 2. InitializeForApp's callback is called with |expected_status|
207 void InitializeAppForObserverTest(
208 RemoteServiceState state_to_notify
,
209 SyncStatusCode status_to_return
,
210 const std::vector
<SyncServiceState
>& expected_states
,
211 SyncStatusCode expected_status
) {
212 StrictMock
<MockSyncEventObserver
> event_observer
;
213 sync_service_
->AddSyncEventObserver(&event_observer
);
217 EXPECT_CALL(*mock_remote_service(), GetCurrentState())
219 .WillRepeatedly(Return(state_to_notify
));
221 EXPECT_CALL(*mock_remote_service(),
222 RegisterOrigin(GURL(kOrigin
), _
))
223 .WillOnce(NotifyStateAndCallback(mock_remote_service(),
227 std::vector
<SyncServiceState
> actual_states
;
228 EXPECT_CALL(event_observer
, OnSyncStateUpdated(GURL(), _
, _
))
229 .WillRepeatedly(RecordState(&actual_states
));
231 SyncStatusCode actual_status
= SYNC_STATUS_UNKNOWN
;
232 base::RunLoop run_loop
;
233 sync_service_
->InitializeForApp(
234 file_system_
->file_system_context(),
236 AssignAndQuitCallback(&run_loop
, &actual_status
));
239 EXPECT_EQ(expected_status
, actual_status
);
240 ASSERT_EQ(expected_states
.size(), actual_states
.size());
241 for (size_t i
= 0; i
< actual_states
.size(); ++i
)
242 EXPECT_EQ(expected_states
[i
], actual_states
[i
]);
244 sync_service_
->RemoveSyncEventObserver(&event_observer
);
247 FileSystemURL
URL(const std::string
& path
) const {
248 return file_system_
->URL(path
);
251 StrictMock
<MockRemoteFileSyncService
>* mock_remote_service() {
252 return remote_service_
;
255 StrictMock
<MockLocalChangeProcessor
>* mock_local_change_processor() {
256 return &local_change_processor_
;
260 EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(true)).Times(1);
261 sync_service_
->SetSyncEnabledForTesting(true);
264 content::TestBrowserThreadBundle thread_bundle_
;
265 scoped_ptr
<leveldb::Env
> in_memory_env_
;
266 TestingProfile profile_
;
267 scoped_ptr
<CannedSyncableFileSystem
> file_system_
;
269 // Their ownerships are transferred to SyncFileSystemService.
270 StrictMock
<MockRemoteFileSyncService
>* remote_service_
;
271 StrictMock
<MockLocalChangeProcessor
> local_change_processor_
;
273 scoped_ptr
<SyncFileSystemService
> sync_service_
;
276 TEST_F(SyncFileSystemServiceTest
, InitializeForApp
) {
280 TEST_F(SyncFileSystemServiceTest
, InitializeForAppSuccess
) {
281 std::vector
<SyncServiceState
> expected_states
;
282 expected_states
.push_back(SYNC_SERVICE_RUNNING
);
284 InitializeAppForObserverTest(
291 TEST_F(SyncFileSystemServiceTest
, InitializeForAppWithNetworkFailure
) {
292 std::vector
<SyncServiceState
> expected_states
;
293 expected_states
.push_back(SYNC_SERVICE_TEMPORARY_UNAVAILABLE
);
295 // Notify REMOTE_SERVICE_TEMPORARY_UNAVAILABLE and callback with
296 // SYNC_STATUS_NETWORK_ERROR. This should let the
297 // InitializeApp fail.
298 InitializeAppForObserverTest(
299 REMOTE_SERVICE_TEMPORARY_UNAVAILABLE
,
300 SYNC_STATUS_NETWORK_ERROR
,
302 SYNC_STATUS_NETWORK_ERROR
);
305 TEST_F(SyncFileSystemServiceTest
, InitializeForAppWithError
) {
306 std::vector
<SyncServiceState
> expected_states
;
307 expected_states
.push_back(SYNC_SERVICE_DISABLED
);
309 // Notify REMOTE_SERVICE_DISABLED and callback with
310 // SYNC_STATUS_FAILED. This should let the InitializeApp fail.
311 InitializeAppForObserverTest(
312 REMOTE_SERVICE_DISABLED
,
318 TEST_F(SyncFileSystemServiceTest
, SimpleLocalSyncFlow
) {
321 StrictMock
<MockSyncStatusObserver
> status_observer
;
324 file_system_
->backend()->sync_context()->
325 set_mock_notify_changes_duration_in_sec(0);
326 file_system_
->AddSyncStatusObserver(&status_observer
);
328 // We'll test one local sync for this file.
329 const FileSystemURL
kFile(file_system_
->URL("foo"));
331 base::RunLoop run_loop
;
333 // We should get called OnSyncEnabled and OnWriteEnabled on kFile as in:
334 // 1. OnWriteEnabled when PrepareForSync(SYNC_SHARED) is finished and
335 // the target file is unlocked for writing
336 // 2. OnSyncEnabled x 3 times; 1) when CreateFile is finished, 2) when
337 // file is unlocked after PrepareForSync, and 3) when the sync is
339 EXPECT_CALL(status_observer
, OnWriteEnabled(kFile
))
343 ::testing::InSequence sequence
;
344 EXPECT_CALL(status_observer
, OnSyncEnabled(kFile
))
346 EXPECT_CALL(status_observer
, OnSyncEnabled(kFile
))
347 .WillOnce(InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
));
350 // The local_change_processor's ApplyLocalChange should be called once
351 // with ADD_OR_UPDATE change for TYPE_FILE.
352 const FileChange
change(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
353 SYNC_FILE_TYPE_FILE
);
354 EXPECT_CALL(*mock_local_change_processor(),
355 ApplyLocalChange(change
, _
, _
, kFile
, _
))
356 .WillOnce(MockStatusCallback(SYNC_STATUS_OK
));
357 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_
))
358 .WillRepeatedly(MockSyncFileCallback(SYNC_STATUS_NO_CHANGE_TO_SYNC
,
361 EXPECT_CALL(*mock_remote_service(), PromoteDemotedChanges(_
))
362 .WillRepeatedly(InvokeCompletionClosure());
364 EXPECT_EQ(base::File::FILE_OK
, file_system_
->CreateFile(kFile
));
368 file_system_
->RemoveSyncStatusObserver(&status_observer
);
371 TEST_F(SyncFileSystemServiceTest
, SimpleRemoteSyncFlow
) {
376 base::RunLoop run_loop
;
378 // We expect a set of method calls for starting a remote sync.
379 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_
))
380 .WillOnce(InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
));
382 // This should trigger a remote sync.
383 mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
388 TEST_F(SyncFileSystemServiceTest
, SimpleSyncFlowWithFileBusy
) {
392 file_system_
->backend()->sync_context()->
393 set_mock_notify_changes_duration_in_sec(0);
395 const FileSystemURL
kFile(file_system_
->URL("foo"));
397 base::RunLoop run_loop
;
402 // Return with SYNC_STATUS_FILE_BUSY once.
403 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_
))
404 .WillOnce(MockSyncFileCallback(SYNC_STATUS_FILE_BUSY
,
407 // ProcessRemoteChange should be called again when the becomes
409 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_
))
410 .WillOnce(InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
));
413 EXPECT_CALL(*mock_remote_service(), PromoteDemotedChanges(_
))
414 .WillRepeatedly(InvokeCompletionClosure());
416 // We might also see an activity for local sync as we're going to make
417 // a local write operation on kFile.
418 EXPECT_CALL(*mock_local_change_processor(),
419 ApplyLocalChange(_
, _
, _
, kFile
, _
))
422 // This should trigger a remote sync.
423 mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
425 // Start a local operation on the same file (to make it BUSY).
426 base::RunLoop verify_file_error_run_loop
;
427 BrowserThread::PostTask(
430 base::Bind(&CannedSyncableFileSystem::DoCreateFile
,
431 base::Unretained(file_system_
.get()),
432 kFile
, base::Bind(&VerifyFileError
,
433 &verify_file_error_run_loop
)));
437 mock_remote_service()->NotifyRemoteChangeQueueUpdated(0);
439 verify_file_error_run_loop
.Run();
442 #if defined(THREAD_SANITIZER)
443 // SyncFileSystemServiceTest.GetFileSyncStatus fails under ThreadSanitizer,
444 // see http://crbug.com/294904.
445 #define MAYBE_GetFileSyncStatus DISABLED_GetFileSyncStatus
447 #define MAYBE_GetFileSyncStatus GetFileSyncStatus
449 TEST_F(SyncFileSystemServiceTest
, MAYBE_GetFileSyncStatus
) {
452 const FileSystemURL
kFile(file_system_
->URL("foo"));
454 SyncStatusCode status
;
455 SyncFileStatus sync_file_status
;
457 // 1. The file is synced state.
459 base::RunLoop run_loop
;
460 status
= SYNC_STATUS_UNKNOWN
;
461 sync_file_status
= SYNC_FILE_STATUS_UNKNOWN
;
462 sync_service_
->GetFileSyncStatus(
464 base::Bind(&AssignValueAndQuit
<SyncFileStatus
>,
465 &run_loop
, &status
, &sync_file_status
));
468 EXPECT_EQ(SYNC_STATUS_OK
, status
);
469 EXPECT_EQ(SYNC_FILE_STATUS_SYNCED
, sync_file_status
);
472 // 2. The file has pending local changes.
474 base::RunLoop run_loop
;
475 EXPECT_EQ(base::File::FILE_OK
, file_system_
->CreateFile(kFile
));
477 status
= SYNC_STATUS_UNKNOWN
;
478 sync_file_status
= SYNC_FILE_STATUS_UNKNOWN
;
479 sync_service_
->GetFileSyncStatus(
481 base::Bind(&AssignValueAndQuit
<SyncFileStatus
>,
482 &run_loop
, &status
, &sync_file_status
));
485 EXPECT_EQ(SYNC_STATUS_OK
, status
);
486 EXPECT_EQ(SYNC_FILE_STATUS_HAS_PENDING_CHANGES
, sync_file_status
);
490 } // namespace sync_file_system