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/sync_api_component_factory.h"
14 #include "sync/api/attachments/attachment_id.h"
15 #include "sync/api/attachments/attachment_store.h"
16 #include "sync/api/fake_syncable_service.h"
17 #include "sync/api/sync_change.h"
18 #include "sync/api/sync_merge_result.h"
19 #include "sync/internal_api/public/attachments/attachment_service_impl.h"
20 #include "sync/internal_api/public/attachments/fake_attachment_downloader.h"
21 #include "sync/internal_api/public/attachments/fake_attachment_uploader.h"
22 #include "sync/internal_api/public/base/model_type.h"
23 #include "sync/internal_api/public/read_node.h"
24 #include "sync/internal_api/public/read_transaction.h"
25 #include "sync/internal_api/public/sync_encryption_handler.h"
26 #include "sync/internal_api/public/test/test_user_share.h"
27 #include "sync/internal_api/public/user_share.h"
28 #include "sync/internal_api/public/write_node.h"
29 #include "sync/internal_api/public/write_transaction.h"
30 #include "testing/gmock/include/gmock/gmock-matchers.h"
31 #include "testing/gtest/include/gtest/gtest.h"
33 namespace sync_driver
{
37 // A mock that keeps track of attachments passed to UploadAttachments.
38 class MockAttachmentService
: public syncer::AttachmentServiceImpl
{
40 MockAttachmentService(
41 scoped_ptr
<syncer::AttachmentStoreForSync
> attachment_store
);
42 ~MockAttachmentService() override
;
43 void UploadAttachments(
44 const syncer::AttachmentIdList
& attachment_ids
) override
;
45 std::vector
<syncer::AttachmentIdList
>* attachment_id_lists();
48 std::vector
<syncer::AttachmentIdList
> attachment_id_lists_
;
51 MockAttachmentService::MockAttachmentService(
52 scoped_ptr
<syncer::AttachmentStoreForSync
> attachment_store
)
53 : AttachmentServiceImpl(attachment_store
.Pass(),
54 scoped_ptr
<syncer::AttachmentUploader
>(
55 new syncer::FakeAttachmentUploader
),
56 scoped_ptr
<syncer::AttachmentDownloader
>(
57 new syncer::FakeAttachmentDownloader
),
63 MockAttachmentService::~MockAttachmentService() {
66 void MockAttachmentService::UploadAttachments(
67 const syncer::AttachmentIdList
& attachment_ids
) {
68 attachment_id_lists_
.push_back(attachment_ids
);
69 AttachmentServiceImpl::UploadAttachments(attachment_ids
);
72 std::vector
<syncer::AttachmentIdList
>*
73 MockAttachmentService::attachment_id_lists() {
74 return &attachment_id_lists_
;
77 // MockSyncApiComponentFactory needed to initialize GenericChangeProcessor and
78 // pass MockAttachmentService to it.
79 class MockSyncApiComponentFactory
: public SyncApiComponentFactory
{
81 MockSyncApiComponentFactory() {}
83 base::WeakPtr
<syncer::SyncableService
> GetSyncableServiceForType(
84 syncer::ModelType type
) override
{
85 // Shouldn't be called for this test.
87 return base::WeakPtr
<syncer::SyncableService
>();
90 scoped_ptr
<syncer::AttachmentService
> CreateAttachmentService(
91 scoped_ptr
<syncer::AttachmentStoreForSync
> attachment_store
,
92 const syncer::UserShare
& user_share
,
93 const std::string
& store_birthday
,
94 syncer::ModelType model_type
,
95 syncer::AttachmentService::Delegate
* delegate
) override
{
96 scoped_ptr
<MockAttachmentService
> attachment_service(
97 new MockAttachmentService(attachment_store
.Pass()));
98 // GenericChangeProcessor takes ownership of the AttachmentService, but we
99 // need to have a pointer to it so we can see that it was used properly.
100 // Take a pointer and trust that GenericChangeProcessor does not prematurely
102 mock_attachment_service_
= attachment_service
.get();
103 return attachment_service
.Pass();
106 MockAttachmentService
* GetMockAttachmentService() {
107 return mock_attachment_service_
;
111 MockAttachmentService
* mock_attachment_service_
;
114 class SyncGenericChangeProcessorTest
: public testing::Test
{
116 // Most test cases will use this type. For those that need a
117 // GenericChangeProcessor for a different type, use |InitializeForType|.
118 static const syncer::ModelType kType
= syncer::PREFERENCES
;
120 SyncGenericChangeProcessorTest()
121 : syncable_service_ptr_factory_(&fake_syncable_service_
),
122 mock_attachment_service_(NULL
) {}
124 void SetUp() override
{
125 // Use kType by default, but allow test cases to re-initialize with whatever
126 // type they choose. Therefore, it's important that all type dependent
127 // initialization occurs in InitializeForType.
128 InitializeForType(kType
);
131 void TearDown() override
{
132 mock_attachment_service_
= NULL
;
133 if (test_user_share_
) {
134 test_user_share_
->TearDown();
138 // Initialize GenericChangeProcessor and related classes for testing with
139 // model type |type|.
140 void InitializeForType(syncer::ModelType type
) {
142 test_user_share_
.reset(new syncer::TestUserShare
);
143 test_user_share_
->SetUp();
144 sync_merge_result_
.reset(new syncer::SyncMergeResult(type
));
145 merge_result_ptr_factory_
.reset(
146 new base::WeakPtrFactory
<syncer::SyncMergeResult
>(
147 sync_merge_result_
.get()));
149 syncer::ModelTypeSet types
= syncer::ProtocolTypes();
150 for (syncer::ModelTypeSet::Iterator iter
= types
.First(); iter
.Good();
152 syncer::TestUserShare::CreateRoot(iter
.Get(),
153 test_user_share_
->user_share());
155 test_user_share_
->encryption_handler()->Init();
156 ConstructGenericChangeProcessor(type
);
159 void ConstructGenericChangeProcessor(syncer::ModelType type
) {
160 MockSyncApiComponentFactory sync_factory
;
161 scoped_ptr
<syncer::AttachmentStore
> attachment_store
=
162 syncer::AttachmentStore::CreateInMemoryStore();
163 change_processor_
.reset(new GenericChangeProcessor(
164 type
, &data_type_error_handler_
,
165 syncable_service_ptr_factory_
.GetWeakPtr(),
166 merge_result_ptr_factory_
->GetWeakPtr(), test_user_share_
->user_share(),
167 &sync_factory
, attachment_store
->CreateAttachmentStoreForSync()));
168 mock_attachment_service_
= sync_factory
.GetMockAttachmentService();
171 void BuildChildNodes(syncer::ModelType type
, int n
) {
172 syncer::WriteTransaction
trans(FROM_HERE
, user_share());
173 for (int i
= 0; i
< n
; ++i
) {
174 syncer::WriteNode
node(&trans
);
175 node
.InitUniqueByCreation(type
, base::StringPrintf("node%05d", i
));
179 GenericChangeProcessor
* change_processor() {
180 return change_processor_
.get();
183 syncer::UserShare
* user_share() {
184 return test_user_share_
->user_share();
187 MockAttachmentService
* mock_attachment_service() {
188 return mock_attachment_service_
;
192 base::RunLoop run_loop
;
193 run_loop
.RunUntilIdle();
197 base::MessageLoopForUI loop_
;
199 scoped_ptr
<syncer::SyncMergeResult
> sync_merge_result_
;
200 scoped_ptr
<base::WeakPtrFactory
<syncer::SyncMergeResult
> >
201 merge_result_ptr_factory_
;
203 syncer::FakeSyncableService fake_syncable_service_
;
204 base::WeakPtrFactory
<syncer::FakeSyncableService
>
205 syncable_service_ptr_factory_
;
207 DataTypeErrorHandlerMock data_type_error_handler_
;
208 scoped_ptr
<syncer::TestUserShare
> test_user_share_
;
209 MockAttachmentService
* mock_attachment_service_
;
211 scoped_ptr
<GenericChangeProcessor
> change_processor_
;
214 // Similar to above, but focused on the method that implements sync/api
215 // interfaces and is hence exposed to datatypes directly.
216 TEST_F(SyncGenericChangeProcessorTest
, StressGetAllSyncData
) {
217 const int kNumChildNodes
= 1000;
218 const int kRepeatCount
= 1;
220 ASSERT_NO_FATAL_FAILURE(BuildChildNodes(kType
, kNumChildNodes
));
222 for (int i
= 0; i
< kRepeatCount
; ++i
) {
223 syncer::SyncDataList sync_data
=
224 change_processor()->GetAllSyncData(kType
);
226 // Start with a simple test. We can add more in-depth testing later.
227 EXPECT_EQ(static_cast<size_t>(kNumChildNodes
), sync_data
.size());
231 TEST_F(SyncGenericChangeProcessorTest
, SetGetPasswords
) {
232 InitializeForType(syncer::PASSWORDS
);
233 const int kNumPasswords
= 10;
234 sync_pb::PasswordSpecificsData password_data
;
235 password_data
.set_username_value("user");
237 sync_pb::EntitySpecifics password_holder
;
239 syncer::SyncChangeList change_list
;
240 for (int i
= 0; i
< kNumPasswords
; ++i
) {
241 password_data
.set_password_value(
242 base::StringPrintf("password%i", i
));
243 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
244 CopyFrom(password_data
);
245 change_list
.push_back(
246 syncer::SyncChange(FROM_HERE
,
247 syncer::SyncChange::ACTION_ADD
,
248 syncer::SyncData::CreateLocalData(
249 base::StringPrintf("tag%i", i
),
250 base::StringPrintf("title%i", i
),
255 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
257 syncer::SyncDataList
password_list(
258 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
260 ASSERT_EQ(password_list
.size(), change_list
.size());
261 for (int i
= 0; i
< kNumPasswords
; ++i
) {
262 // Verify the password is returned properly.
263 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
264 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
265 has_client_only_encrypted_data());
266 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
267 const sync_pb::PasswordSpecificsData
& sync_password
=
268 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
269 const sync_pb::PasswordSpecificsData
& change_password
=
270 change_list
[i
].sync_data().GetSpecifics().password().
271 client_only_encrypted_data();
272 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
273 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
275 // Verify the raw sync data was stored securely.
276 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
277 syncer::ReadNode
node(&read_transaction
);
278 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
279 base::StringPrintf("tag%i", i
)),
280 syncer::BaseNode::INIT_OK
);
281 ASSERT_EQ(node
.GetTitle(), "encrypted");
282 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
283 ASSERT_TRUE(raw_specifics
.has_password());
284 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
285 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
289 TEST_F(SyncGenericChangeProcessorTest
, UpdatePasswords
) {
290 InitializeForType(syncer::PASSWORDS
);
291 const int kNumPasswords
= 10;
292 sync_pb::PasswordSpecificsData password_data
;
293 password_data
.set_username_value("user");
295 sync_pb::EntitySpecifics password_holder
;
297 syncer::SyncChangeList change_list
;
298 syncer::SyncChangeList change_list2
;
299 for (int i
= 0; i
< kNumPasswords
; ++i
) {
300 password_data
.set_password_value(
301 base::StringPrintf("password%i", i
));
302 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
303 CopyFrom(password_data
);
304 change_list
.push_back(
305 syncer::SyncChange(FROM_HERE
,
306 syncer::SyncChange::ACTION_ADD
,
307 syncer::SyncData::CreateLocalData(
308 base::StringPrintf("tag%i", i
),
309 base::StringPrintf("title%i", i
),
311 password_data
.set_password_value(
312 base::StringPrintf("password_m%i", i
));
313 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
314 CopyFrom(password_data
);
315 change_list2
.push_back(
316 syncer::SyncChange(FROM_HERE
,
317 syncer::SyncChange::ACTION_UPDATE
,
318 syncer::SyncData::CreateLocalData(
319 base::StringPrintf("tag%i", i
),
320 base::StringPrintf("title_m%i", i
),
325 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
327 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list2
).IsSet());
329 syncer::SyncDataList
password_list(
330 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
332 ASSERT_EQ(password_list
.size(), change_list2
.size());
333 for (int i
= 0; i
< kNumPasswords
; ++i
) {
334 // Verify the password is returned properly.
335 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
336 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
337 has_client_only_encrypted_data());
338 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
339 const sync_pb::PasswordSpecificsData
& sync_password
=
340 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
341 const sync_pb::PasswordSpecificsData
& change_password
=
342 change_list2
[i
].sync_data().GetSpecifics().password().
343 client_only_encrypted_data();
344 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
345 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
347 // Verify the raw sync data was stored securely.
348 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
349 syncer::ReadNode
node(&read_transaction
);
350 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
351 base::StringPrintf("tag%i", i
)),
352 syncer::BaseNode::INIT_OK
);
353 ASSERT_EQ(node
.GetTitle(), "encrypted");
354 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
355 ASSERT_TRUE(raw_specifics
.has_password());
356 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
357 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
361 // Verify that attachments on newly added or updated SyncData are passed to the
362 // AttachmentService.
363 TEST_F(SyncGenericChangeProcessorTest
,
364 ProcessSyncChanges_AddUpdateWithAttachment
) {
365 std::string tag
= "client_tag";
366 std::string title
= "client_title";
367 sync_pb::EntitySpecifics specifics
;
368 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
369 pref_specifics
->set_name("test");
371 syncer::AttachmentIdList attachment_ids
;
372 attachment_ids
.push_back(syncer::AttachmentId::Create(0, 0));
373 attachment_ids
.push_back(syncer::AttachmentId::Create(0, 0));
375 // Add a SyncData with two attachments.
376 syncer::SyncChangeList change_list
;
377 change_list
.push_back(
378 syncer::SyncChange(FROM_HERE
,
379 syncer::SyncChange::ACTION_ADD
,
380 syncer::SyncData::CreateLocalDataWithAttachments(
381 tag
, title
, specifics
, attachment_ids
)));
383 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
386 // Check that the AttachmentService received the new attachments.
387 ASSERT_EQ(mock_attachment_service()->attachment_id_lists()->size(), 1U);
388 const syncer::AttachmentIdList
& attachments_added
=
389 mock_attachment_service()->attachment_id_lists()->front();
392 testing::UnorderedElementsAre(attachment_ids
[0], attachment_ids
[1]));
394 // Update the SyncData, replacing its two attachments with one new attachment.
395 syncer::AttachmentIdList new_attachment_ids
;
396 new_attachment_ids
.push_back(syncer::AttachmentId::Create(0, 0));
397 mock_attachment_service()->attachment_id_lists()->clear();
399 change_list
.push_back(
400 syncer::SyncChange(FROM_HERE
,
401 syncer::SyncChange::ACTION_UPDATE
,
402 syncer::SyncData::CreateLocalDataWithAttachments(
403 tag
, title
, specifics
, new_attachment_ids
)));
405 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
408 // Check that the AttachmentService received it.
409 ASSERT_EQ(mock_attachment_service()->attachment_id_lists()->size(), 1U);
410 const syncer::AttachmentIdList
& new_attachments_added
=
411 mock_attachment_service()->attachment_id_lists()->front();
412 ASSERT_THAT(new_attachments_added
,
413 testing::UnorderedElementsAre(new_attachment_ids
[0]));
416 // Verify that after attachment is uploaded GenericChangeProcessor updates
417 // corresponding entries
418 TEST_F(SyncGenericChangeProcessorTest
, AttachmentUploaded
) {
419 std::string tag
= "client_tag";
420 std::string title
= "client_title";
421 sync_pb::EntitySpecifics specifics
;
422 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
423 pref_specifics
->set_name("test");
425 syncer::AttachmentIdList attachment_ids
;
426 attachment_ids
.push_back(syncer::AttachmentId::Create(0, 0));
428 // Add a SyncData with two attachments.
429 syncer::SyncChangeList change_list
;
430 change_list
.push_back(
431 syncer::SyncChange(FROM_HERE
,
432 syncer::SyncChange::ACTION_ADD
,
433 syncer::SyncData::CreateLocalDataWithAttachments(
434 tag
, title
, specifics
, attachment_ids
)));
436 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
438 sync_pb::AttachmentIdProto attachment_id_proto
= attachment_ids
[0].GetProto();
439 syncer::AttachmentId attachment_id
=
440 syncer::AttachmentId::CreateFromProto(attachment_id_proto
);
442 change_processor()->OnAttachmentUploaded(attachment_id
);
443 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
444 syncer::ReadNode
node(&read_transaction
);
445 ASSERT_EQ(node
.InitByClientTagLookup(kType
, tag
), syncer::BaseNode::INIT_OK
);
446 attachment_ids
= node
.GetAttachmentIds();
447 EXPECT_EQ(1U, attachment_ids
.size());
450 // Verify that upon construction, all attachments not yet on the server are
451 // scheduled for upload.
452 TEST_F(SyncGenericChangeProcessorTest
, UploadAllAttachmentsNotOnServer
) {
453 // Create two attachment ids. id2 will be marked as "on server".
454 syncer::AttachmentId id1
= syncer::AttachmentId::Create(0, 0);
455 syncer::AttachmentId id2
= syncer::AttachmentId::Create(0, 0);
457 // Write an entry containing these two attachment ids.
458 syncer::WriteTransaction
trans(FROM_HERE
, user_share());
459 syncer::ReadNode
root(&trans
);
460 ASSERT_EQ(syncer::BaseNode::INIT_OK
, root
.InitTypeRoot(kType
));
461 syncer::WriteNode
node(&trans
);
462 node
.InitUniqueByCreation(kType
, root
, "some node");
463 sync_pb::AttachmentMetadata metadata
;
464 sync_pb::AttachmentMetadataRecord
* record1
= metadata
.add_record();
465 *record1
->mutable_id() = id1
.GetProto();
466 sync_pb::AttachmentMetadataRecord
* record2
= metadata
.add_record();
467 *record2
->mutable_id() = id2
.GetProto();
468 record2
->set_is_on_server(true);
469 node
.SetAttachmentMetadata(metadata
);
472 // Construct the GenericChangeProcessor and see that it asks the
473 // AttachmentService to upload id1 only.
474 ConstructGenericChangeProcessor(kType
);
475 ASSERT_EQ(1U, mock_attachment_service()->attachment_id_lists()->size());
476 ASSERT_THAT(mock_attachment_service()->attachment_id_lists()->front(),
477 testing::UnorderedElementsAre(id1
));
480 // Test that attempting to add an entry that already exists still works.
481 TEST_F(SyncGenericChangeProcessorTest
, AddExistingEntry
) {
482 InitializeForType(syncer::SESSIONS
);
483 sync_pb::EntitySpecifics sessions_specifics
;
484 sessions_specifics
.mutable_session()->set_session_tag("session tag");
485 syncer::SyncChangeList changes
;
487 // First add it normally.
488 changes
.push_back(syncer::SyncChange(
489 FROM_HERE
, syncer::SyncChange::ACTION_ADD
,
490 syncer::SyncData::CreateLocalData(base::StringPrintf("tag"),
491 base::StringPrintf("title"),
492 sessions_specifics
)));
494 change_processor()->ProcessSyncChanges(FROM_HERE
, changes
).IsSet());
496 // Now attempt to add it again, but with different specifics. Should not
497 // result in an error and should still update the specifics.
498 sessions_specifics
.mutable_session()->set_session_tag("session tag 2");
500 syncer::SyncChange(FROM_HERE
, syncer::SyncChange::ACTION_ADD
,
501 syncer::SyncData::CreateLocalData(
502 base::StringPrintf("tag"),
503 base::StringPrintf("title"), sessions_specifics
));
505 change_processor()->ProcessSyncChanges(FROM_HERE
, changes
).IsSet());
507 // Verify the data was updated properly.
508 syncer::SyncDataList sync_data
=
509 change_processor()->GetAllSyncData(syncer::SESSIONS
);
510 ASSERT_EQ(sync_data
.size(), 1U);
511 ASSERT_EQ("session tag 2",
512 sync_data
[0].GetSpecifics().session().session_tag());
517 } // namespace sync_driver