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/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/fake_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"
32
33namespace sync_driver {
34
35namespace {
36
37// A mock that keeps track of attachments passed to UploadAttachments.
38class MockAttachmentService : public syncer::AttachmentServiceImpl {
39 public:
40  MockAttachmentService(
41      const scoped_refptr<syncer::AttachmentStore>& attachment_store);
42  virtual ~MockAttachmentService();
43  virtual void UploadAttachments(
44      const syncer::AttachmentIdSet& attachment_ids) OVERRIDE;
45  std::vector<syncer::AttachmentIdSet>* attachment_id_sets();
46
47 private:
48  std::vector<syncer::AttachmentIdSet> attachment_id_sets_;
49};
50
51MockAttachmentService::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),
58                            NULL,
59                            base::TimeDelta(),
60                            base::TimeDelta()) {
61}
62
63MockAttachmentService::~MockAttachmentService() {
64}
65
66void MockAttachmentService::UploadAttachments(
67    const syncer::AttachmentIdSet& attachment_ids) {
68  attachment_id_sets_.push_back(attachment_ids);
69  AttachmentServiceImpl::UploadAttachments(attachment_ids);
70}
71
72std::vector<syncer::AttachmentIdSet>*
73MockAttachmentService::attachment_id_sets() {
74  return &attachment_id_sets_;
75}
76
77// MockSyncApiComponentFactory needed to initialize GenericChangeProcessor and
78// pass MockAttachmentService to it.
79class MockSyncApiComponentFactory : public SyncApiComponentFactory {
80 public:
81  MockSyncApiComponentFactory(
82      scoped_ptr<syncer::AttachmentService> attachment_service)
83      : attachment_service_(attachment_service.Pass()) {}
84
85  virtual base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType(
86      syncer::ModelType type) OVERRIDE {
87    // Shouldn't be called for this test.
88    NOTREACHED();
89    return base::WeakPtr<syncer::SyncableService>();
90  }
91
92  virtual scoped_ptr<syncer::AttachmentService> CreateAttachmentService(
93      const scoped_refptr<syncer::AttachmentStore>& attachment_store,
94      const syncer::UserShare& user_share,
95      syncer::AttachmentService::Delegate* delegate) OVERRIDE {
96    EXPECT_TRUE(attachment_service_ != NULL);
97    return attachment_service_.Pass();
98  }
99
100 private:
101  scoped_ptr<syncer::AttachmentService> attachment_service_;
102};
103
104class SyncGenericChangeProcessorTest : public testing::Test {
105 public:
106  // Most test cases will use this type.  For those that need a
107  // GenericChangeProcessor for a different type, use |InitializeForType|.
108  static const syncer::ModelType kType = syncer::PREFERENCES;
109
110  SyncGenericChangeProcessorTest()
111      : syncable_service_ptr_factory_(&fake_syncable_service_),
112        mock_attachment_service_(NULL) {}
113
114  virtual void SetUp() OVERRIDE {
115    // Use kType by default, but allow test cases to re-initialize with whatever
116    // type they choose.  Therefore, it's important that all type dependent
117    // initialization occurs in InitializeForType.
118    InitializeForType(kType);
119  }
120
121  virtual void TearDown() OVERRIDE {
122    mock_attachment_service_ = NULL;
123    if (test_user_share_) {
124      test_user_share_->TearDown();
125    }
126  }
127
128  // Initialize GenericChangeProcessor and related classes for testing with
129  // model type |type|.
130  void InitializeForType(syncer::ModelType type) {
131    TearDown();
132    test_user_share_.reset(new syncer::TestUserShare);
133    test_user_share_->SetUp();
134    sync_merge_result_.reset(new syncer::SyncMergeResult(type));
135    merge_result_ptr_factory_.reset(
136        new base::WeakPtrFactory<syncer::SyncMergeResult>(
137            sync_merge_result_.get()));
138
139    syncer::ModelTypeSet types = syncer::ProtocolTypes();
140    for (syncer::ModelTypeSet::Iterator iter = types.First(); iter.Good();
141         iter.Inc()) {
142      syncer::TestUserShare::CreateRoot(iter.Get(),
143                                        test_user_share_->user_share());
144    }
145    test_user_share_->encryption_handler()->Init();
146    ConstructGenericChangeProcessor(type);
147  }
148
149  void ConstructGenericChangeProcessor(syncer::ModelType type) {
150    scoped_refptr<syncer::AttachmentStore> attachment_store(
151        new syncer::FakeAttachmentStore(base::MessageLoopProxy::current()));
152    scoped_ptr<MockAttachmentService> mock_attachment_service(
153        new MockAttachmentService(attachment_store));
154    // GenericChangeProcessor takes ownership of the AttachmentService, but we
155    // need to have a pointer to it so we can see that it was used properly.
156    // Take a pointer and trust that GenericChangeProcessor does not prematurely
157    // destroy it.
158    mock_attachment_service_ = mock_attachment_service.get();
159    sync_factory_.reset(new MockSyncApiComponentFactory(
160        mock_attachment_service.PassAs<syncer::AttachmentService>()));
161    change_processor_.reset(
162        new GenericChangeProcessor(type,
163                                   &data_type_error_handler_,
164                                   syncable_service_ptr_factory_.GetWeakPtr(),
165                                   merge_result_ptr_factory_->GetWeakPtr(),
166                                   test_user_share_->user_share(),
167                                   sync_factory_.get(),
168                                   attachment_store));
169  }
170
171  void BuildChildNodes(syncer::ModelType type, int n) {
172    syncer::WriteTransaction trans(FROM_HERE, user_share());
173    syncer::ReadNode root(&trans);
174    ASSERT_EQ(syncer::BaseNode::INIT_OK, root.InitTypeRoot(type));
175    for (int i = 0; i < n; ++i) {
176      syncer::WriteNode node(&trans);
177      node.InitUniqueByCreation(type, root, base::StringPrintf("node%05d", i));
178    }
179  }
180
181  GenericChangeProcessor* change_processor() {
182    return change_processor_.get();
183  }
184
185  syncer::UserShare* user_share() {
186    return test_user_share_->user_share();
187  }
188
189  MockAttachmentService* mock_attachment_service() {
190    return mock_attachment_service_;
191  }
192
193  void RunLoop() {
194    base::RunLoop run_loop;
195    run_loop.RunUntilIdle();
196  }
197
198 private:
199  base::MessageLoopForUI loop_;
200
201  scoped_ptr<syncer::SyncMergeResult> sync_merge_result_;
202  scoped_ptr<base::WeakPtrFactory<syncer::SyncMergeResult> >
203      merge_result_ptr_factory_;
204
205  syncer::FakeSyncableService fake_syncable_service_;
206  base::WeakPtrFactory<syncer::FakeSyncableService>
207      syncable_service_ptr_factory_;
208
209  DataTypeErrorHandlerMock data_type_error_handler_;
210  scoped_ptr<syncer::TestUserShare> test_user_share_;
211  MockAttachmentService* mock_attachment_service_;
212  scoped_ptr<SyncApiComponentFactory> sync_factory_;
213
214  scoped_ptr<GenericChangeProcessor> change_processor_;
215};
216
217// Similar to above, but focused on the method that implements sync/api
218// interfaces and is hence exposed to datatypes directly.
219TEST_F(SyncGenericChangeProcessorTest, StressGetAllSyncData) {
220  const int kNumChildNodes = 1000;
221  const int kRepeatCount = 1;
222
223  ASSERT_NO_FATAL_FAILURE(BuildChildNodes(kType, kNumChildNodes));
224
225  for (int i = 0; i < kRepeatCount; ++i) {
226    syncer::SyncDataList sync_data =
227        change_processor()->GetAllSyncData(kType);
228
229    // Start with a simple test.  We can add more in-depth testing later.
230    EXPECT_EQ(static_cast<size_t>(kNumChildNodes), sync_data.size());
231  }
232}
233
234TEST_F(SyncGenericChangeProcessorTest, SetGetPasswords) {
235  InitializeForType(syncer::PASSWORDS);
236  const int kNumPasswords = 10;
237  sync_pb::PasswordSpecificsData password_data;
238  password_data.set_username_value("user");
239
240  sync_pb::EntitySpecifics password_holder;
241
242  syncer::SyncChangeList change_list;
243  for (int i = 0; i < kNumPasswords; ++i) {
244    password_data.set_password_value(
245        base::StringPrintf("password%i", i));
246    password_holder.mutable_password()->mutable_client_only_encrypted_data()->
247        CopyFrom(password_data);
248    change_list.push_back(
249        syncer::SyncChange(FROM_HERE,
250                           syncer::SyncChange::ACTION_ADD,
251                           syncer::SyncData::CreateLocalData(
252                               base::StringPrintf("tag%i", i),
253                               base::StringPrintf("title%i", i),
254                               password_holder)));
255  }
256
257  ASSERT_FALSE(
258      change_processor()->ProcessSyncChanges(FROM_HERE, change_list).IsSet());
259
260  syncer::SyncDataList password_list(
261      change_processor()->GetAllSyncData(syncer::PASSWORDS));
262
263  ASSERT_EQ(password_list.size(), change_list.size());
264  for (int i = 0; i < kNumPasswords; ++i) {
265    // Verify the password is returned properly.
266    ASSERT_TRUE(password_list[i].GetSpecifics().has_password());
267    ASSERT_TRUE(password_list[i].GetSpecifics().password().
268                    has_client_only_encrypted_data());
269    ASSERT_FALSE(password_list[i].GetSpecifics().password().has_encrypted());
270    const sync_pb::PasswordSpecificsData& sync_password =
271        password_list[i].GetSpecifics().password().client_only_encrypted_data();
272    const sync_pb::PasswordSpecificsData& change_password =
273        change_list[i].sync_data().GetSpecifics().password().
274            client_only_encrypted_data();
275    ASSERT_EQ(sync_password.password_value(), change_password.password_value());
276    ASSERT_EQ(sync_password.username_value(), change_password.username_value());
277
278    // Verify the raw sync data was stored securely.
279    syncer::ReadTransaction read_transaction(FROM_HERE, user_share());
280    syncer::ReadNode node(&read_transaction);
281    ASSERT_EQ(node.InitByClientTagLookup(syncer::PASSWORDS,
282                                         base::StringPrintf("tag%i", i)),
283              syncer::BaseNode::INIT_OK);
284    ASSERT_EQ(node.GetTitle(), "encrypted");
285    const sync_pb::EntitySpecifics& raw_specifics = node.GetEntitySpecifics();
286    ASSERT_TRUE(raw_specifics.has_password());
287    ASSERT_TRUE(raw_specifics.password().has_encrypted());
288    ASSERT_FALSE(raw_specifics.password().has_client_only_encrypted_data());
289  }
290}
291
292TEST_F(SyncGenericChangeProcessorTest, UpdatePasswords) {
293  InitializeForType(syncer::PASSWORDS);
294  const int kNumPasswords = 10;
295  sync_pb::PasswordSpecificsData password_data;
296  password_data.set_username_value("user");
297
298  sync_pb::EntitySpecifics password_holder;
299
300  syncer::SyncChangeList change_list;
301  syncer::SyncChangeList change_list2;
302  for (int i = 0; i < kNumPasswords; ++i) {
303    password_data.set_password_value(
304        base::StringPrintf("password%i", i));
305    password_holder.mutable_password()->mutable_client_only_encrypted_data()->
306        CopyFrom(password_data);
307    change_list.push_back(
308        syncer::SyncChange(FROM_HERE,
309                           syncer::SyncChange::ACTION_ADD,
310                           syncer::SyncData::CreateLocalData(
311                               base::StringPrintf("tag%i", i),
312                               base::StringPrintf("title%i", i),
313                               password_holder)));
314    password_data.set_password_value(
315        base::StringPrintf("password_m%i", i));
316    password_holder.mutable_password()->mutable_client_only_encrypted_data()->
317        CopyFrom(password_data);
318    change_list2.push_back(
319        syncer::SyncChange(FROM_HERE,
320                           syncer::SyncChange::ACTION_UPDATE,
321                           syncer::SyncData::CreateLocalData(
322                               base::StringPrintf("tag%i", i),
323                               base::StringPrintf("title_m%i", i),
324                               password_holder)));
325  }
326
327  ASSERT_FALSE(
328      change_processor()->ProcessSyncChanges(FROM_HERE, change_list).IsSet());
329  ASSERT_FALSE(
330      change_processor()->ProcessSyncChanges(FROM_HERE, change_list2).IsSet());
331
332  syncer::SyncDataList password_list(
333      change_processor()->GetAllSyncData(syncer::PASSWORDS));
334
335  ASSERT_EQ(password_list.size(), change_list2.size());
336  for (int i = 0; i < kNumPasswords; ++i) {
337    // Verify the password is returned properly.
338    ASSERT_TRUE(password_list[i].GetSpecifics().has_password());
339    ASSERT_TRUE(password_list[i].GetSpecifics().password().
340                    has_client_only_encrypted_data());
341    ASSERT_FALSE(password_list[i].GetSpecifics().password().has_encrypted());
342    const sync_pb::PasswordSpecificsData& sync_password =
343        password_list[i].GetSpecifics().password().client_only_encrypted_data();
344    const sync_pb::PasswordSpecificsData& change_password =
345        change_list2[i].sync_data().GetSpecifics().password().
346            client_only_encrypted_data();
347    ASSERT_EQ(sync_password.password_value(), change_password.password_value());
348    ASSERT_EQ(sync_password.username_value(), change_password.username_value());
349
350    // Verify the raw sync data was stored securely.
351    syncer::ReadTransaction read_transaction(FROM_HERE, user_share());
352    syncer::ReadNode node(&read_transaction);
353    ASSERT_EQ(node.InitByClientTagLookup(syncer::PASSWORDS,
354                                         base::StringPrintf("tag%i", i)),
355              syncer::BaseNode::INIT_OK);
356    ASSERT_EQ(node.GetTitle(), "encrypted");
357    const sync_pb::EntitySpecifics& raw_specifics = node.GetEntitySpecifics();
358    ASSERT_TRUE(raw_specifics.has_password());
359    ASSERT_TRUE(raw_specifics.password().has_encrypted());
360    ASSERT_FALSE(raw_specifics.password().has_client_only_encrypted_data());
361  }
362}
363
364// Verify that attachments on newly added or updated SyncData are passed to the
365// AttachmentService.
366TEST_F(SyncGenericChangeProcessorTest,
367       ProcessSyncChanges_AddUpdateWithAttachment) {
368  std::string tag = "client_tag";
369  std::string title = "client_title";
370  sync_pb::EntitySpecifics specifics;
371  sync_pb::PreferenceSpecifics* pref_specifics = specifics.mutable_preference();
372  pref_specifics->set_name("test");
373
374  syncer::AttachmentIdList attachment_ids;
375  attachment_ids.push_back(syncer::AttachmentId::Create());
376  attachment_ids.push_back(syncer::AttachmentId::Create());
377
378  // Add a SyncData with two attachments.
379  syncer::SyncChangeList change_list;
380  change_list.push_back(
381      syncer::SyncChange(FROM_HERE,
382                         syncer::SyncChange::ACTION_ADD,
383                         syncer::SyncData::CreateLocalDataWithAttachments(
384                             tag, title, specifics, attachment_ids)));
385  ASSERT_FALSE(
386      change_processor()->ProcessSyncChanges(FROM_HERE, change_list).IsSet());
387  RunLoop();
388
389  // Check that the AttachmentService received the new attachments.
390  ASSERT_EQ(mock_attachment_service()->attachment_id_sets()->size(), 1U);
391  const syncer::AttachmentIdSet& attachments_added =
392      mock_attachment_service()->attachment_id_sets()->front();
393  ASSERT_THAT(
394      attachments_added,
395      testing::UnorderedElementsAre(attachment_ids[0], attachment_ids[1]));
396
397  // Update the SyncData, replacing its two attachments with one new attachment.
398  syncer::AttachmentIdList new_attachment_ids;
399  new_attachment_ids.push_back(syncer::AttachmentId::Create());
400  mock_attachment_service()->attachment_id_sets()->clear();
401  change_list.clear();
402  change_list.push_back(
403      syncer::SyncChange(FROM_HERE,
404                         syncer::SyncChange::ACTION_UPDATE,
405                         syncer::SyncData::CreateLocalDataWithAttachments(
406                             tag, title, specifics, new_attachment_ids)));
407  ASSERT_FALSE(
408      change_processor()->ProcessSyncChanges(FROM_HERE, change_list).IsSet());
409  RunLoop();
410
411  // Check that the AttachmentService received it.
412  ASSERT_EQ(mock_attachment_service()->attachment_id_sets()->size(), 1U);
413  const syncer::AttachmentIdSet& new_attachments_added =
414      mock_attachment_service()->attachment_id_sets()->front();
415  ASSERT_THAT(new_attachments_added,
416              testing::UnorderedElementsAre(new_attachment_ids[0]));
417}
418
419// Verify that after attachment is uploaded GenericChangeProcessor updates
420// corresponding entries
421TEST_F(SyncGenericChangeProcessorTest, AttachmentUploaded) {
422  std::string tag = "client_tag";
423  std::string title = "client_title";
424  sync_pb::EntitySpecifics specifics;
425  sync_pb::PreferenceSpecifics* pref_specifics = specifics.mutable_preference();
426  pref_specifics->set_name("test");
427
428  syncer::AttachmentIdList attachment_ids;
429  attachment_ids.push_back(syncer::AttachmentId::Create());
430
431  // Add a SyncData with two attachments.
432  syncer::SyncChangeList change_list;
433  change_list.push_back(
434      syncer::SyncChange(FROM_HERE,
435                         syncer::SyncChange::ACTION_ADD,
436                         syncer::SyncData::CreateLocalDataWithAttachments(
437                             tag, title, specifics, attachment_ids)));
438  ASSERT_FALSE(
439      change_processor()->ProcessSyncChanges(FROM_HERE, change_list).IsSet());
440
441  sync_pb::AttachmentIdProto attachment_id_proto = attachment_ids[0].GetProto();
442  syncer::AttachmentId attachment_id =
443      syncer::AttachmentId::CreateFromProto(attachment_id_proto);
444
445  change_processor()->OnAttachmentUploaded(attachment_id);
446  syncer::ReadTransaction read_transaction(FROM_HERE, user_share());
447  syncer::ReadNode node(&read_transaction);
448  ASSERT_EQ(node.InitByClientTagLookup(kType, tag), syncer::BaseNode::INIT_OK);
449  attachment_ids = node.GetAttachmentIds();
450  EXPECT_EQ(1U, attachment_ids.size());
451}
452
453// Verify that upon construction, all attachments not yet on the server are
454// scheduled for upload.
455TEST_F(SyncGenericChangeProcessorTest, UploadAllAttachmentsNotOnServer) {
456  // Create two attachment ids.  id2 will be marked as "on server".
457  syncer::AttachmentId id1 = syncer::AttachmentId::Create();
458  syncer::AttachmentId id2 = syncer::AttachmentId::Create();
459  {
460    // Write an entry containing these two attachment ids.
461    syncer::WriteTransaction trans(FROM_HERE, user_share());
462    syncer::ReadNode root(&trans);
463    ASSERT_EQ(syncer::BaseNode::INIT_OK, root.InitTypeRoot(kType));
464    syncer::WriteNode node(&trans);
465    node.InitUniqueByCreation(kType, root, "some node");
466    sync_pb::AttachmentMetadata metadata;
467    sync_pb::AttachmentMetadataRecord* record1 = metadata.add_record();
468    *record1->mutable_id() = id1.GetProto();
469    sync_pb::AttachmentMetadataRecord* record2 = metadata.add_record();
470    *record2->mutable_id() = id2.GetProto();
471    record2->set_is_on_server(true);
472    node.SetAttachmentMetadata(metadata);
473  }
474
475  // Construct the GenericChangeProcessor and see that it asks the
476  // AttachmentService to upload id1 only.
477  ConstructGenericChangeProcessor(kType);
478  ASSERT_EQ(1U, mock_attachment_service()->attachment_id_sets()->size());
479  ASSERT_THAT(mock_attachment_service()->attachment_id_sets()->front(),
480              testing::UnorderedElementsAre(id1));
481}
482
483}  // namespace
484
485}  // namespace sync_driver
486