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::AttachmentService::Delegate
* delegate
) override
{
97 EXPECT_TRUE(attachment_service_
!= NULL
);
98 return attachment_service_
.Pass();
102 scoped_ptr
<syncer::AttachmentService
> attachment_service_
;
105 class SyncGenericChangeProcessorTest
: public testing::Test
{
107 // Most test cases will use this type. For those that need a
108 // GenericChangeProcessor for a different type, use |InitializeForType|.
109 static const syncer::ModelType kType
= syncer::PREFERENCES
;
111 SyncGenericChangeProcessorTest()
112 : syncable_service_ptr_factory_(&fake_syncable_service_
),
113 mock_attachment_service_(NULL
) {}
115 void SetUp() override
{
116 // Use kType by default, but allow test cases to re-initialize with whatever
117 // type they choose. Therefore, it's important that all type dependent
118 // initialization occurs in InitializeForType.
119 InitializeForType(kType
);
122 void TearDown() override
{
123 mock_attachment_service_
= NULL
;
124 if (test_user_share_
) {
125 test_user_share_
->TearDown();
129 // Initialize GenericChangeProcessor and related classes for testing with
130 // model type |type|.
131 void InitializeForType(syncer::ModelType type
) {
133 test_user_share_
.reset(new syncer::TestUserShare
);
134 test_user_share_
->SetUp();
135 sync_merge_result_
.reset(new syncer::SyncMergeResult(type
));
136 merge_result_ptr_factory_
.reset(
137 new base::WeakPtrFactory
<syncer::SyncMergeResult
>(
138 sync_merge_result_
.get()));
140 syncer::ModelTypeSet types
= syncer::ProtocolTypes();
141 for (syncer::ModelTypeSet::Iterator iter
= types
.First(); iter
.Good();
143 syncer::TestUserShare::CreateRoot(iter
.Get(),
144 test_user_share_
->user_share());
146 test_user_share_
->encryption_handler()->Init();
147 ConstructGenericChangeProcessor(type
);
150 void ConstructGenericChangeProcessor(syncer::ModelType type
) {
151 scoped_refptr
<syncer::AttachmentStore
> attachment_store
=
152 syncer::AttachmentStore::CreateInMemoryStore();
153 scoped_ptr
<MockAttachmentService
> mock_attachment_service(
154 new MockAttachmentService(attachment_store
));
155 // GenericChangeProcessor takes ownership of the AttachmentService, but we
156 // need to have a pointer to it so we can see that it was used properly.
157 // Take a pointer and trust that GenericChangeProcessor does not prematurely
159 mock_attachment_service_
= mock_attachment_service
.get();
161 new MockSyncApiComponentFactory(mock_attachment_service
.Pass()));
162 change_processor_
.reset(
163 new GenericChangeProcessor(type
,
164 &data_type_error_handler_
,
165 syncable_service_ptr_factory_
.GetWeakPtr(),
166 merge_result_ptr_factory_
->GetWeakPtr(),
167 test_user_share_
->user_share(),
172 void BuildChildNodes(syncer::ModelType type
, int n
) {
173 syncer::WriteTransaction
trans(FROM_HERE
, user_share());
174 syncer::ReadNode
root(&trans
);
175 ASSERT_EQ(syncer::BaseNode::INIT_OK
, root
.InitTypeRoot(type
));
176 for (int i
= 0; i
< n
; ++i
) {
177 syncer::WriteNode
node(&trans
);
178 node
.InitUniqueByCreation(type
, root
, base::StringPrintf("node%05d", i
));
182 GenericChangeProcessor
* change_processor() {
183 return change_processor_
.get();
186 syncer::UserShare
* user_share() {
187 return test_user_share_
->user_share();
190 MockAttachmentService
* mock_attachment_service() {
191 return mock_attachment_service_
;
195 base::RunLoop run_loop
;
196 run_loop
.RunUntilIdle();
200 base::MessageLoopForUI loop_
;
202 scoped_ptr
<syncer::SyncMergeResult
> sync_merge_result_
;
203 scoped_ptr
<base::WeakPtrFactory
<syncer::SyncMergeResult
> >
204 merge_result_ptr_factory_
;
206 syncer::FakeSyncableService fake_syncable_service_
;
207 base::WeakPtrFactory
<syncer::FakeSyncableService
>
208 syncable_service_ptr_factory_
;
210 DataTypeErrorHandlerMock data_type_error_handler_
;
211 scoped_ptr
<syncer::TestUserShare
> test_user_share_
;
212 MockAttachmentService
* mock_attachment_service_
;
213 scoped_ptr
<SyncApiComponentFactory
> sync_factory_
;
215 scoped_ptr
<GenericChangeProcessor
> change_processor_
;
218 // Similar to above, but focused on the method that implements sync/api
219 // interfaces and is hence exposed to datatypes directly.
220 TEST_F(SyncGenericChangeProcessorTest
, StressGetAllSyncData
) {
221 const int kNumChildNodes
= 1000;
222 const int kRepeatCount
= 1;
224 ASSERT_NO_FATAL_FAILURE(BuildChildNodes(kType
, kNumChildNodes
));
226 for (int i
= 0; i
< kRepeatCount
; ++i
) {
227 syncer::SyncDataList sync_data
=
228 change_processor()->GetAllSyncData(kType
);
230 // Start with a simple test. We can add more in-depth testing later.
231 EXPECT_EQ(static_cast<size_t>(kNumChildNodes
), sync_data
.size());
235 TEST_F(SyncGenericChangeProcessorTest
, SetGetPasswords
) {
236 InitializeForType(syncer::PASSWORDS
);
237 const int kNumPasswords
= 10;
238 sync_pb::PasswordSpecificsData password_data
;
239 password_data
.set_username_value("user");
241 sync_pb::EntitySpecifics password_holder
;
243 syncer::SyncChangeList change_list
;
244 for (int i
= 0; i
< kNumPasswords
; ++i
) {
245 password_data
.set_password_value(
246 base::StringPrintf("password%i", i
));
247 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
248 CopyFrom(password_data
);
249 change_list
.push_back(
250 syncer::SyncChange(FROM_HERE
,
251 syncer::SyncChange::ACTION_ADD
,
252 syncer::SyncData::CreateLocalData(
253 base::StringPrintf("tag%i", i
),
254 base::StringPrintf("title%i", i
),
259 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
261 syncer::SyncDataList
password_list(
262 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
264 ASSERT_EQ(password_list
.size(), change_list
.size());
265 for (int i
= 0; i
< kNumPasswords
; ++i
) {
266 // Verify the password is returned properly.
267 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
268 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
269 has_client_only_encrypted_data());
270 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
271 const sync_pb::PasswordSpecificsData
& sync_password
=
272 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
273 const sync_pb::PasswordSpecificsData
& change_password
=
274 change_list
[i
].sync_data().GetSpecifics().password().
275 client_only_encrypted_data();
276 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
277 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
279 // Verify the raw sync data was stored securely.
280 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
281 syncer::ReadNode
node(&read_transaction
);
282 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
283 base::StringPrintf("tag%i", i
)),
284 syncer::BaseNode::INIT_OK
);
285 ASSERT_EQ(node
.GetTitle(), "encrypted");
286 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
287 ASSERT_TRUE(raw_specifics
.has_password());
288 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
289 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
293 TEST_F(SyncGenericChangeProcessorTest
, UpdatePasswords
) {
294 InitializeForType(syncer::PASSWORDS
);
295 const int kNumPasswords
= 10;
296 sync_pb::PasswordSpecificsData password_data
;
297 password_data
.set_username_value("user");
299 sync_pb::EntitySpecifics password_holder
;
301 syncer::SyncChangeList change_list
;
302 syncer::SyncChangeList change_list2
;
303 for (int i
= 0; i
< kNumPasswords
; ++i
) {
304 password_data
.set_password_value(
305 base::StringPrintf("password%i", i
));
306 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
307 CopyFrom(password_data
);
308 change_list
.push_back(
309 syncer::SyncChange(FROM_HERE
,
310 syncer::SyncChange::ACTION_ADD
,
311 syncer::SyncData::CreateLocalData(
312 base::StringPrintf("tag%i", i
),
313 base::StringPrintf("title%i", i
),
315 password_data
.set_password_value(
316 base::StringPrintf("password_m%i", i
));
317 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
318 CopyFrom(password_data
);
319 change_list2
.push_back(
320 syncer::SyncChange(FROM_HERE
,
321 syncer::SyncChange::ACTION_UPDATE
,
322 syncer::SyncData::CreateLocalData(
323 base::StringPrintf("tag%i", i
),
324 base::StringPrintf("title_m%i", i
),
329 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
331 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list2
).IsSet());
333 syncer::SyncDataList
password_list(
334 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
336 ASSERT_EQ(password_list
.size(), change_list2
.size());
337 for (int i
= 0; i
< kNumPasswords
; ++i
) {
338 // Verify the password is returned properly.
339 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
340 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
341 has_client_only_encrypted_data());
342 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
343 const sync_pb::PasswordSpecificsData
& sync_password
=
344 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
345 const sync_pb::PasswordSpecificsData
& change_password
=
346 change_list2
[i
].sync_data().GetSpecifics().password().
347 client_only_encrypted_data();
348 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
349 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
351 // Verify the raw sync data was stored securely.
352 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
353 syncer::ReadNode
node(&read_transaction
);
354 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
355 base::StringPrintf("tag%i", i
)),
356 syncer::BaseNode::INIT_OK
);
357 ASSERT_EQ(node
.GetTitle(), "encrypted");
358 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
359 ASSERT_TRUE(raw_specifics
.has_password());
360 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
361 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
365 // Verify that attachments on newly added or updated SyncData are passed to the
366 // AttachmentService.
367 TEST_F(SyncGenericChangeProcessorTest
,
368 ProcessSyncChanges_AddUpdateWithAttachment
) {
369 std::string tag
= "client_tag";
370 std::string title
= "client_title";
371 sync_pb::EntitySpecifics specifics
;
372 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
373 pref_specifics
->set_name("test");
375 syncer::AttachmentIdList attachment_ids
;
376 attachment_ids
.push_back(syncer::AttachmentId::Create());
377 attachment_ids
.push_back(syncer::AttachmentId::Create());
379 // Add a SyncData with two attachments.
380 syncer::SyncChangeList change_list
;
381 change_list
.push_back(
382 syncer::SyncChange(FROM_HERE
,
383 syncer::SyncChange::ACTION_ADD
,
384 syncer::SyncData::CreateLocalDataWithAttachments(
385 tag
, title
, specifics
, attachment_ids
)));
387 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
390 // Check that the AttachmentService received the new attachments.
391 ASSERT_EQ(mock_attachment_service()->attachment_id_sets()->size(), 1U);
392 const syncer::AttachmentIdSet
& attachments_added
=
393 mock_attachment_service()->attachment_id_sets()->front();
396 testing::UnorderedElementsAre(attachment_ids
[0], attachment_ids
[1]));
398 // Update the SyncData, replacing its two attachments with one new attachment.
399 syncer::AttachmentIdList new_attachment_ids
;
400 new_attachment_ids
.push_back(syncer::AttachmentId::Create());
401 mock_attachment_service()->attachment_id_sets()->clear();
403 change_list
.push_back(
404 syncer::SyncChange(FROM_HERE
,
405 syncer::SyncChange::ACTION_UPDATE
,
406 syncer::SyncData::CreateLocalDataWithAttachments(
407 tag
, title
, specifics
, new_attachment_ids
)));
409 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
412 // Check that the AttachmentService received it.
413 ASSERT_EQ(mock_attachment_service()->attachment_id_sets()->size(), 1U);
414 const syncer::AttachmentIdSet
& new_attachments_added
=
415 mock_attachment_service()->attachment_id_sets()->front();
416 ASSERT_THAT(new_attachments_added
,
417 testing::UnorderedElementsAre(new_attachment_ids
[0]));
420 // Verify that after attachment is uploaded GenericChangeProcessor updates
421 // corresponding entries
422 TEST_F(SyncGenericChangeProcessorTest
, AttachmentUploaded
) {
423 std::string tag
= "client_tag";
424 std::string title
= "client_title";
425 sync_pb::EntitySpecifics specifics
;
426 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
427 pref_specifics
->set_name("test");
429 syncer::AttachmentIdList attachment_ids
;
430 attachment_ids
.push_back(syncer::AttachmentId::Create());
432 // Add a SyncData with two attachments.
433 syncer::SyncChangeList change_list
;
434 change_list
.push_back(
435 syncer::SyncChange(FROM_HERE
,
436 syncer::SyncChange::ACTION_ADD
,
437 syncer::SyncData::CreateLocalDataWithAttachments(
438 tag
, title
, specifics
, attachment_ids
)));
440 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
442 sync_pb::AttachmentIdProto attachment_id_proto
= attachment_ids
[0].GetProto();
443 syncer::AttachmentId attachment_id
=
444 syncer::AttachmentId::CreateFromProto(attachment_id_proto
);
446 change_processor()->OnAttachmentUploaded(attachment_id
);
447 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
448 syncer::ReadNode
node(&read_transaction
);
449 ASSERT_EQ(node
.InitByClientTagLookup(kType
, tag
), syncer::BaseNode::INIT_OK
);
450 attachment_ids
= node
.GetAttachmentIds();
451 EXPECT_EQ(1U, attachment_ids
.size());
454 // Verify that upon construction, all attachments not yet on the server are
455 // scheduled for upload.
456 TEST_F(SyncGenericChangeProcessorTest
, UploadAllAttachmentsNotOnServer
) {
457 // Create two attachment ids. id2 will be marked as "on server".
458 syncer::AttachmentId id1
= syncer::AttachmentId::Create();
459 syncer::AttachmentId id2
= syncer::AttachmentId::Create();
461 // Write an entry containing these two attachment ids.
462 syncer::WriteTransaction
trans(FROM_HERE
, user_share());
463 syncer::ReadNode
root(&trans
);
464 ASSERT_EQ(syncer::BaseNode::INIT_OK
, root
.InitTypeRoot(kType
));
465 syncer::WriteNode
node(&trans
);
466 node
.InitUniqueByCreation(kType
, root
, "some node");
467 sync_pb::AttachmentMetadata metadata
;
468 sync_pb::AttachmentMetadataRecord
* record1
= metadata
.add_record();
469 *record1
->mutable_id() = id1
.GetProto();
470 sync_pb::AttachmentMetadataRecord
* record2
= metadata
.add_record();
471 *record2
->mutable_id() = id2
.GetProto();
472 record2
->set_is_on_server(true);
473 node
.SetAttachmentMetadata(metadata
);
476 // Construct the GenericChangeProcessor and see that it asks the
477 // AttachmentService to upload id1 only.
478 ConstructGenericChangeProcessor(kType
);
479 ASSERT_EQ(1U, mock_attachment_service()->attachment_id_sets()->size());
480 ASSERT_THAT(mock_attachment_service()->attachment_id_sets()->front(),
481 testing::UnorderedElementsAre(id1
));
486 } // namespace sync_driver