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