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/attachment_service_impl.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/fake_attachment_store.h"
18 #include "sync/internal_api/public/attachments/fake_attachment_uploader.h"
19 #include "sync/internal_api/public/base/model_type.h"
20 #include "sync/internal_api/public/read_node.h"
21 #include "sync/internal_api/public/read_transaction.h"
22 #include "sync/internal_api/public/sync_encryption_handler.h"
23 #include "sync/internal_api/public/test/test_user_share.h"
24 #include "sync/internal_api/public/user_share.h"
25 #include "sync/internal_api/public/write_node.h"
26 #include "sync/internal_api/public/write_transaction.h"
27 #include "testing/gtest/include/gtest/gtest.h"
29 namespace browser_sync
{
33 const char kTestData
[] = "some data";
35 // A mock that keeps track of attachments passed to StoreAttachments.
36 class MockAttachmentService
: public syncer::AttachmentServiceImpl
{
38 MockAttachmentService();
39 virtual ~MockAttachmentService();
40 virtual void StoreAttachments(const syncer::AttachmentList
& attachments
,
41 const StoreCallback
& callback
) OVERRIDE
;
42 std::vector
<syncer::AttachmentList
>* attachment_lists();
45 std::vector
<syncer::AttachmentList
> attachment_lists_
;
48 MockAttachmentService::MockAttachmentService()
49 : AttachmentServiceImpl(
50 scoped_ptr
<syncer::AttachmentStore
>(new syncer::FakeAttachmentStore(
51 base::MessageLoopProxy::current())),
52 scoped_ptr
<syncer::AttachmentUploader
>(
53 new syncer::FakeAttachmentUploader
),
57 MockAttachmentService::~MockAttachmentService() {
60 void MockAttachmentService::StoreAttachments(
61 const syncer::AttachmentList
& attachments
,
62 const StoreCallback
& callback
) {
63 attachment_lists_
.push_back(attachments
);
64 AttachmentServiceImpl::StoreAttachments(attachments
, callback
);
67 std::vector
<syncer::AttachmentList
>* MockAttachmentService::attachment_lists() {
68 return &attachment_lists_
;
71 // MockSyncApiComponentFactory needed to initialize GenericChangeProcessor and
72 // pass MockAttachmentService to it.
73 class MockSyncApiComponentFactory
: public SyncApiComponentFactory
{
75 MockSyncApiComponentFactory(
76 scoped_ptr
<syncer::AttachmentService
> attachment_service
)
77 : attachment_service_(attachment_service
.Pass()) {}
79 virtual base::WeakPtr
<syncer::SyncableService
> GetSyncableServiceForType(
80 syncer::ModelType type
) OVERRIDE
{
81 // Shouldn't be called for this test.
83 return base::WeakPtr
<syncer::SyncableService
>();
86 virtual scoped_ptr
<syncer::AttachmentService
> CreateAttachmentService(
87 syncer::AttachmentService::Delegate
* delegate
) OVERRIDE
{
88 EXPECT_TRUE(attachment_service_
!= NULL
);
89 return attachment_service_
.Pass();
93 scoped_ptr
<syncer::AttachmentService
> attachment_service_
;
96 class SyncGenericChangeProcessorTest
: public testing::Test
{
98 // It doesn't matter which type we use. Just pick one.
99 static const syncer::ModelType kType
= syncer::PREFERENCES
;
101 SyncGenericChangeProcessorTest()
102 : sync_merge_result_(kType
),
103 merge_result_ptr_factory_(&sync_merge_result_
),
104 syncable_service_ptr_factory_(&fake_syncable_service_
),
105 mock_attachment_service_(NULL
) {}
107 virtual void SetUp() OVERRIDE
{
108 test_user_share_
.SetUp();
109 syncer::ModelTypeSet types
= syncer::ProtocolTypes();
110 for (syncer::ModelTypeSet::Iterator iter
= types
.First(); iter
.Good();
112 syncer::TestUserShare::CreateRoot(iter
.Get(),
113 test_user_share_
.user_share());
115 test_user_share_
.encryption_handler()->Init();
116 scoped_ptr
<MockAttachmentService
> mock_attachment_service(
117 new MockAttachmentService
);
118 // GenericChangeProcessor takes ownership of the AttachmentService, but we
119 // need to have a pointer to it so we can see that it was used properly.
120 // Take a pointer and trust that GenericChangeProcessor does not prematurely
122 mock_attachment_service_
= mock_attachment_service
.get();
123 sync_factory_
.reset(new MockSyncApiComponentFactory(
124 mock_attachment_service
.PassAs
<syncer::AttachmentService
>()));
125 change_processor_
.reset(
126 new GenericChangeProcessor(&data_type_error_handler_
,
127 syncable_service_ptr_factory_
.GetWeakPtr(),
128 merge_result_ptr_factory_
.GetWeakPtr(),
129 test_user_share_
.user_share(),
130 sync_factory_
.get()));
133 virtual void TearDown() OVERRIDE
{
134 mock_attachment_service_
= NULL
;
135 test_user_share_
.TearDown();
138 void BuildChildNodes(int n
) {
139 syncer::WriteTransaction
trans(FROM_HERE
, user_share());
140 syncer::ReadNode
root(&trans
);
141 ASSERT_EQ(syncer::BaseNode::INIT_OK
,
142 root
.InitByTagLookup(syncer::ModelTypeToRootTag(kType
)));
143 for (int i
= 0; i
< n
; ++i
) {
144 syncer::WriteNode
node(&trans
);
145 node
.InitUniqueByCreation(kType
, root
, base::StringPrintf("node%05d", i
));
149 GenericChangeProcessor
* change_processor() {
150 return change_processor_
.get();
153 syncer::UserShare
* user_share() {
154 return test_user_share_
.user_share();
157 MockAttachmentService
* mock_attachment_service() {
158 return mock_attachment_service_
;
162 base::MessageLoopForUI loop_
;
164 syncer::SyncMergeResult sync_merge_result_
;
165 base::WeakPtrFactory
<syncer::SyncMergeResult
> merge_result_ptr_factory_
;
167 syncer::FakeSyncableService fake_syncable_service_
;
168 base::WeakPtrFactory
<syncer::FakeSyncableService
>
169 syncable_service_ptr_factory_
;
171 DataTypeErrorHandlerMock data_type_error_handler_
;
172 syncer::TestUserShare test_user_share_
;
173 MockAttachmentService
* mock_attachment_service_
;
174 scoped_ptr
<SyncApiComponentFactory
> sync_factory_
;
176 scoped_ptr
<GenericChangeProcessor
> change_processor_
;
179 // Similar to above, but focused on the method that implements sync/api
180 // interfaces and is hence exposed to datatypes directly.
181 TEST_F(SyncGenericChangeProcessorTest
, StressGetAllSyncData
) {
182 const int kNumChildNodes
= 1000;
183 const int kRepeatCount
= 1;
185 ASSERT_NO_FATAL_FAILURE(BuildChildNodes(kNumChildNodes
));
187 for (int i
= 0; i
< kRepeatCount
; ++i
) {
188 syncer::SyncDataList sync_data
=
189 change_processor()->GetAllSyncData(kType
);
191 // Start with a simple test. We can add more in-depth testing later.
192 EXPECT_EQ(static_cast<size_t>(kNumChildNodes
), sync_data
.size());
196 TEST_F(SyncGenericChangeProcessorTest
, SetGetPasswords
) {
197 const int kNumPasswords
= 10;
198 sync_pb::PasswordSpecificsData password_data
;
199 password_data
.set_username_value("user");
201 sync_pb::EntitySpecifics password_holder
;
203 syncer::SyncChangeList change_list
;
204 for (int i
= 0; i
< kNumPasswords
; ++i
) {
205 password_data
.set_password_value(
206 base::StringPrintf("password%i", i
));
207 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
208 CopyFrom(password_data
);
209 change_list
.push_back(
210 syncer::SyncChange(FROM_HERE
,
211 syncer::SyncChange::ACTION_ADD
,
212 syncer::SyncData::CreateLocalData(
213 base::StringPrintf("tag%i", i
),
214 base::StringPrintf("title%i", i
),
219 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
221 syncer::SyncDataList
password_list(
222 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
224 ASSERT_EQ(password_list
.size(), change_list
.size());
225 for (int i
= 0; i
< kNumPasswords
; ++i
) {
226 // Verify the password is returned properly.
227 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
228 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
229 has_client_only_encrypted_data());
230 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
231 const sync_pb::PasswordSpecificsData
& sync_password
=
232 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
233 const sync_pb::PasswordSpecificsData
& change_password
=
234 change_list
[i
].sync_data().GetSpecifics().password().
235 client_only_encrypted_data();
236 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
237 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
239 // Verify the raw sync data was stored securely.
240 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
241 syncer::ReadNode
node(&read_transaction
);
242 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
243 base::StringPrintf("tag%i", i
)),
244 syncer::BaseNode::INIT_OK
);
245 ASSERT_EQ(node
.GetTitle(), "encrypted");
246 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
247 ASSERT_TRUE(raw_specifics
.has_password());
248 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
249 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
253 TEST_F(SyncGenericChangeProcessorTest
, UpdatePasswords
) {
254 const int kNumPasswords
= 10;
255 sync_pb::PasswordSpecificsData password_data
;
256 password_data
.set_username_value("user");
258 sync_pb::EntitySpecifics password_holder
;
260 syncer::SyncChangeList change_list
;
261 syncer::SyncChangeList change_list2
;
262 for (int i
= 0; i
< kNumPasswords
; ++i
) {
263 password_data
.set_password_value(
264 base::StringPrintf("password%i", i
));
265 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
266 CopyFrom(password_data
);
267 change_list
.push_back(
268 syncer::SyncChange(FROM_HERE
,
269 syncer::SyncChange::ACTION_ADD
,
270 syncer::SyncData::CreateLocalData(
271 base::StringPrintf("tag%i", i
),
272 base::StringPrintf("title%i", i
),
274 password_data
.set_password_value(
275 base::StringPrintf("password_m%i", i
));
276 password_holder
.mutable_password()->mutable_client_only_encrypted_data()->
277 CopyFrom(password_data
);
278 change_list2
.push_back(
279 syncer::SyncChange(FROM_HERE
,
280 syncer::SyncChange::ACTION_UPDATE
,
281 syncer::SyncData::CreateLocalData(
282 base::StringPrintf("tag%i", i
),
283 base::StringPrintf("title_m%i", i
),
288 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
290 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list2
).IsSet());
292 syncer::SyncDataList
password_list(
293 change_processor()->GetAllSyncData(syncer::PASSWORDS
));
295 ASSERT_EQ(password_list
.size(), change_list2
.size());
296 for (int i
= 0; i
< kNumPasswords
; ++i
) {
297 // Verify the password is returned properly.
298 ASSERT_TRUE(password_list
[i
].GetSpecifics().has_password());
299 ASSERT_TRUE(password_list
[i
].GetSpecifics().password().
300 has_client_only_encrypted_data());
301 ASSERT_FALSE(password_list
[i
].GetSpecifics().password().has_encrypted());
302 const sync_pb::PasswordSpecificsData
& sync_password
=
303 password_list
[i
].GetSpecifics().password().client_only_encrypted_data();
304 const sync_pb::PasswordSpecificsData
& change_password
=
305 change_list2
[i
].sync_data().GetSpecifics().password().
306 client_only_encrypted_data();
307 ASSERT_EQ(sync_password
.password_value(), change_password
.password_value());
308 ASSERT_EQ(sync_password
.username_value(), change_password
.username_value());
310 // Verify the raw sync data was stored securely.
311 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
312 syncer::ReadNode
node(&read_transaction
);
313 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PASSWORDS
,
314 base::StringPrintf("tag%i", i
)),
315 syncer::BaseNode::INIT_OK
);
316 ASSERT_EQ(node
.GetTitle(), "encrypted");
317 const sync_pb::EntitySpecifics
& raw_specifics
= node
.GetEntitySpecifics();
318 ASSERT_TRUE(raw_specifics
.has_password());
319 ASSERT_TRUE(raw_specifics
.password().has_encrypted());
320 ASSERT_FALSE(raw_specifics
.password().has_client_only_encrypted_data());
324 // Verify that attachments on newly added or updated SyncData are passed to the
325 // AttachmentService.
326 TEST_F(SyncGenericChangeProcessorTest
,
327 ProcessSyncChanges_AddUpdateWithAttachment
) {
328 std::string tag
= "client_tag";
329 std::string title
= "client_title";
330 sync_pb::EntitySpecifics specifics
;
331 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
332 pref_specifics
->set_name("test");
333 syncer::AttachmentList attachments
;
334 scoped_refptr
<base::RefCountedString
> attachment_data
=
335 new base::RefCountedString
;
336 attachment_data
->data() = kTestData
;
337 attachments
.push_back(syncer::Attachment::Create(attachment_data
));
338 attachments
.push_back(syncer::Attachment::Create(attachment_data
));
340 // Add a SyncData with two attachments.
341 syncer::SyncChangeList change_list
;
342 change_list
.push_back(
343 syncer::SyncChange(FROM_HERE
,
344 syncer::SyncChange::ACTION_ADD
,
345 syncer::SyncData::CreateLocalDataWithAttachments(
346 tag
, title
, specifics
, attachments
)));
348 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
350 // Check that the AttachmentService received the new attachments.
351 ASSERT_EQ(mock_attachment_service()->attachment_lists()->size(), 1U);
352 const syncer::AttachmentList
& attachments_added
=
353 mock_attachment_service()->attachment_lists()->front();
354 ASSERT_EQ(attachments_added
.size(), 2U);
355 ASSERT_EQ(attachments_added
[0].GetId(), attachments
[0].GetId());
356 ASSERT_EQ(attachments_added
[1].GetId(), attachments
[1].GetId());
358 // Update the SyncData, replacing its two attachments with one new attachment.
359 syncer::AttachmentList new_attachments
;
360 new_attachments
.push_back(syncer::Attachment::Create(attachment_data
));
361 mock_attachment_service()->attachment_lists()->clear();
363 change_list
.push_back(
364 syncer::SyncChange(FROM_HERE
,
365 syncer::SyncChange::ACTION_UPDATE
,
366 syncer::SyncData::CreateLocalDataWithAttachments(
367 tag
, title
, specifics
, new_attachments
)));
369 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
371 // Check that the AttachmentService received it.
372 ASSERT_EQ(mock_attachment_service()->attachment_lists()->size(), 1U);
373 const syncer::AttachmentList
& new_attachments_added
=
374 mock_attachment_service()->attachment_lists()->front();
375 ASSERT_EQ(new_attachments_added
.size(), 1U);
376 ASSERT_EQ(new_attachments_added
[0].GetId(), new_attachments
[0].GetId());
379 // Verify that after attachment is uploaded GenericChangeProcessor updates
380 // corresponding entries
381 TEST_F(SyncGenericChangeProcessorTest
, AttachmentUploaded
) {
382 std::string tag
= "client_tag";
383 std::string title
= "client_title";
384 sync_pb::EntitySpecifics specifics
;
385 sync_pb::PreferenceSpecifics
* pref_specifics
= specifics
.mutable_preference();
386 pref_specifics
->set_name("test");
387 syncer::AttachmentList attachments
;
388 scoped_refptr
<base::RefCountedString
> attachment_data
=
389 new base::RefCountedString
;
390 attachment_data
->data() = kTestData
;
391 attachments
.push_back(syncer::Attachment::Create(attachment_data
));
393 // Add a SyncData with two attachments.
394 syncer::SyncChangeList change_list
;
395 change_list
.push_back(
396 syncer::SyncChange(FROM_HERE
,
397 syncer::SyncChange::ACTION_ADD
,
398 syncer::SyncData::CreateLocalDataWithAttachments(
399 tag
, title
, specifics
, attachments
)));
401 change_processor()->ProcessSyncChanges(FROM_HERE
, change_list
).IsSet());
403 sync_pb::AttachmentIdProto attachment_id_proto
=
404 attachments
[0].GetId().GetProto();
405 syncer::AttachmentId attachment_id
=
406 syncer::AttachmentId::CreateFromProto(attachment_id_proto
);
408 change_processor()->OnAttachmentUploaded(attachment_id
);
409 syncer::ReadTransaction
read_transaction(FROM_HERE
, user_share());
410 syncer::ReadNode
node(&read_transaction
);
411 ASSERT_EQ(node
.InitByClientTagLookup(syncer::PREFERENCES
, tag
),
412 syncer::BaseNode::INIT_OK
);
413 syncer::AttachmentIdList attachment_ids
= node
.GetAttachmentIds();
414 EXPECT_EQ(1U, attachment_ids
.size());
419 } // namespace browser_sync