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 "webkit/browser/fileapi/file_system_context.h"
32 using content::BrowserThread
;
33 using fileapi::FileSystemURL
;
34 using fileapi::FileSystemURLSet
;
35 using ::testing::AnyNumber
;
36 using ::testing::AtLeast
;
37 using ::testing::InSequence
;
38 using ::testing::InvokeWithoutArgs
;
39 using ::testing::Return
;
40 using ::testing::StrictMock
;
43 namespace sync_file_system
{
47 const char kOrigin
[] = "http://example.com";
49 template <typename R
> struct AssignTrait
{
50 typedef const R
& ArgumentType
;
53 template <> struct AssignTrait
<SyncFileStatus
> {
54 typedef SyncFileStatus ArgumentType
;
58 void AssignValueAndQuit(base::RunLoop
* run_loop
,
59 SyncStatusCode
* status_out
, R
* value_out
,
60 SyncStatusCode status
,
61 typename AssignTrait
<R
>::ArgumentType value
) {
70 // This is called on IO thread.
71 void VerifyFileError(base::RunLoop
* run_loop
,
72 base::PlatformFileError error
) {
74 EXPECT_EQ(base::PLATFORM_FILE_OK
, error
);
80 class MockSyncEventObserver
: public SyncEventObserver
{
82 MockSyncEventObserver() {}
83 virtual ~MockSyncEventObserver() {}
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
,
93 SyncDirection direction
));
96 ACTION_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
));
104 ACTION_P(RecordState
, states
) {
105 states
->push_back(arg1
);
108 ACTION_P(MockStatusCallback
, status
) {
109 base::MessageLoopProxy::current()->PostTask(
110 FROM_HERE
, base::Bind(arg4
, status
));
113 ACTION_P2(MockSyncFileCallback
, status
, url
) {
114 base::MessageLoopProxy::current()->PostTask(
115 FROM_HERE
, base::Bind(arg0
, status
, url
));
118 class SyncFileSystemServiceTest
: public testing::Test
{
120 SyncFileSystemServiceTest()
121 : thread_bundle_(content::TestBrowserThreadBundle::REAL_FILE_THREAD
|
122 content::TestBrowserThreadBundle::REAL_IO_THREAD
) {}
124 virtual void SetUp() OVERRIDE
{
125 file_system_
.reset(new CannedSyncableFileSystem(
127 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO
),
128 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE)));
130 local_service_
= new LocalFileSyncService(&profile_
);
131 remote_service_
= new StrictMock
<MockRemoteFileSyncService
>;
132 sync_service_
.reset(new SyncFileSystemService(&profile_
));
134 EXPECT_CALL(*mock_remote_service(),
135 AddServiceObserver(_
)).Times(1);
136 EXPECT_CALL(*mock_remote_service(),
137 AddFileStatusObserver(sync_service_
.get())).Times(1);
138 EXPECT_CALL(*mock_remote_service(),
139 GetLocalChangeProcessor())
140 .WillRepeatedly(Return(&local_change_processor_
));
141 EXPECT_CALL(*mock_remote_service(),
142 SetRemoteChangeProcessor(local_service_
)).Times(1);
144 sync_service_
->Initialize(
145 make_scoped_ptr(local_service_
),
146 scoped_ptr
<RemoteFileSyncService
>(remote_service_
));
148 // Disable auto sync by default.
149 EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(false)).Times(1);
150 sync_service_
->SetSyncEnabledForTesting(false);
152 file_system_
->SetUp();
155 virtual void TearDown() OVERRIDE
{
156 sync_service_
->Shutdown();
157 file_system_
->TearDown();
158 RevokeSyncableFileSystem();
159 content::RunAllPendingInMessageLoop(BrowserThread::FILE);
162 void InitializeApp() {
163 base::RunLoop run_loop
;
164 SyncStatusCode status
= SYNC_STATUS_UNKNOWN
;
166 EXPECT_CALL(*mock_remote_service(),
167 RegisterOrigin(GURL(kOrigin
), _
)).Times(1);
169 // GetCurrentState may be called when a remote or local sync is scheduled
170 // by change notifications or by a timer.
171 EXPECT_CALL(*mock_remote_service(), GetCurrentState())
173 .WillRepeatedly(Return(REMOTE_SERVICE_OK
));
175 sync_service_
->InitializeForApp(
176 file_system_
->file_system_context(),
178 AssignAndQuitCallback(&run_loop
, &status
));
181 EXPECT_EQ(SYNC_STATUS_OK
, status
);
182 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_system_
->OpenFileSystem());
185 // Calls InitializeForApp after setting up the mock remote service to
186 // perform following when RegisterOrigin is called:
187 // 1. Notify RemoteFileSyncService's observers of |state_to_notify|
188 // 2. Run the given callback with |status_to_return|.
190 // ..and verifies if following conditions are met:
191 // 1. The SyncEventObserver of the service is called with
192 // |expected_states| service state values.
193 // 2. InitializeForApp's callback is called with |expected_status|
194 void InitializeAppForObserverTest(
195 RemoteServiceState state_to_notify
,
196 SyncStatusCode status_to_return
,
197 const std::vector
<SyncServiceState
>& expected_states
,
198 SyncStatusCode expected_status
) {
199 StrictMock
<MockSyncEventObserver
> event_observer
;
200 sync_service_
->AddSyncEventObserver(&event_observer
);
204 EXPECT_CALL(*mock_remote_service(), GetCurrentState())
206 .WillRepeatedly(Return(state_to_notify
));
208 EXPECT_CALL(*mock_remote_service(),
209 RegisterOrigin(GURL(kOrigin
), _
))
210 .WillOnce(NotifyStateAndCallback(mock_remote_service(),
214 std::vector
<SyncServiceState
> actual_states
;
215 EXPECT_CALL(event_observer
, OnSyncStateUpdated(GURL(), _
, _
))
216 .WillRepeatedly(RecordState(&actual_states
));
218 SyncStatusCode actual_status
= SYNC_STATUS_UNKNOWN
;
219 base::RunLoop run_loop
;
220 sync_service_
->InitializeForApp(
221 file_system_
->file_system_context(),
223 AssignAndQuitCallback(&run_loop
, &actual_status
));
226 EXPECT_EQ(expected_status
, actual_status
);
227 ASSERT_EQ(expected_states
.size(), actual_states
.size());
228 for (size_t i
= 0; i
< actual_states
.size(); ++i
)
229 EXPECT_EQ(expected_states
[i
], actual_states
[i
]);
231 sync_service_
->RemoveSyncEventObserver(&event_observer
);
234 FileSystemURL
URL(const std::string
& path
) const {
235 return file_system_
->URL(path
);
238 StrictMock
<MockRemoteFileSyncService
>* mock_remote_service() {
239 return remote_service_
;
242 StrictMock
<MockLocalChangeProcessor
>* mock_local_change_processor() {
243 return &local_change_processor_
;
247 EXPECT_CALL(*mock_remote_service(), SetSyncEnabled(true)).Times(1);
248 sync_service_
->SetSyncEnabledForTesting(true);
251 ScopedEnableSyncFSDirectoryOperation enable_directory_operation_
;
253 content::TestBrowserThreadBundle thread_bundle_
;
254 TestingProfile profile_
;
255 scoped_ptr
<CannedSyncableFileSystem
> file_system_
;
257 // Their ownerships are transferred to SyncFileSystemService.
258 LocalFileSyncService
* local_service_
;
259 StrictMock
<MockRemoteFileSyncService
>* remote_service_
;
260 StrictMock
<MockLocalChangeProcessor
> local_change_processor_
;
262 scoped_ptr
<SyncFileSystemService
> sync_service_
;
265 TEST_F(SyncFileSystemServiceTest
, InitializeForApp
) {
269 TEST_F(SyncFileSystemServiceTest
, InitializeForAppSuccess
) {
270 std::vector
<SyncServiceState
> expected_states
;
271 expected_states
.push_back(SYNC_SERVICE_RUNNING
);
273 InitializeAppForObserverTest(
280 TEST_F(SyncFileSystemServiceTest
, InitializeForAppWithNetworkFailure
) {
281 std::vector
<SyncServiceState
> expected_states
;
282 expected_states
.push_back(SYNC_SERVICE_TEMPORARY_UNAVAILABLE
);
284 // Notify REMOTE_SERVICE_TEMPORARY_UNAVAILABLE and callback with
285 // SYNC_STATUS_NETWORK_ERROR. This should let the
286 // InitializeApp fail.
287 InitializeAppForObserverTest(
288 REMOTE_SERVICE_TEMPORARY_UNAVAILABLE
,
289 SYNC_STATUS_NETWORK_ERROR
,
291 SYNC_STATUS_NETWORK_ERROR
);
294 TEST_F(SyncFileSystemServiceTest
, InitializeForAppWithError
) {
295 std::vector
<SyncServiceState
> expected_states
;
296 expected_states
.push_back(SYNC_SERVICE_DISABLED
);
298 // Notify REMOTE_SERVICE_DISABLED and callback with
299 // SYNC_STATUS_FAILED. This should let the InitializeApp fail.
300 InitializeAppForObserverTest(
301 REMOTE_SERVICE_DISABLED
,
307 TEST_F(SyncFileSystemServiceTest
, SimpleLocalSyncFlow
) {
310 StrictMock
<MockSyncStatusObserver
> status_observer
;
313 file_system_
->backend()->sync_context()->
314 set_mock_notify_changes_duration_in_sec(0);
315 file_system_
->AddSyncStatusObserver(&status_observer
);
317 // We'll test one local sync for this file.
318 const FileSystemURL
kFile(file_system_
->URL("foo"));
320 base::RunLoop run_loop
;
322 // We should get called OnSyncEnabled and OnWriteEnabled on kFile as in:
323 // 1. OnWriteEnabled when PrepareForSync(SYNC_SHARED) is finished and
324 // the target file is unlocked for writing
325 // 2. OnSyncEnabled x 3 times; 1) when CreateFile is finished, 2) when
326 // file is unlocked after PrepareForSync, and 3) when the sync is
328 EXPECT_CALL(status_observer
, OnWriteEnabled(kFile
))
332 ::testing::InSequence sequence
;
333 EXPECT_CALL(status_observer
, OnSyncEnabled(kFile
))
335 EXPECT_CALL(status_observer
, OnSyncEnabled(kFile
))
336 .WillOnce(InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
));
339 // The local_change_processor's ApplyLocalChange should be called once
340 // with ADD_OR_UPDATE change for TYPE_FILE.
341 const FileChange
change(FileChange::FILE_CHANGE_ADD_OR_UPDATE
,
342 SYNC_FILE_TYPE_FILE
);
343 EXPECT_CALL(*mock_local_change_processor(),
344 ApplyLocalChange(change
, _
, _
, kFile
, _
))
345 .WillOnce(MockStatusCallback(SYNC_STATUS_OK
));
347 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_system_
->CreateFile(kFile
));
351 file_system_
->RemoveSyncStatusObserver(&status_observer
);
354 TEST_F(SyncFileSystemServiceTest
, SimpleRemoteSyncFlow
) {
359 base::RunLoop run_loop
;
361 // We expect a set of method calls for starting a remote sync.
362 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_
))
363 .WillOnce(InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
));
365 // This should trigger a remote sync.
366 mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
371 TEST_F(SyncFileSystemServiceTest
, SimpleSyncFlowWithFileBusy
) {
375 file_system_
->backend()->sync_context()->
376 set_mock_notify_changes_duration_in_sec(0);
378 const FileSystemURL
kFile(file_system_
->URL("foo"));
380 base::RunLoop run_loop
;
385 // Return with SYNC_STATUS_FILE_BUSY once.
386 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_
))
387 .WillOnce(MockSyncFileCallback(SYNC_STATUS_FILE_BUSY
,
390 // ProcessRemoteChange should be called again when the becomes
392 EXPECT_CALL(*mock_remote_service(), ProcessRemoteChange(_
))
393 .WillOnce(InvokeWithoutArgs(&run_loop
, &base::RunLoop::Quit
));
396 // We might also see an activity for local sync as we're going to make
397 // a local write operation on kFile.
398 EXPECT_CALL(*mock_local_change_processor(),
399 ApplyLocalChange(_
, _
, _
, kFile
, _
))
402 // This should trigger a remote sync.
403 mock_remote_service()->NotifyRemoteChangeQueueUpdated(1);
405 // Start a local operation on the same file (to make it BUSY).
406 base::RunLoop verify_file_error_run_loop
;
407 BrowserThread::PostTask(
410 base::Bind(&CannedSyncableFileSystem::DoCreateFile
,
411 base::Unretained(file_system_
.get()),
412 kFile
, base::Bind(&VerifyFileError
,
413 &verify_file_error_run_loop
)));
417 mock_remote_service()->NotifyRemoteChangeQueueUpdated(0);
419 verify_file_error_run_loop
.Run();
422 #if defined(THREAD_SANITIZER)
423 // SyncFileSystemServiceTest.GetFileSyncStatus fails under ThreadSanitizer,
424 // see http://crbug.com/294904.
425 #define MAYBE_GetFileSyncStatus DISABLED_GetFileSyncStatus
427 #define MAYBE_GetFileSyncStatus GetFileSyncStatus
429 TEST_F(SyncFileSystemServiceTest
, MAYBE_GetFileSyncStatus
) {
432 const FileSystemURL
kFile(file_system_
->URL("foo"));
434 SyncStatusCode status
;
435 SyncFileStatus sync_file_status
;
437 // 1. The file is not in conflicting nor in pending change state.
439 base::RunLoop run_loop
;
440 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile
))
441 .WillOnce(Return(false));
443 status
= SYNC_STATUS_UNKNOWN
;
444 sync_file_status
= SYNC_FILE_STATUS_UNKNOWN
;
445 sync_service_
->GetFileSyncStatus(
447 base::Bind(&AssignValueAndQuit
<SyncFileStatus
>,
448 &run_loop
, &status
, &sync_file_status
));
451 EXPECT_EQ(SYNC_STATUS_OK
, status
);
452 EXPECT_EQ(SYNC_FILE_STATUS_SYNCED
, sync_file_status
);
455 // 2. Conflicting case.
457 base::RunLoop run_loop
;
458 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile
))
459 .WillOnce(Return(true));
461 status
= SYNC_STATUS_UNKNOWN
;
462 sync_file_status
= SYNC_FILE_STATUS_UNKNOWN
;
463 sync_service_
->GetFileSyncStatus(
465 base::Bind(&AssignValueAndQuit
<SyncFileStatus
>,
466 &run_loop
, &status
, &sync_file_status
));
469 EXPECT_EQ(SYNC_STATUS_OK
, status
);
470 EXPECT_EQ(SYNC_FILE_STATUS_CONFLICTING
, sync_file_status
);
473 // 3. The file has pending local changes.
475 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_system_
->CreateFile(kFile
));
477 base::RunLoop run_loop
;
478 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile
))
479 .WillOnce(Return(false));
481 status
= SYNC_STATUS_UNKNOWN
;
482 sync_file_status
= SYNC_FILE_STATUS_UNKNOWN
;
483 sync_service_
->GetFileSyncStatus(
485 base::Bind(&AssignValueAndQuit
<SyncFileStatus
>,
486 &run_loop
, &status
, &sync_file_status
));
489 EXPECT_EQ(SYNC_STATUS_OK
, status
);
490 EXPECT_EQ(SYNC_FILE_STATUS_HAS_PENDING_CHANGES
, sync_file_status
);
493 // 4. The file has a conflict and pending local changes. In this case
494 // we return SYNC_FILE_STATUS_CONFLICTING.
496 EXPECT_EQ(base::PLATFORM_FILE_OK
, file_system_
->TruncateFile(kFile
, 1U));
498 base::RunLoop run_loop
;
499 EXPECT_CALL(*mock_remote_service(), IsConflicting(kFile
))
500 .WillOnce(Return(true));
502 status
= SYNC_STATUS_UNKNOWN
;
503 sync_file_status
= SYNC_FILE_STATUS_UNKNOWN
;
504 sync_service_
->GetFileSyncStatus(
506 base::Bind(&AssignValueAndQuit
<SyncFileStatus
>,
507 &run_loop
, &status
, &sync_file_status
));
510 EXPECT_EQ(SYNC_STATUS_OK
, status
);
511 EXPECT_EQ(SYNC_FILE_STATUS_CONFLICTING
, sync_file_status
);
515 } // namespace sync_file_system