• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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