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/strings/stringprintf.h"
11 #include "components/sync_driver/data_type_error_handler_mock.h"
12 #include "components/sync_driver/sync_api_component_factory.h"
13 #include "sync/api/attachments/fake_attachment_store.h"
14 #include "sync/api/fake_syncable_service.h"
15 #include "sync/api/sync_change.h"
16 #include "sync/api/sync_merge_result.h"
17 #include "sync/internal_api/public/attachments/attachment_service_impl.h"
18 #include "sync/internal_api/public/attachments/fake_attachment_downloader.h"
19 #include "sync/internal_api/public/attachments/fake_attachment_uploader.h"
20 #include "sync/internal_api/public/base/model_type.h"
21 #include "sync/internal_api/public/read_node.h"
22 #include "sync/internal_api/public/read_transaction.h"
23 #include "sync/internal_api/public/sync_encryption_handler.h"
24 #include "sync/internal_api/public/test/test_user_share.h"
25 #include "sync/internal_api/public/user_share.h"
26 #include "sync/internal_api/public/write_node.h"
27 #include "sync/internal_api/public/write_transaction.h"
28 #include "testing/gtest/include/gtest/gtest.h"
30 namespace sync_driver
{
34 const char kTestData
[] = "some data";
36 // A mock that keeps track of attachments passed to StoreAttachments.
37 class MockAttachmentService
: public syncer::AttachmentServiceImpl
{
39 MockAttachmentService();
40 virtual ~MockAttachmentService();
41 virtual void StoreAttachments(const syncer::AttachmentList
& attachments
,
42 const StoreCallback
& callback
) OVERRIDE
;
43 std::vector
<syncer::AttachmentList
>* attachment_lists();
46 std::vector
<syncer::AttachmentList
> attachment_lists_
;
49 MockAttachmentService::MockAttachmentService()
50 : AttachmentServiceImpl(
51 scoped_ptr
<syncer::AttachmentStore
>(new syncer::FakeAttachmentStore(
52 base::MessageLoopProxy::current())),
53 scoped_ptr
<syncer::AttachmentUploader
>(
54 new syncer::FakeAttachmentUploader
),
55 scoped_ptr
<syncer::AttachmentDownloader
>(
56 new syncer::FakeAttachmentDownloader
),
60 MockAttachmentService::~MockAttachmentService() {
63 void MockAttachmentService::StoreAttachments(
64 const syncer::AttachmentList
& attachments
,
65 const StoreCallback
& callback
) {
66 attachment_lists_
.push_back(attachments
);
67 AttachmentServiceImpl::StoreAttachments(attachments
, callback
);
70 std::vector
<syncer::AttachmentList
>* MockAttachmentService::attachment_lists() {
71 return &attachment_lists_
;
74 // MockSyncApiComponentFactory needed to initialize GenericChangeProcessor and
75 // pass MockAttachmentService to it.
76 class MockSyncApiComponentFactory
: public SyncApiComponentFactory
{
78 MockSyncApiComponentFactory(
79 scoped_ptr
<syncer::AttachmentService
> attachment_service
)
80 : attachment_service_(attachment_service
.Pass()) {}
82 virtual base::WeakPtr
<syncer::SyncableService
> GetSyncableServiceForType(
83 syncer::ModelType type
) OVERRIDE
{
84 // Shouldn't be called for this test.
86 return base::WeakPtr
<syncer::SyncableService
>();
89 virtual scoped_ptr
<syncer::AttachmentService
> CreateAttachmentService(
90 const syncer::UserShare
& user_share
,
91 syncer::AttachmentService::Delegate
* delegate
) OVERRIDE
{
92 EXPECT_TRUE(attachment_service_
!= NULL
);
93 return attachment_service_
.Pass();
97 scoped_ptr
<syncer::AttachmentService
> attachment_service_
;
100 class SyncGenericChangeProcessorTest
: public testing::Test
{
102 // It doesn't matter which type we use. Just pick one.
103 static const syncer::ModelType kType
= syncer::PREFERENCES
;
105 SyncGenericChangeProcessorTest()
106 : sync_merge_result_(kType
),
107 merge_result_ptr_factory_(&sync_merge_result_
),
108 syncable_service_ptr_factory_(&fake_syncable_service_
),
109 mock_attachment_service_(NULL
) {}
111 virtual void SetUp() OVERRIDE
{
112 test_user_share_
.SetUp();
113 syncer::ModelTypeSet types
= syncer::ProtocolTypes();
114 for (syncer::ModelTypeSet::Iterator iter
= types
.First(); iter
.Good();
116 syncer::TestUserShare::CreateRoot(iter
.Get(),
117 test_user_share_
.user_share());
119 test_user_share_
.encryption_handler()->Init();
120 scoped_ptr
<MockAttachmentService
> mock_attachment_service(
121 new MockAttachmentService
);
122 // GenericChangeProcessor takes ownership of the AttachmentService, but we
123 // need to have a pointer to it so we can see that it was used properly.
124 // Take a pointer and trust that GenericChangeProcessor does not prematurely
126 mock_attachment_service_
= mock_attachment_service
.get();
127 sync_factory_
.reset(new MockSyncApiComponentFactory(
128 mock_attachment_service
.PassAs
<syncer::AttachmentService
>()));
129 change_processor_
.reset(
130 new GenericChangeProcessor(&data_type_error_handler_
,
131 syncable_service_ptr_factory_
.GetWeakPtr(),
132 merge_result_ptr_factory_
.GetWeakPtr(),
133 test_user_share_
.user_share(),
134 sync_factory_
.get()));
137 virtual void TearDown() OVERRIDE
{
138 mock_attachment_service_
= NULL
;
139 test_user_share_
.TearDown();
142 void BuildChildNodes(int n
) {
143 syncer::WriteTransaction
trans(FROM_HERE
, user_share());
144 syncer::ReadNode
root(&trans
);
145 ASSERT_EQ(syncer::BaseNode::INIT_OK
, root
.InitTypeRoot(kType
));
146 for (int i
= 0; i
< n
; ++i
) {
147 syncer::WriteNode
node(&trans
);
148 node
.InitUniqueByCreation(kType
, root
, base::StringPrintf("node%05d", i
));
152 GenericChangeProcessor
* change_processor() {
153 return change_processor_
.get();
156 syncer::UserShare
* user_share() {
157 return test_user_share_
.user_share();
160 MockAttachmentService
* mock_attachment_service() {
161 return mock_attachment_service_
;
165 base::MessageLoopForUI loop_
;
167 syncer::SyncMergeResult sync_merge_result_
;
168 base::WeakPtrFactory
<syncer::SyncMergeResult
> merge_result_ptr_factory_
;
170 syncer::FakeSyncableService fake_syncable_service_
;
171 base::WeakPtrFactory
<syncer::FakeSyncableService
>
172 syncable_service_ptr_factory_
;
174 DataTypeErrorHandlerMock data_type_error_handler_
;
175 syncer::TestUserShare test_user_share_
;
176 MockAttachmentService
* mock_attachment_service_
;
177 scoped_ptr
<SyncApiComponentFactory
> sync_factory_
;
179 scoped_ptr
<GenericChangeProcessor
> change_processor_
;
182 // Similar to above, but focused on the method that implements sync/api
183 // interfaces and is hence exposed to datatypes directly.
184 TEST_F(SyncGenericChangeProcessorTest
, StressGetAllSyncData
) {
185 const int kNumChildNodes
= 1000;
186 const int kRepeatCount
= 1;
188 ASSERT_NO_FATAL_FAILURE(BuildChildNodes(kNumChildNodes
));
190 for (int i
= 0; i
< kRepeatCount
; ++i
) {
191 syncer::SyncDataList sync_data
=
192 change_processor()->GetAllSyncData(kType
);
194 // Start with a simple test. We can add more in-depth testing later.
195 EXPECT_EQ(static_cast<size_t>(kNumChildNodes
), sync_data
.size());
199 TEST_F(SyncGenericChangeProcessorTest
, SetGetPasswords
) {
200 const int kNumPasswords
= 10;
201 sync_pb::PasswordSpecificsData password_data
;
202 password_data
.set_username_value("user");
204 sync_pb::EntitySpecifics password_holder
;
206 syncer::SyncChangeList change_list
;
207 for (int i
= 0; i
< kNumPasswords
; ++i
) {
208 password_data
.set_password_value(
209 base::StringPrintf("password%i", i
));
210 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
211 CopyFrom(password_data
);
212 change_list
.push_back(
213 syncer::SyncChange(FROM_HERE
,
214 syncer::SyncChange::ACTION_ADD
,
215 syncer::SyncData::CreateLocalData(
216 base::StringPrintf("tag%i", i
),
217 base::StringPrintf("title%i", i
),
222 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
224 syncer::SyncDataList
password_list(
225 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
227 ASSERT_EQ(password_list
.size(), change_list
.size());
228 for (int i
= 0; i
< kNumPasswords
; ++i
) {
229 // Verify the password is returned properly.
230 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
231 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
232 has_client_only_encrypted_data());
233 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
234 const sync_pb::PasswordSpecificsData
& sync_password
=
235 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
236 const sync_pb::PasswordSpecificsData
& change_password
=
237 change_list
[i
].sync_data().GetSpecifics().password().
238 client_only_encrypted_data();
239 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
240 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
242 // Verify the raw sync data was stored securely.
243 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
244 syncer::ReadNode
node(&read_transaction
);
245 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
246 base::StringPrintf("tag%i", i
)),
247 syncer::BaseNode::INIT_OK
);
248 ASSERT_EQ(node
.GetTitle(), "encrypted");
249 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
250 ASSERT_TRUE(raw_specifics
.has_password());
251 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
252 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
256 TEST_F(SyncGenericChangeProcessorTest
, UpdatePasswords
) {
257 const int kNumPasswords
= 10;
258 sync_pb::PasswordSpecificsData password_data
;
259 password_data
.set_username_value("user");
261 sync_pb::EntitySpecifics password_holder
;
263 syncer::SyncChangeList change_list
;
264 syncer::SyncChangeList change_list2
;
265 for (int i
= 0; i
< kNumPasswords
; ++i
) {
266 password_data
.set_password_value(
267 base::StringPrintf("password%i", i
));
268 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
269 CopyFrom(password_data
);
270 change_list
.push_back(
271 syncer::SyncChange(FROM_HERE
,
272 syncer::SyncChange::ACTION_ADD
,
273 syncer::SyncData::CreateLocalData(
274 base::StringPrintf("tag%i", i
),
275 base::StringPrintf("title%i", i
),
277 password_data
.set_password_value(
278 base::StringPrintf("password_m%i", i
));
279 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
280 CopyFrom(password_data
);
281 change_list2
.push_back(
282 syncer::SyncChange(FROM_HERE
,
283 syncer::SyncChange::ACTION_UPDATE
,
284 syncer::SyncData::CreateLocalData(
285 base::StringPrintf("tag%i", i
),
286 base::StringPrintf("title_m%i", i
),
291 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
293 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list2
).IsSet());
295 syncer::SyncDataList
password_list(
296 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
298 ASSERT_EQ(password_list
.size(), change_list2
.size());
299 for (int i
= 0; i
< kNumPasswords
; ++i
) {
300 // Verify the password is returned properly.
301 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
302 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
303 has_client_only_encrypted_data());
304 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
305 const sync_pb::PasswordSpecificsData
& sync_password
=
306 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
307 const sync_pb::PasswordSpecificsData
& change_password
=
308 change_list2
[i
].sync_data().GetSpecifics().password().
309 client_only_encrypted_data();
310 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
311 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
313 // Verify the raw sync data was stored securely.
314 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
315 syncer::ReadNode
node(&read_transaction
);
316 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
317 base::StringPrintf("tag%i", i
)),
318 syncer::BaseNode::INIT_OK
);
319 ASSERT_EQ(node
.GetTitle(), "encrypted");
320 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
321 ASSERT_TRUE(raw_specifics
.has_password());
322 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
323 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
327 // Verify that attachments on newly added or updated SyncData are passed to the
328 // AttachmentService.
329 TEST_F(SyncGenericChangeProcessorTest
,
330 ProcessSyncChanges_AddUpdateWithAttachment
) {
331 std::string tag
= "client_tag";
332 std::string title
= "client_title";
333 sync_pb::EntitySpecifics specifics
;
334 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
335 pref_specifics
->set_name("test");
336 syncer::AttachmentList attachments
;
337 scoped_refptr
<base::RefCountedString
> attachment_data
=
338 new base::RefCountedString
;
339 attachment_data
->data() = kTestData
;
340 attachments
.push_back(syncer::Attachment::Create(attachment_data
));
341 attachments
.push_back(syncer::Attachment::Create(attachment_data
));
343 // Add a SyncData with two attachments.
344 syncer::SyncChangeList change_list
;
345 change_list
.push_back(
346 syncer::SyncChange(FROM_HERE
,
347 syncer::SyncChange::ACTION_ADD
,
348 syncer::SyncData::CreateLocalDataWithAttachments(
349 tag
, title
, specifics
, attachments
)));
351 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
353 // Check that the AttachmentService received the new attachments.
354 ASSERT_EQ(mock_attachment_service()->attachment_lists()->size(), 1U);
355 const syncer::AttachmentList
& attachments_added
=
356 mock_attachment_service()->attachment_lists()->front();
357 ASSERT_EQ(attachments_added
.size(), 2U);
358 ASSERT_EQ(attachments_added
[0].GetId(), attachments
[0].GetId());
359 ASSERT_EQ(attachments_added
[1].GetId(), attachments
[1].GetId());
361 // Update the SyncData, replacing its two attachments with one new attachment.
362 syncer::AttachmentList new_attachments
;
363 new_attachments
.push_back(syncer::Attachment::Create(attachment_data
));
364 mock_attachment_service()->attachment_lists()->clear();
366 change_list
.push_back(
367 syncer::SyncChange(FROM_HERE
,
368 syncer::SyncChange::ACTION_UPDATE
,
369 syncer::SyncData::CreateLocalDataWithAttachments(
370 tag
, title
, specifics
, new_attachments
)));
372 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
374 // Check that the AttachmentService received it.
375 ASSERT_EQ(mock_attachment_service()->attachment_lists()->size(), 1U);
376 const syncer::AttachmentList
& new_attachments_added
=
377 mock_attachment_service()->attachment_lists()->front();
378 ASSERT_EQ(new_attachments_added
.size(), 1U);
379 ASSERT_EQ(new_attachments_added
[0].GetId(), new_attachments
[0].GetId());
382 // Verify that after attachment is uploaded GenericChangeProcessor updates
383 // corresponding entries
384 TEST_F(SyncGenericChangeProcessorTest
, AttachmentUploaded
) {
385 std::string tag
= "client_tag";
386 std::string title
= "client_title";
387 sync_pb::EntitySpecifics specifics
;
388 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
389 pref_specifics
->set_name("test");
390 syncer::AttachmentList attachments
;
391 scoped_refptr
<base::RefCountedString
> attachment_data
=
392 new base::RefCountedString
;
393 attachment_data
->data() = kTestData
;
394 attachments
.push_back(syncer::Attachment::Create(attachment_data
));
396 // Add a SyncData with two attachments.
397 syncer::SyncChangeList change_list
;
398 change_list
.push_back(
399 syncer::SyncChange(FROM_HERE
,
400 syncer::SyncChange::ACTION_ADD
,
401 syncer::SyncData::CreateLocalDataWithAttachments(
402 tag
, title
, specifics
, attachments
)));
404 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
406 sync_pb::AttachmentIdProto attachment_id_proto
=
407 attachments
[0].GetId().GetProto();
408 syncer::AttachmentId attachment_id
=
409 syncer::AttachmentId::CreateFromProto(attachment_id_proto
);
411 change_processor()->OnAttachmentUploaded(attachment_id
);
412 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
413 syncer::ReadNode
node(&read_transaction
);
414 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PREFERENCES
, tag
),
415 syncer::BaseNode::INIT_OK
);
416 syncer::AttachmentIdList attachment_ids
= node
.GetAttachmentIds();
417 EXPECT_EQ(1U, attachment_ids
.size());
422 } // namespace sync_driver