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 const scoped_refptr
<syncer::AttachmentStore
>& attachment_store
);
42 ~MockAttachmentService() override
;
43 void UploadAttachments(
44 const syncer::AttachmentIdSet
& attachment_ids
) override
;
45 std::vector
<syncer::AttachmentIdSet
>* attachment_id_sets();
48 std::vector
<syncer::AttachmentIdSet
> attachment_id_sets_
;
51 MockAttachmentService::MockAttachmentService(
52 const scoped_refptr
<syncer::AttachmentStore
>& attachment_store
)
53 : AttachmentServiceImpl(attachment_store
,
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::AttachmentIdSet
& attachment_ids
) {
68 attachment_id_sets_
.push_back(attachment_ids
);
69 AttachmentServiceImpl::UploadAttachments(attachment_ids
);
72 std::vector
<syncer::AttachmentIdSet
>*
73 MockAttachmentService::attachment_id_sets() {
74 return &attachment_id_sets_
;
77 // MockSyncApiComponentFactory needed to initialize GenericChangeProcessor and
78 // pass MockAttachmentService to it.
79 class MockSyncApiComponentFactory
: public SyncApiComponentFactory
{
81 MockSyncApiComponentFactory(
82 scoped_ptr
<syncer::AttachmentService
> attachment_service
)
83 : attachment_service_(attachment_service
.Pass()) {}
85 base::WeakPtr
<syncer::SyncableService
> GetSyncableServiceForType(
86 syncer::ModelType type
) override
{
87 // Shouldn't be called for this test.
89 return base::WeakPtr
<syncer::SyncableService
>();
92 scoped_ptr
<syncer::AttachmentService
> CreateAttachmentService(
93 const scoped_refptr
<syncer::AttachmentStore
>& attachment_store
,
94 const syncer::UserShare
& user_share
,
95 const std::string
& store_birthday
,
96 syncer::ModelType model_type
,
97 syncer::AttachmentService::Delegate
* delegate
) override
{
98 EXPECT_TRUE(attachment_service_
!= NULL
);
99 return attachment_service_
.Pass();
103 scoped_ptr
<syncer::AttachmentService
> attachment_service_
;
106 class SyncGenericChangeProcessorTest
: public testing::Test
{
108 // Most test cases will use this type. For those that need a
109 // GenericChangeProcessor for a different type, use |InitializeForType|.
110 static const syncer::ModelType kType
= syncer::PREFERENCES
;
112 SyncGenericChangeProcessorTest()
113 : syncable_service_ptr_factory_(&fake_syncable_service_
),
114 mock_attachment_service_(NULL
) {}
116 void SetUp() override
{
117 // Use kType by default, but allow test cases to re-initialize with whatever
118 // type they choose. Therefore, it's important that all type dependent
119 // initialization occurs in InitializeForType.
120 InitializeForType(kType
);
123 void TearDown() override
{
124 mock_attachment_service_
= NULL
;
125 if (test_user_share_
) {
126 test_user_share_
->TearDown();
130 // Initialize GenericChangeProcessor and related classes for testing with
131 // model type |type|.
132 void InitializeForType(syncer::ModelType type
) {
134 test_user_share_
.reset(new syncer::TestUserShare
);
135 test_user_share_
->SetUp();
136 sync_merge_result_
.reset(new syncer::SyncMergeResult(type
));
137 merge_result_ptr_factory_
.reset(
138 new base::WeakPtrFactory
<syncer::SyncMergeResult
>(
139 sync_merge_result_
.get()));
141 syncer::ModelTypeSet types
= syncer::ProtocolTypes();
142 for (syncer::ModelTypeSet::Iterator iter
= types
.First(); iter
.Good();
144 syncer::TestUserShare::CreateRoot(iter
.Get(),
145 test_user_share_
->user_share());
147 test_user_share_
->encryption_handler()->Init();
148 ConstructGenericChangeProcessor(type
);
151 void ConstructGenericChangeProcessor(syncer::ModelType type
) {
152 scoped_refptr
<syncer::AttachmentStore
> attachment_store
=
153 syncer::AttachmentStore::CreateInMemoryStore();
154 scoped_ptr
<MockAttachmentService
> mock_attachment_service(
155 new MockAttachmentService(attachment_store
));
156 // GenericChangeProcessor takes ownership of the AttachmentService, but we
157 // need to have a pointer to it so we can see that it was used properly.
158 // Take a pointer and trust that GenericChangeProcessor does not prematurely
160 mock_attachment_service_
= mock_attachment_service
.get();
162 new MockSyncApiComponentFactory(mock_attachment_service
.Pass()));
163 change_processor_
.reset(
164 new GenericChangeProcessor(type
,
165 &data_type_error_handler_
,
166 syncable_service_ptr_factory_
.GetWeakPtr(),
167 merge_result_ptr_factory_
->GetWeakPtr(),
168 test_user_share_
->user_share(),
173 void BuildChildNodes(syncer::ModelType type
, int n
) {
174 syncer::WriteTransaction
trans(FROM_HERE
, user_share());
175 syncer::ReadNode
root(&trans
);
176 ASSERT_EQ(syncer::BaseNode::INIT_OK
, root
.InitTypeRoot(type
));
177 for (int i
= 0; i
< n
; ++i
) {
178 syncer::WriteNode
node(&trans
);
179 node
.InitUniqueByCreation(type
, root
, base::StringPrintf("node%05d", i
));
183 GenericChangeProcessor
* change_processor() {
184 return change_processor_
.get();
187 syncer::UserShare
* user_share() {
188 return test_user_share_
->user_share();
191 MockAttachmentService
* mock_attachment_service() {
192 return mock_attachment_service_
;
196 base::RunLoop run_loop
;
197 run_loop
.RunUntilIdle();
201 base::MessageLoopForUI loop_
;
203 scoped_ptr
<syncer::SyncMergeResult
> sync_merge_result_
;
204 scoped_ptr
<base::WeakPtrFactory
<syncer::SyncMergeResult
> >
205 merge_result_ptr_factory_
;
207 syncer::FakeSyncableService fake_syncable_service_
;
208 base::WeakPtrFactory
<syncer::FakeSyncableService
>
209 syncable_service_ptr_factory_
;
211 DataTypeErrorHandlerMock data_type_error_handler_
;
212 scoped_ptr
<syncer::TestUserShare
> test_user_share_
;
213 MockAttachmentService
* mock_attachment_service_
;
214 scoped_ptr
<SyncApiComponentFactory
> sync_factory_
;
216 scoped_ptr
<GenericChangeProcessor
> change_processor_
;
219 // Similar to above, but focused on the method that implements sync/api
220 // interfaces and is hence exposed to datatypes directly.
221 TEST_F(SyncGenericChangeProcessorTest
, StressGetAllSyncData
) {
222 const int kNumChildNodes
= 1000;
223 const int kRepeatCount
= 1;
225 ASSERT_NO_FATAL_FAILURE(BuildChildNodes(kType
, kNumChildNodes
));
227 for (int i
= 0; i
< kRepeatCount
; ++i
) {
228 syncer::SyncDataList sync_data
=
229 change_processor()->GetAllSyncData(kType
);
231 // Start with a simple test. We can add more in-depth testing later.
232 EXPECT_EQ(static_cast<size_t>(kNumChildNodes
), sync_data
.size());
236 TEST_F(SyncGenericChangeProcessorTest
, SetGetPasswords
) {
237 InitializeForType(syncer::PASSWORDS
);
238 const int kNumPasswords
= 10;
239 sync_pb::PasswordSpecificsData password_data
;
240 password_data
.set_username_value("user");
242 sync_pb::EntitySpecifics password_holder
;
244 syncer::SyncChangeList change_list
;
245 for (int i
= 0; i
< kNumPasswords
; ++i
) {
246 password_data
.set_password_value(
247 base::StringPrintf("password%i", i
));
248 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
249 CopyFrom(password_data
);
250 change_list
.push_back(
251 syncer::SyncChange(FROM_HERE
,
252 syncer::SyncChange::ACTION_ADD
,
253 syncer::SyncData::CreateLocalData(
254 base::StringPrintf("tag%i", i
),
255 base::StringPrintf("title%i", i
),
260 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
262 syncer::SyncDataList
password_list(
263 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
265 ASSERT_EQ(password_list
.size(), change_list
.size());
266 for (int i
= 0; i
< kNumPasswords
; ++i
) {
267 // Verify the password is returned properly.
268 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
269 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
270 has_client_only_encrypted_data());
271 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
272 const sync_pb::PasswordSpecificsData
& sync_password
=
273 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
274 const sync_pb::PasswordSpecificsData
& change_password
=
275 change_list
[i
].sync_data().GetSpecifics().password().
276 client_only_encrypted_data();
277 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
278 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
280 // Verify the raw sync data was stored securely.
281 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
282 syncer::ReadNode
node(&read_transaction
);
283 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
284 base::StringPrintf("tag%i", i
)),
285 syncer::BaseNode::INIT_OK
);
286 ASSERT_EQ(node
.GetTitle(), "encrypted");
287 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
288 ASSERT_TRUE(raw_specifics
.has_password());
289 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
290 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
294 TEST_F(SyncGenericChangeProcessorTest
, UpdatePasswords
) {
295 InitializeForType(syncer::PASSWORDS
);
296 const int kNumPasswords
= 10;
297 sync_pb::PasswordSpecificsData password_data
;
298 password_data
.set_username_value("user");
300 sync_pb::EntitySpecifics password_holder
;
302 syncer::SyncChangeList change_list
;
303 syncer::SyncChangeList change_list2
;
304 for (int i
= 0; i
< kNumPasswords
; ++i
) {
305 password_data
.set_password_value(
306 base::StringPrintf("password%i", i
));
307 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
308 CopyFrom(password_data
);
309 change_list
.push_back(
310 syncer::SyncChange(FROM_HERE
,
311 syncer::SyncChange::ACTION_ADD
,
312 syncer::SyncData::CreateLocalData(
313 base::StringPrintf("tag%i", i
),
314 base::StringPrintf("title%i", i
),
316 password_data
.set_password_value(
317 base::StringPrintf("password_m%i", i
));
318 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
319 CopyFrom(password_data
);
320 change_list2
.push_back(
321 syncer::SyncChange(FROM_HERE
,
322 syncer::SyncChange::ACTION_UPDATE
,
323 syncer::SyncData::CreateLocalData(
324 base::StringPrintf("tag%i", i
),
325 base::StringPrintf("title_m%i", i
),
330 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
332 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list2
).IsSet());
334 syncer::SyncDataList
password_list(
335 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
337 ASSERT_EQ(password_list
.size(), change_list2
.size());
338 for (int i
= 0; i
< kNumPasswords
; ++i
) {
339 // Verify the password is returned properly.
340 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
341 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
342 has_client_only_encrypted_data());
343 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
344 const sync_pb::PasswordSpecificsData
& sync_password
=
345 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
346 const sync_pb::PasswordSpecificsData
& change_password
=
347 change_list2
[i
].sync_data().GetSpecifics().password().
348 client_only_encrypted_data();
349 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
350 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
352 // Verify the raw sync data was stored securely.
353 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
354 syncer::ReadNode
node(&read_transaction
);
355 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
356 base::StringPrintf("tag%i", i
)),
357 syncer::BaseNode::INIT_OK
);
358 ASSERT_EQ(node
.GetTitle(), "encrypted");
359 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
360 ASSERT_TRUE(raw_specifics
.has_password());
361 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
362 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
366 // Verify that attachments on newly added or updated SyncData are passed to the
367 // AttachmentService.
368 TEST_F(SyncGenericChangeProcessorTest
,
369 ProcessSyncChanges_AddUpdateWithAttachment
) {
370 std::string tag
= "client_tag";
371 std::string title
= "client_title";
372 sync_pb::EntitySpecifics specifics
;
373 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
374 pref_specifics
->set_name("test");
376 syncer::AttachmentIdList attachment_ids
;
377 attachment_ids
.push_back(syncer::AttachmentId::Create());
378 attachment_ids
.push_back(syncer::AttachmentId::Create());
380 // Add a SyncData with two attachments.
381 syncer::SyncChangeList change_list
;
382 change_list
.push_back(
383 syncer::SyncChange(FROM_HERE
,
384 syncer::SyncChange::ACTION_ADD
,
385 syncer::SyncData::CreateLocalDataWithAttachments(
386 tag
, title
, specifics
, attachment_ids
)));
388 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
391 // Check that the AttachmentService received the new attachments.
392 ASSERT_EQ(mock_attachment_service()->attachment_id_sets()->size(), 1U);
393 const syncer::AttachmentIdSet
& attachments_added
=
394 mock_attachment_service()->attachment_id_sets()->front();
397 testing::UnorderedElementsAre(attachment_ids
[0], attachment_ids
[1]));
399 // Update the SyncData, replacing its two attachments with one new attachment.
400 syncer::AttachmentIdList new_attachment_ids
;
401 new_attachment_ids
.push_back(syncer::AttachmentId::Create());
402 mock_attachment_service()->attachment_id_sets()->clear();
404 change_list
.push_back(
405 syncer::SyncChange(FROM_HERE
,
406 syncer::SyncChange::ACTION_UPDATE
,
407 syncer::SyncData::CreateLocalDataWithAttachments(
408 tag
, title
, specifics
, new_attachment_ids
)));
410 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
413 // Check that the AttachmentService received it.
414 ASSERT_EQ(mock_attachment_service()->attachment_id_sets()->size(), 1U);
415 const syncer::AttachmentIdSet
& new_attachments_added
=
416 mock_attachment_service()->attachment_id_sets()->front();
417 ASSERT_THAT(new_attachments_added
,
418 testing::UnorderedElementsAre(new_attachment_ids
[0]));
421 // Verify that after attachment is uploaded GenericChangeProcessor updates
422 // corresponding entries
423 TEST_F(SyncGenericChangeProcessorTest
, AttachmentUploaded
) {
424 std::string tag
= "client_tag";
425 std::string title
= "client_title";
426 sync_pb::EntitySpecifics specifics
;
427 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
428 pref_specifics
->set_name("test");
430 syncer::AttachmentIdList attachment_ids
;
431 attachment_ids
.push_back(syncer::AttachmentId::Create());
433 // Add a SyncData with two attachments.
434 syncer::SyncChangeList change_list
;
435 change_list
.push_back(
436 syncer::SyncChange(FROM_HERE
,
437 syncer::SyncChange::ACTION_ADD
,
438 syncer::SyncData::CreateLocalDataWithAttachments(
439 tag
, title
, specifics
, attachment_ids
)));
441 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
443 sync_pb::AttachmentIdProto attachment_id_proto
= attachment_ids
[0].GetProto();
444 syncer::AttachmentId attachment_id
=
445 syncer::AttachmentId::CreateFromProto(attachment_id_proto
);
447 change_processor()->OnAttachmentUploaded(attachment_id
);
448 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
449 syncer::ReadNode
node(&read_transaction
);
450 ASSERT_EQ(node
.InitByClientTagLookup(kType
, tag
), syncer::BaseNode::INIT_OK
);
451 attachment_ids
= node
.GetAttachmentIds();
452 EXPECT_EQ(1U, attachment_ids
.size());
455 // Verify that upon construction, all attachments not yet on the server are
456 // scheduled for upload.
457 TEST_F(SyncGenericChangeProcessorTest
, UploadAllAttachmentsNotOnServer
) {
458 // Create two attachment ids. id2 will be marked as "on server".
459 syncer::AttachmentId id1
= syncer::AttachmentId::Create();
460 syncer::AttachmentId id2
= syncer::AttachmentId::Create();
462 // Write an entry containing these two attachment ids.
463 syncer::WriteTransaction
trans(FROM_HERE
, user_share());
464 syncer::ReadNode
root(&trans
);
465 ASSERT_EQ(syncer::BaseNode::INIT_OK
, root
.InitTypeRoot(kType
));
466 syncer::WriteNode
node(&trans
);
467 node
.InitUniqueByCreation(kType
, root
, "some node");
468 sync_pb::AttachmentMetadata metadata
;
469 sync_pb::AttachmentMetadataRecord
* record1
= metadata
.add_record();
470 *record1
->mutable_id() = id1
.GetProto();
471 sync_pb::AttachmentMetadataRecord
* record2
= metadata
.add_record();
472 *record2
->mutable_id() = id2
.GetProto();
473 record2
->set_is_on_server(true);
474 node
.SetAttachmentMetadata(metadata
);
477 // Construct the GenericChangeProcessor and see that it asks the
478 // AttachmentService to upload id1 only.
479 ConstructGenericChangeProcessor(kType
);
480 ASSERT_EQ(1U, mock_attachment_service()->attachment_id_sets()->size());
481 ASSERT_THAT(mock_attachment_service()->attachment_id_sets()->front(),
482 testing::UnorderedElementsAre(id1
));
487 } // namespace sync_driver