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