1 // Copyright 2014 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.
5 #include "components/sync_driver/generic_change_processor.h"
7 #include "base/memory/scoped_ptr.h"
8 #include "base/memory/weak_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/strings/stringprintf.h"
12 #include "components/sync_driver/data_type_error_handler_mock.h"
13 #include "components/sync_driver/fake_sync_client.h"
14 #include "components/sync_driver/local_device_info_provider.h"
15 #include "components/sync_driver/sync_api_component_factory.h"
16 #include "sync/api/attachments/attachment_id.h"
17 #include "sync/api/attachments/attachment_store.h"
18 #include "sync/api/fake_syncable_service.h"
19 #include "sync/api/sync_change.h"
20 #include "sync/api/sync_merge_result.h"
21 #include "sync/internal_api/public/attachments/attachment_service_impl.h"
22 #include "sync/internal_api/public/attachments/fake_attachment_downloader.h"
23 #include "sync/internal_api/public/attachments/fake_attachment_uploader.h"
24 #include "sync/internal_api/public/base/model_type.h"
25 #include "sync/internal_api/public/read_node.h"
26 #include "sync/internal_api/public/read_transaction.h"
27 #include "sync/internal_api/public/sync_encryption_handler.h"
28 #include "sync/internal_api/public/test/test_user_share.h"
29 #include "sync/internal_api/public/user_share.h"
30 #include "sync/internal_api/public/write_node.h"
31 #include "sync/internal_api/public/write_transaction.h"
32 #include "testing/gmock/include/gmock/gmock-matchers.h"
33 #include "testing/gtest/include/gtest/gtest.h"
35 namespace sync_driver
{
39 // A mock that keeps track of attachments passed to UploadAttachments.
40 class MockAttachmentService
: public syncer::AttachmentServiceImpl
{
42 MockAttachmentService(
43 scoped_ptr
<syncer::AttachmentStoreForSync
> attachment_store
);
44 ~MockAttachmentService() override
;
45 void UploadAttachments(
46 const syncer::AttachmentIdList
& attachment_ids
) override
;
47 std::vector
<syncer::AttachmentIdList
>* attachment_id_lists();
50 std::vector
<syncer::AttachmentIdList
> attachment_id_lists_
;
53 MockAttachmentService::MockAttachmentService(
54 scoped_ptr
<syncer::AttachmentStoreForSync
> attachment_store
)
55 : AttachmentServiceImpl(attachment_store
.Pass(),
56 scoped_ptr
<syncer::AttachmentUploader
>(
57 new syncer::FakeAttachmentUploader
),
58 scoped_ptr
<syncer::AttachmentDownloader
>(
59 new syncer::FakeAttachmentDownloader
),
65 MockAttachmentService::~MockAttachmentService() {
68 void MockAttachmentService::UploadAttachments(
69 const syncer::AttachmentIdList
& attachment_ids
) {
70 attachment_id_lists_
.push_back(attachment_ids
);
71 AttachmentServiceImpl::UploadAttachments(attachment_ids
);
74 std::vector
<syncer::AttachmentIdList
>*
75 MockAttachmentService::attachment_id_lists() {
76 return &attachment_id_lists_
;
79 // MockSyncApiComponentFactory needed to initialize GenericChangeProcessor and
80 // pass MockAttachmentService to it.
81 class MockSyncApiComponentFactory
: public SyncApiComponentFactory
{
83 MockSyncApiComponentFactory() {}
85 // SyncApiComponentFactory implementation.
86 void Initialize(SyncService
* sync_service
) override
{}
87 void RegisterDataTypes() override
{}
88 sync_driver::DataTypeManager
* CreateDataTypeManager(
89 const syncer::WeakHandle
<syncer::DataTypeDebugInfoListener
>&
91 const sync_driver::DataTypeController::TypeMap
* controllers
,
92 const sync_driver::DataTypeEncryptionHandler
* encryption_handler
,
93 browser_sync::SyncBackendHost
* backend
,
94 sync_driver::DataTypeManagerObserver
* observer
) override
{
97 browser_sync::SyncBackendHost
* CreateSyncBackendHost(
98 const std::string
& name
,
99 invalidation::InvalidationService
* invalidator
,
100 const base::WeakPtr
<sync_driver::SyncPrefs
>& sync_prefs
,
101 const base::FilePath
& sync_folder
) override
{
104 scoped_ptr
<sync_driver::LocalDeviceInfoProvider
>
105 CreateLocalDeviceInfoProvider() override
{ return nullptr; }
106 SyncComponents
CreateBookmarkSyncComponents(
107 sync_driver::SyncService
* sync_service
,
108 sync_driver::DataTypeErrorHandler
* error_handler
) override
{
109 return SyncComponents(nullptr, nullptr);
111 SyncComponents
CreateTypedUrlSyncComponents(
112 sync_driver::SyncService
* sync_service
,
113 history::HistoryBackend
* history_backend
,
114 sync_driver::DataTypeErrorHandler
* error_handler
) override
{
115 return SyncComponents(nullptr, nullptr);
118 scoped_ptr
<syncer::AttachmentService
> CreateAttachmentService(
119 scoped_ptr
<syncer::AttachmentStoreForSync
> attachment_store
,
120 const syncer::UserShare
& user_share
,
121 const std::string
& store_birthday
,
122 syncer::ModelType model_type
,
123 syncer::AttachmentService::Delegate
* delegate
) override
{
124 scoped_ptr
<MockAttachmentService
> attachment_service(
125 new MockAttachmentService(attachment_store
.Pass()));
126 // GenericChangeProcessor takes ownership of the AttachmentService, but we
127 // need to have a pointer to it so we can see that it was used properly.
128 // Take a pointer and trust that GenericChangeProcessor does not prematurely
130 mock_attachment_service_
= attachment_service
.get();
131 return attachment_service
.Pass();
134 MockAttachmentService
* GetMockAttachmentService() {
135 return mock_attachment_service_
;
139 MockAttachmentService
* mock_attachment_service_
;
142 class SyncGenericChangeProcessorTest
: public testing::Test
{
144 // Most test cases will use this type. For those that need a
145 // GenericChangeProcessor for a different type, use |InitializeForType|.
146 static const syncer::ModelType kType
= syncer::PREFERENCES
;
148 SyncGenericChangeProcessorTest()
149 : syncable_service_ptr_factory_(&fake_syncable_service_
),
150 mock_attachment_service_(NULL
),
151 sync_client_(&sync_factory_
) {}
153 void SetUp() override
{
154 // Use kType by default, but allow test cases to re-initialize with whatever
155 // type they choose. Therefore, it's important that all type dependent
156 // initialization occurs in InitializeForType.
157 InitializeForType(kType
);
160 void TearDown() override
{
161 mock_attachment_service_
= NULL
;
162 if (test_user_share_
) {
163 test_user_share_
->TearDown();
167 // Initialize GenericChangeProcessor and related classes for testing with
168 // model type |type|.
169 void InitializeForType(syncer::ModelType type
) {
171 test_user_share_
.reset(new syncer::TestUserShare
);
172 test_user_share_
->SetUp();
173 sync_merge_result_
.reset(new syncer::SyncMergeResult(type
));
174 merge_result_ptr_factory_
.reset(
175 new base::WeakPtrFactory
<syncer::SyncMergeResult
>(
176 sync_merge_result_
.get()));
178 syncer::ModelTypeSet types
= syncer::ProtocolTypes();
179 for (syncer::ModelTypeSet::Iterator iter
= types
.First(); iter
.Good();
181 syncer::TestUserShare::CreateRoot(iter
.Get(),
182 test_user_share_
->user_share());
184 test_user_share_
->encryption_handler()->Init();
185 ConstructGenericChangeProcessor(type
);
188 void ConstructGenericChangeProcessor(syncer::ModelType type
) {
189 scoped_ptr
<syncer::AttachmentStore
> attachment_store
=
190 syncer::AttachmentStore::CreateInMemoryStore();
191 change_processor_
.reset(new GenericChangeProcessor(
192 type
, &data_type_error_handler_
,
193 syncable_service_ptr_factory_
.GetWeakPtr(),
194 merge_result_ptr_factory_
->GetWeakPtr(), test_user_share_
->user_share(),
195 &sync_client_
, attachment_store
->CreateAttachmentStoreForSync()));
196 mock_attachment_service_
= sync_factory_
.GetMockAttachmentService();
199 void BuildChildNodes(syncer::ModelType type
, int n
) {
200 syncer::WriteTransaction
trans(FROM_HERE
, user_share());
201 for (int i
= 0; i
< n
; ++i
) {
202 syncer::WriteNode
node(&trans
);
203 node
.InitUniqueByCreation(type
, base::StringPrintf("node%05d", i
));
207 GenericChangeProcessor
* change_processor() {
208 return change_processor_
.get();
211 syncer::UserShare
* user_share() {
212 return test_user_share_
->user_share();
215 MockAttachmentService
* mock_attachment_service() {
216 return mock_attachment_service_
;
220 base::RunLoop run_loop
;
221 run_loop
.RunUntilIdle();
225 base::MessageLoopForUI loop_
;
227 scoped_ptr
<syncer::SyncMergeResult
> sync_merge_result_
;
228 scoped_ptr
<base::WeakPtrFactory
<syncer::SyncMergeResult
> >
229 merge_result_ptr_factory_
;
231 syncer::FakeSyncableService fake_syncable_service_
;
232 base::WeakPtrFactory
<syncer::FakeSyncableService
>
233 syncable_service_ptr_factory_
;
235 DataTypeErrorHandlerMock data_type_error_handler_
;
236 scoped_ptr
<syncer::TestUserShare
> test_user_share_
;
237 MockAttachmentService
* mock_attachment_service_
;
238 FakeSyncClient sync_client_
;
239 MockSyncApiComponentFactory sync_factory_
;
241 scoped_ptr
<GenericChangeProcessor
> change_processor_
;
244 // Similar to above, but focused on the method that implements sync/api
245 // interfaces and is hence exposed to datatypes directly.
246 TEST_F(SyncGenericChangeProcessorTest
, StressGetAllSyncData
) {
247 const int kNumChildNodes
= 1000;
248 const int kRepeatCount
= 1;
250 ASSERT_NO_FATAL_FAILURE(BuildChildNodes(kType
, kNumChildNodes
));
252 for (int i
= 0; i
< kRepeatCount
; ++i
) {
253 syncer::SyncDataList sync_data
=
254 change_processor()->GetAllSyncData(kType
);
256 // Start with a simple test. We can add more in-depth testing later.
257 EXPECT_EQ(static_cast<size_t>(kNumChildNodes
), sync_data
.size());
261 TEST_F(SyncGenericChangeProcessorTest
, SetGetPasswords
) {
262 InitializeForType(syncer::PASSWORDS
);
263 const int kNumPasswords
= 10;
264 sync_pb::PasswordSpecificsData password_data
;
265 password_data
.set_username_value("user");
267 sync_pb::EntitySpecifics password_holder
;
269 syncer::SyncChangeList change_list
;
270 for (int i
= 0; i
< kNumPasswords
; ++i
) {
271 password_data
.set_password_value(
272 base::StringPrintf("password%i", i
));
273 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
274 CopyFrom(password_data
);
275 change_list
.push_back(
276 syncer::SyncChange(FROM_HERE
,
277 syncer::SyncChange::ACTION_ADD
,
278 syncer::SyncData::CreateLocalData(
279 base::StringPrintf("tag%i", i
),
280 base::StringPrintf("title%i", i
),
285 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
287 syncer::SyncDataList
password_list(
288 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
290 ASSERT_EQ(password_list
.size(), change_list
.size());
291 for (int i
= 0; i
< kNumPasswords
; ++i
) {
292 // Verify the password is returned properly.
293 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
294 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
295 has_client_only_encrypted_data());
296 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
297 const sync_pb::PasswordSpecificsData
& sync_password
=
298 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
299 const sync_pb::PasswordSpecificsData
& change_password
=
300 change_list
[i
].sync_data().GetSpecifics().password().
301 client_only_encrypted_data();
302 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
303 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
305 // Verify the raw sync data was stored securely.
306 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
307 syncer::ReadNode
node(&read_transaction
);
308 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
309 base::StringPrintf("tag%i", i
)),
310 syncer::BaseNode::INIT_OK
);
311 ASSERT_EQ(node
.GetTitle(), "encrypted");
312 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
313 ASSERT_TRUE(raw_specifics
.has_password());
314 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
315 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
319 TEST_F(SyncGenericChangeProcessorTest
, UpdatePasswords
) {
320 InitializeForType(syncer::PASSWORDS
);
321 const int kNumPasswords
= 10;
322 sync_pb::PasswordSpecificsData password_data
;
323 password_data
.set_username_value("user");
325 sync_pb::EntitySpecifics password_holder
;
327 syncer::SyncChangeList change_list
;
328 syncer::SyncChangeList change_list2
;
329 for (int i
= 0; i
< kNumPasswords
; ++i
) {
330 password_data
.set_password_value(
331 base::StringPrintf("password%i", i
));
332 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
333 CopyFrom(password_data
);
334 change_list
.push_back(
335 syncer::SyncChange(FROM_HERE
,
336 syncer::SyncChange::ACTION_ADD
,
337 syncer::SyncData::CreateLocalData(
338 base::StringPrintf("tag%i", i
),
339 base::StringPrintf("title%i", i
),
341 password_data
.set_password_value(
342 base::StringPrintf("password_m%i", i
));
343 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
344 CopyFrom(password_data
);
345 change_list2
.push_back(
346 syncer::SyncChange(FROM_HERE
,
347 syncer::SyncChange::ACTION_UPDATE
,
348 syncer::SyncData::CreateLocalData(
349 base::StringPrintf("tag%i", i
),
350 base::StringPrintf("title_m%i", i
),
355 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
357 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list2
).IsSet());
359 syncer::SyncDataList
password_list(
360 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
362 ASSERT_EQ(password_list
.size(), change_list2
.size());
363 for (int i
= 0; i
< kNumPasswords
; ++i
) {
364 // Verify the password is returned properly.
365 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
366 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
367 has_client_only_encrypted_data());
368 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
369 const sync_pb::PasswordSpecificsData
& sync_password
=
370 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
371 const sync_pb::PasswordSpecificsData
& change_password
=
372 change_list2
[i
].sync_data().GetSpecifics().password().
373 client_only_encrypted_data();
374 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
375 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
377 // Verify the raw sync data was stored securely.
378 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
379 syncer::ReadNode
node(&read_transaction
);
380 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
381 base::StringPrintf("tag%i", i
)),
382 syncer::BaseNode::INIT_OK
);
383 ASSERT_EQ(node
.GetTitle(), "encrypted");
384 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
385 ASSERT_TRUE(raw_specifics
.has_password());
386 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
387 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
391 // Verify that attachments on newly added or updated SyncData are passed to the
392 // AttachmentService.
393 TEST_F(SyncGenericChangeProcessorTest
,
394 ProcessSyncChanges_AddUpdateWithAttachment
) {
395 std::string tag
= "client_tag";
396 std::string title
= "client_title";
397 sync_pb::EntitySpecifics specifics
;
398 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
399 pref_specifics
->set_name("test");
401 syncer::AttachmentIdList attachment_ids
;
402 attachment_ids
.push_back(syncer::AttachmentId::Create(0, 0));
403 attachment_ids
.push_back(syncer::AttachmentId::Create(0, 0));
405 // Add a SyncData with two attachments.
406 syncer::SyncChangeList change_list
;
407 change_list
.push_back(
408 syncer::SyncChange(FROM_HERE
,
409 syncer::SyncChange::ACTION_ADD
,
410 syncer::SyncData::CreateLocalDataWithAttachments(
411 tag
, title
, specifics
, attachment_ids
)));
413 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
416 // Check that the AttachmentService received the new attachments.
417 ASSERT_EQ(mock_attachment_service()->attachment_id_lists()->size(), 1U);
418 const syncer::AttachmentIdList
& attachments_added
=
419 mock_attachment_service()->attachment_id_lists()->front();
422 testing::UnorderedElementsAre(attachment_ids
[0], attachment_ids
[1]));
424 // Update the SyncData, replacing its two attachments with one new attachment.
425 syncer::AttachmentIdList new_attachment_ids
;
426 new_attachment_ids
.push_back(syncer::AttachmentId::Create(0, 0));
427 mock_attachment_service()->attachment_id_lists()->clear();
429 change_list
.push_back(
430 syncer::SyncChange(FROM_HERE
,
431 syncer::SyncChange::ACTION_UPDATE
,
432 syncer::SyncData::CreateLocalDataWithAttachments(
433 tag
, title
, specifics
, new_attachment_ids
)));
435 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
438 // Check that the AttachmentService received it.
439 ASSERT_EQ(mock_attachment_service()->attachment_id_lists()->size(), 1U);
440 const syncer::AttachmentIdList
& new_attachments_added
=
441 mock_attachment_service()->attachment_id_lists()->front();
442 ASSERT_THAT(new_attachments_added
,
443 testing::UnorderedElementsAre(new_attachment_ids
[0]));
446 // Verify that after attachment is uploaded GenericChangeProcessor updates
447 // corresponding entries
448 TEST_F(SyncGenericChangeProcessorTest
, AttachmentUploaded
) {
449 std::string tag
= "client_tag";
450 std::string title
= "client_title";
451 sync_pb::EntitySpecifics specifics
;
452 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
453 pref_specifics
->set_name("test");
455 syncer::AttachmentIdList attachment_ids
;
456 attachment_ids
.push_back(syncer::AttachmentId::Create(0, 0));
458 // Add a SyncData with two attachments.
459 syncer::SyncChangeList change_list
;
460 change_list
.push_back(
461 syncer::SyncChange(FROM_HERE
,
462 syncer::SyncChange::ACTION_ADD
,
463 syncer::SyncData::CreateLocalDataWithAttachments(
464 tag
, title
, specifics
, attachment_ids
)));
466 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
468 sync_pb::AttachmentIdProto attachment_id_proto
= attachment_ids
[0].GetProto();
469 syncer::AttachmentId attachment_id
=
470 syncer::AttachmentId::CreateFromProto(attachment_id_proto
);
472 change_processor()->OnAttachmentUploaded(attachment_id
);
473 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
474 syncer::ReadNode
node(&read_transaction
);
475 ASSERT_EQ(node
.InitByClientTagLookup(kType
, tag
), syncer::BaseNode::INIT_OK
);
476 attachment_ids
= node
.GetAttachmentIds();
477 EXPECT_EQ(1U, attachment_ids
.size());
480 // Verify that upon construction, all attachments not yet on the server are
481 // scheduled for upload.
482 TEST_F(SyncGenericChangeProcessorTest
, UploadAllAttachmentsNotOnServer
) {
483 // Create two attachment ids. id2 will be marked as "on server".
484 syncer::AttachmentId id1
= syncer::AttachmentId::Create(0, 0);
485 syncer::AttachmentId id2
= syncer::AttachmentId::Create(0, 0);
487 // Write an entry containing these two attachment ids.
488 syncer::WriteTransaction
trans(FROM_HERE
, user_share());
489 syncer::ReadNode
root(&trans
);
490 ASSERT_EQ(syncer::BaseNode::INIT_OK
, root
.InitTypeRoot(kType
));
491 syncer::WriteNode
node(&trans
);
492 node
.InitUniqueByCreation(kType
, root
, "some node");
493 sync_pb::AttachmentMetadata metadata
;
494 sync_pb::AttachmentMetadataRecord
* record1
= metadata
.add_record();
495 *record1
->mutable_id() = id1
.GetProto();
496 sync_pb::AttachmentMetadataRecord
* record2
= metadata
.add_record();
497 *record2
->mutable_id() = id2
.GetProto();
498 record2
->set_is_on_server(true);
499 node
.SetAttachmentMetadata(metadata
);
502 // Construct the GenericChangeProcessor and see that it asks the
503 // AttachmentService to upload id1 only.
504 ConstructGenericChangeProcessor(kType
);
505 ASSERT_EQ(1U, mock_attachment_service()->attachment_id_lists()->size());
506 ASSERT_THAT(mock_attachment_service()->attachment_id_lists()->front(),
507 testing::UnorderedElementsAre(id1
));
510 // Test that attempting to add an entry that already exists still works.
511 TEST_F(SyncGenericChangeProcessorTest
, AddExistingEntry
) {
512 InitializeForType(syncer::SESSIONS
);
513 sync_pb::EntitySpecifics sessions_specifics
;
514 sessions_specifics
.mutable_session()->set_session_tag("session tag");
515 syncer::SyncChangeList changes
;
517 // First add it normally.
518 changes
.push_back(syncer::SyncChange(
519 FROM_HERE
, syncer::SyncChange::ACTION_ADD
,
520 syncer::SyncData::CreateLocalData(base::StringPrintf("tag"),
521 base::StringPrintf("title"),
522 sessions_specifics
)));
524 change_processor()->ProcessSyncChanges(FROM_HERE
, changes
).IsSet());
526 // Now attempt to add it again, but with different specifics. Should not
527 // result in an error and should still update the specifics.
528 sessions_specifics
.mutable_session()->set_session_tag("session tag 2");
530 syncer::SyncChange(FROM_HERE
, syncer::SyncChange::ACTION_ADD
,
531 syncer::SyncData::CreateLocalData(
532 base::StringPrintf("tag"),
533 base::StringPrintf("title"), sessions_specifics
));
535 change_processor()->ProcessSyncChanges(FROM_HERE
, changes
).IsSet());
537 // Verify the data was updated properly.
538 syncer::SyncDataList sync_data
=
539 change_processor()->GetAllSyncData(syncer::SESSIONS
);
540 ASSERT_EQ(sync_data
.size(), 1U);
541 ASSERT_EQ("session tag 2",
542 sync_data
[0].GetSpecifics().session().session_tag());
547 } // namespace sync_driver