1 // Copyright 2012 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 // Unit tests for the SyncApi. Note that a lot of the underlying 6 // functionality is provided by the Syncable layer, which has its own 7 // unit tests. We'll test SyncApi specific things in this harness. 8 9 #include <cstddef> 10 #include <map> 11 12 #include "base/basictypes.h" 13 #include "base/callback.h" 14 #include "base/compiler_specific.h" 15 #include "base/files/scoped_temp_dir.h" 16 #include "base/format_macros.h" 17 #include "base/location.h" 18 #include "base/memory/scoped_ptr.h" 19 #include "base/message_loop/message_loop.h" 20 #include "base/message_loop/message_loop_proxy.h" 21 #include "base/strings/string_number_conversions.h" 22 #include "base/strings/stringprintf.h" 23 #include "base/strings/utf_string_conversions.h" 24 #include "base/test/values_test_util.h" 25 #include "base/values.h" 26 #include "sync/engine/sync_scheduler.h" 27 #include "sync/internal_api/public/base/model_type_test_util.h" 28 #include "sync/internal_api/public/change_record.h" 29 #include "sync/internal_api/public/engine/model_safe_worker.h" 30 #include "sync/internal_api/public/engine/polling_constants.h" 31 #include "sync/internal_api/public/http_post_provider_factory.h" 32 #include "sync/internal_api/public/http_post_provider_interface.h" 33 #include "sync/internal_api/public/read_node.h" 34 #include "sync/internal_api/public/read_transaction.h" 35 #include "sync/internal_api/public/test/test_entry_factory.h" 36 #include "sync/internal_api/public/test/test_internal_components_factory.h" 37 #include "sync/internal_api/public/test/test_user_share.h" 38 #include "sync/internal_api/public/write_node.h" 39 #include "sync/internal_api/public/write_transaction.h" 40 #include "sync/internal_api/sync_encryption_handler_impl.h" 41 #include "sync/internal_api/sync_manager_impl.h" 42 #include "sync/internal_api/syncapi_internal.h" 43 #include "sync/js/js_arg_list.h" 44 #include "sync/js/js_backend.h" 45 #include "sync/js/js_event_handler.h" 46 #include "sync/js/js_reply_handler.h" 47 #include "sync/js/js_test_util.h" 48 #include "sync/notifier/fake_invalidation_handler.h" 49 #include "sync/notifier/invalidation_handler.h" 50 #include "sync/notifier/invalidator.h" 51 #include "sync/protocol/bookmark_specifics.pb.h" 52 #include "sync/protocol/encryption.pb.h" 53 #include "sync/protocol/extension_specifics.pb.h" 54 #include "sync/protocol/password_specifics.pb.h" 55 #include "sync/protocol/preference_specifics.pb.h" 56 #include "sync/protocol/proto_value_conversions.h" 57 #include "sync/protocol/sync.pb.h" 58 #include "sync/sessions/sync_session.h" 59 #include "sync/syncable/directory.h" 60 #include "sync/syncable/entry.h" 61 #include "sync/syncable/mutable_entry.h" 62 #include "sync/syncable/nigori_util.h" 63 #include "sync/syncable/syncable_id.h" 64 #include "sync/syncable/syncable_read_transaction.h" 65 #include "sync/syncable/syncable_util.h" 66 #include "sync/syncable/syncable_write_transaction.h" 67 #include "sync/test/callback_counter.h" 68 #include "sync/test/engine/fake_sync_scheduler.h" 69 #include "sync/test/engine/test_id_factory.h" 70 #include "sync/test/fake_encryptor.h" 71 #include "sync/util/cryptographer.h" 72 #include "sync/util/extensions_activity.h" 73 #include "sync/util/test_unrecoverable_error_handler.h" 74 #include "sync/util/time.h" 75 #include "testing/gmock/include/gmock/gmock.h" 76 #include "testing/gtest/include/gtest/gtest.h" 77 78 using base::ExpectDictStringValue; 79 using testing::_; 80 using testing::DoAll; 81 using testing::InSequence; 82 using testing::Return; 83 using testing::SaveArg; 84 using testing::StrictMock; 85 86 namespace syncer { 87 88 using sessions::SyncSessionSnapshot; 89 using syncable::GET_BY_HANDLE; 90 using syncable::IS_DEL; 91 using syncable::IS_UNSYNCED; 92 using syncable::NON_UNIQUE_NAME; 93 using syncable::SPECIFICS; 94 using syncable::kEncryptedString; 95 96 namespace { 97 98 const char kTestChromeVersion[] = "test chrome version"; 99 100 void ExpectInt64Value(int64 expected_value, 101 const base::DictionaryValue& value, 102 const std::string& key) { 103 std::string int64_str; 104 EXPECT_TRUE(value.GetString(key, &int64_str)); 105 int64 val = 0; 106 EXPECT_TRUE(base::StringToInt64(int64_str, &val)); 107 EXPECT_EQ(expected_value, val); 108 } 109 110 void ExpectTimeValue(const base::Time& expected_value, 111 const base::DictionaryValue& value, 112 const std::string& key) { 113 std::string time_str; 114 EXPECT_TRUE(value.GetString(key, &time_str)); 115 EXPECT_EQ(GetTimeDebugString(expected_value), time_str); 116 } 117 118 // Makes a non-folder child of the root node. Returns the id of the 119 // newly-created node. 120 int64 MakeNode(UserShare* share, 121 ModelType model_type, 122 const std::string& client_tag) { 123 WriteTransaction trans(FROM_HERE, share); 124 ReadNode root_node(&trans); 125 root_node.InitByRootLookup(); 126 WriteNode node(&trans); 127 WriteNode::InitUniqueByCreationResult result = 128 node.InitUniqueByCreation(model_type, root_node, client_tag); 129 EXPECT_EQ(WriteNode::INIT_SUCCESS, result); 130 node.SetIsFolder(false); 131 return node.GetId(); 132 } 133 134 // Makes a folder child of a non-root node. Returns the id of the 135 // newly-created node. 136 int64 MakeFolderWithParent(UserShare* share, 137 ModelType model_type, 138 int64 parent_id, 139 BaseNode* predecessor) { 140 WriteTransaction trans(FROM_HERE, share); 141 ReadNode parent_node(&trans); 142 EXPECT_EQ(BaseNode::INIT_OK, parent_node.InitByIdLookup(parent_id)); 143 WriteNode node(&trans); 144 EXPECT_TRUE(node.InitBookmarkByCreation(parent_node, predecessor)); 145 node.SetIsFolder(true); 146 return node.GetId(); 147 } 148 149 int64 MakeBookmarkWithParent(UserShare* share, 150 int64 parent_id, 151 BaseNode* predecessor) { 152 WriteTransaction trans(FROM_HERE, share); 153 ReadNode parent_node(&trans); 154 EXPECT_EQ(BaseNode::INIT_OK, parent_node.InitByIdLookup(parent_id)); 155 WriteNode node(&trans); 156 EXPECT_TRUE(node.InitBookmarkByCreation(parent_node, predecessor)); 157 return node.GetId(); 158 } 159 160 // Creates the "synced" root node for a particular datatype. We use the syncable 161 // methods here so that the syncer treats these nodes as if they were already 162 // received from the server. 163 int64 MakeServerNodeForType(UserShare* share, 164 ModelType model_type) { 165 sync_pb::EntitySpecifics specifics; 166 AddDefaultFieldValue(model_type, &specifics); 167 syncable::WriteTransaction trans( 168 FROM_HERE, syncable::UNITTEST, share->directory.get()); 169 // Attempt to lookup by nigori tag. 170 std::string type_tag = ModelTypeToRootTag(model_type); 171 syncable::Id node_id = syncable::Id::CreateFromServerId(type_tag); 172 syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM, 173 node_id); 174 EXPECT_TRUE(entry.good()); 175 entry.Put(syncable::BASE_VERSION, 1); 176 entry.Put(syncable::SERVER_VERSION, 1); 177 entry.Put(syncable::IS_UNAPPLIED_UPDATE, false); 178 entry.Put(syncable::SERVER_PARENT_ID, syncable::GetNullId()); 179 entry.Put(syncable::SERVER_IS_DIR, true); 180 entry.Put(syncable::IS_DIR, true); 181 entry.Put(syncable::SERVER_SPECIFICS, specifics); 182 entry.Put(syncable::UNIQUE_SERVER_TAG, type_tag); 183 entry.Put(syncable::NON_UNIQUE_NAME, type_tag); 184 entry.Put(syncable::IS_DEL, false); 185 entry.Put(syncable::SPECIFICS, specifics); 186 return entry.Get(syncable::META_HANDLE); 187 } 188 189 // Simulates creating a "synced" node as a child of the root datatype node. 190 int64 MakeServerNode(UserShare* share, ModelType model_type, 191 const std::string& client_tag, 192 const std::string& hashed_tag, 193 const sync_pb::EntitySpecifics& specifics) { 194 syncable::WriteTransaction trans( 195 FROM_HERE, syncable::UNITTEST, share->directory.get()); 196 syncable::Entry root_entry(&trans, syncable::GET_BY_SERVER_TAG, 197 ModelTypeToRootTag(model_type)); 198 EXPECT_TRUE(root_entry.good()); 199 syncable::Id root_id = root_entry.Get(syncable::ID); 200 syncable::Id node_id = syncable::Id::CreateFromServerId(client_tag); 201 syncable::MutableEntry entry(&trans, syncable::CREATE_NEW_UPDATE_ITEM, 202 node_id); 203 EXPECT_TRUE(entry.good()); 204 entry.Put(syncable::BASE_VERSION, 1); 205 entry.Put(syncable::SERVER_VERSION, 1); 206 entry.Put(syncable::IS_UNAPPLIED_UPDATE, false); 207 entry.Put(syncable::SERVER_PARENT_ID, root_id); 208 entry.Put(syncable::PARENT_ID, root_id); 209 entry.Put(syncable::SERVER_IS_DIR, false); 210 entry.Put(syncable::IS_DIR, false); 211 entry.Put(syncable::SERVER_SPECIFICS, specifics); 212 entry.Put(syncable::NON_UNIQUE_NAME, client_tag); 213 entry.Put(syncable::UNIQUE_CLIENT_TAG, hashed_tag); 214 entry.Put(syncable::IS_DEL, false); 215 entry.Put(syncable::SPECIFICS, specifics); 216 return entry.Get(syncable::META_HANDLE); 217 } 218 219 } // namespace 220 221 class SyncApiTest : public testing::Test { 222 public: 223 virtual void SetUp() { 224 test_user_share_.SetUp(); 225 } 226 227 virtual void TearDown() { 228 test_user_share_.TearDown(); 229 } 230 231 protected: 232 base::MessageLoop message_loop_; 233 TestUserShare test_user_share_; 234 }; 235 236 TEST_F(SyncApiTest, SanityCheckTest) { 237 { 238 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 239 EXPECT_TRUE(trans.GetWrappedTrans()); 240 } 241 { 242 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 243 EXPECT_TRUE(trans.GetWrappedTrans()); 244 } 245 { 246 // No entries but root should exist 247 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 248 ReadNode node(&trans); 249 // Metahandle 1 can be root, sanity check 2 250 EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_NOT_GOOD, node.InitByIdLookup(2)); 251 } 252 } 253 254 TEST_F(SyncApiTest, BasicTagWrite) { 255 { 256 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 257 ReadNode root_node(&trans); 258 root_node.InitByRootLookup(); 259 EXPECT_EQ(root_node.GetFirstChildId(), 0); 260 } 261 262 ignore_result(MakeNode(test_user_share_.user_share(), 263 BOOKMARKS, "testtag")); 264 265 { 266 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 267 ReadNode node(&trans); 268 EXPECT_EQ(BaseNode::INIT_OK, 269 node.InitByClientTagLookup(BOOKMARKS, "testtag")); 270 271 ReadNode root_node(&trans); 272 root_node.InitByRootLookup(); 273 EXPECT_NE(node.GetId(), 0); 274 EXPECT_EQ(node.GetId(), root_node.GetFirstChildId()); 275 } 276 } 277 278 TEST_F(SyncApiTest, ModelTypesSiloed) { 279 { 280 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 281 ReadNode root_node(&trans); 282 root_node.InitByRootLookup(); 283 EXPECT_EQ(root_node.GetFirstChildId(), 0); 284 } 285 286 ignore_result(MakeNode(test_user_share_.user_share(), 287 BOOKMARKS, "collideme")); 288 ignore_result(MakeNode(test_user_share_.user_share(), 289 PREFERENCES, "collideme")); 290 ignore_result(MakeNode(test_user_share_.user_share(), 291 AUTOFILL, "collideme")); 292 293 { 294 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 295 296 ReadNode bookmarknode(&trans); 297 EXPECT_EQ(BaseNode::INIT_OK, 298 bookmarknode.InitByClientTagLookup(BOOKMARKS, 299 "collideme")); 300 301 ReadNode prefnode(&trans); 302 EXPECT_EQ(BaseNode::INIT_OK, 303 prefnode.InitByClientTagLookup(PREFERENCES, 304 "collideme")); 305 306 ReadNode autofillnode(&trans); 307 EXPECT_EQ(BaseNode::INIT_OK, 308 autofillnode.InitByClientTagLookup(AUTOFILL, 309 "collideme")); 310 311 EXPECT_NE(bookmarknode.GetId(), prefnode.GetId()); 312 EXPECT_NE(autofillnode.GetId(), prefnode.GetId()); 313 EXPECT_NE(bookmarknode.GetId(), autofillnode.GetId()); 314 } 315 } 316 317 TEST_F(SyncApiTest, ReadMissingTagsFails) { 318 { 319 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 320 ReadNode node(&trans); 321 EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_NOT_GOOD, 322 node.InitByClientTagLookup(BOOKMARKS, 323 "testtag")); 324 } 325 { 326 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 327 WriteNode node(&trans); 328 EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_NOT_GOOD, 329 node.InitByClientTagLookup(BOOKMARKS, 330 "testtag")); 331 } 332 } 333 334 // TODO(chron): Hook this all up to the server and write full integration tests 335 // for update->undelete behavior. 336 TEST_F(SyncApiTest, TestDeleteBehavior) { 337 int64 node_id; 338 int64 folder_id; 339 std::string test_title("test1"); 340 341 { 342 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 343 ReadNode root_node(&trans); 344 root_node.InitByRootLookup(); 345 346 // we'll use this spare folder later 347 WriteNode folder_node(&trans); 348 EXPECT_TRUE(folder_node.InitBookmarkByCreation(root_node, NULL)); 349 folder_id = folder_node.GetId(); 350 351 WriteNode wnode(&trans); 352 WriteNode::InitUniqueByCreationResult result = 353 wnode.InitUniqueByCreation(BOOKMARKS, root_node, "testtag"); 354 EXPECT_EQ(WriteNode::INIT_SUCCESS, result); 355 wnode.SetIsFolder(false); 356 wnode.SetTitle(UTF8ToWide(test_title)); 357 358 node_id = wnode.GetId(); 359 } 360 361 // Ensure we can delete something with a tag. 362 { 363 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 364 WriteNode wnode(&trans); 365 EXPECT_EQ(BaseNode::INIT_OK, 366 wnode.InitByClientTagLookup(BOOKMARKS, 367 "testtag")); 368 EXPECT_FALSE(wnode.GetIsFolder()); 369 EXPECT_EQ(wnode.GetTitle(), test_title); 370 371 wnode.Tombstone(); 372 } 373 374 // Lookup of a node which was deleted should return failure, 375 // but have found some data about the node. 376 { 377 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 378 ReadNode node(&trans); 379 EXPECT_EQ(BaseNode::INIT_FAILED_ENTRY_IS_DEL, 380 node.InitByClientTagLookup(BOOKMARKS, 381 "testtag")); 382 // Note that for proper function of this API this doesn't need to be 383 // filled, we're checking just to make sure the DB worked in this test. 384 EXPECT_EQ(node.GetTitle(), test_title); 385 } 386 387 { 388 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 389 ReadNode folder_node(&trans); 390 EXPECT_EQ(BaseNode::INIT_OK, folder_node.InitByIdLookup(folder_id)); 391 392 WriteNode wnode(&trans); 393 // This will undelete the tag. 394 WriteNode::InitUniqueByCreationResult result = 395 wnode.InitUniqueByCreation(BOOKMARKS, folder_node, "testtag"); 396 EXPECT_EQ(WriteNode::INIT_SUCCESS, result); 397 EXPECT_EQ(wnode.GetIsFolder(), false); 398 EXPECT_EQ(wnode.GetParentId(), folder_node.GetId()); 399 EXPECT_EQ(wnode.GetId(), node_id); 400 EXPECT_NE(wnode.GetTitle(), test_title); // Title should be cleared 401 wnode.SetTitle(UTF8ToWide(test_title)); 402 } 403 404 // Now look up should work. 405 { 406 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 407 ReadNode node(&trans); 408 EXPECT_EQ(BaseNode::INIT_OK, 409 node.InitByClientTagLookup(BOOKMARKS, 410 "testtag")); 411 EXPECT_EQ(node.GetTitle(), test_title); 412 EXPECT_EQ(node.GetModelType(), BOOKMARKS); 413 } 414 } 415 416 TEST_F(SyncApiTest, WriteAndReadPassword) { 417 KeyParams params = {"localhost", "username", "passphrase"}; 418 { 419 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 420 trans.GetCryptographer()->AddKey(params); 421 } 422 { 423 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 424 ReadNode root_node(&trans); 425 root_node.InitByRootLookup(); 426 427 WriteNode password_node(&trans); 428 WriteNode::InitUniqueByCreationResult result = 429 password_node.InitUniqueByCreation(PASSWORDS, 430 root_node, "foo"); 431 EXPECT_EQ(WriteNode::INIT_SUCCESS, result); 432 sync_pb::PasswordSpecificsData data; 433 data.set_password_value("secret"); 434 password_node.SetPasswordSpecifics(data); 435 } 436 { 437 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 438 ReadNode root_node(&trans); 439 root_node.InitByRootLookup(); 440 441 ReadNode password_node(&trans); 442 EXPECT_EQ(BaseNode::INIT_OK, 443 password_node.InitByClientTagLookup(PASSWORDS, "foo")); 444 const sync_pb::PasswordSpecificsData& data = 445 password_node.GetPasswordSpecifics(); 446 EXPECT_EQ("secret", data.password_value()); 447 } 448 } 449 450 TEST_F(SyncApiTest, WriteEncryptedTitle) { 451 KeyParams params = {"localhost", "username", "passphrase"}; 452 { 453 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 454 trans.GetCryptographer()->AddKey(params); 455 } 456 test_user_share_.encryption_handler()->EnableEncryptEverything(); 457 int bookmark_id; 458 { 459 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 460 ReadNode root_node(&trans); 461 root_node.InitByRootLookup(); 462 463 WriteNode bookmark_node(&trans); 464 ASSERT_TRUE(bookmark_node.InitBookmarkByCreation(root_node, NULL)); 465 bookmark_id = bookmark_node.GetId(); 466 bookmark_node.SetTitle(UTF8ToWide("foo")); 467 468 WriteNode pref_node(&trans); 469 WriteNode::InitUniqueByCreationResult result = 470 pref_node.InitUniqueByCreation(PREFERENCES, root_node, "bar"); 471 ASSERT_EQ(WriteNode::INIT_SUCCESS, result); 472 pref_node.SetTitle(UTF8ToWide("bar")); 473 } 474 { 475 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 476 ReadNode root_node(&trans); 477 root_node.InitByRootLookup(); 478 479 ReadNode bookmark_node(&trans); 480 ASSERT_EQ(BaseNode::INIT_OK, bookmark_node.InitByIdLookup(bookmark_id)); 481 EXPECT_EQ("foo", bookmark_node.GetTitle()); 482 EXPECT_EQ(kEncryptedString, 483 bookmark_node.GetEntry()->Get(syncable::NON_UNIQUE_NAME)); 484 485 ReadNode pref_node(&trans); 486 ASSERT_EQ(BaseNode::INIT_OK, 487 pref_node.InitByClientTagLookup(PREFERENCES, 488 "bar")); 489 EXPECT_EQ(kEncryptedString, pref_node.GetTitle()); 490 } 491 } 492 493 TEST_F(SyncApiTest, BaseNodeSetSpecifics) { 494 int64 child_id = MakeNode(test_user_share_.user_share(), 495 BOOKMARKS, "testtag"); 496 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 497 WriteNode node(&trans); 498 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(child_id)); 499 500 sync_pb::EntitySpecifics entity_specifics; 501 entity_specifics.mutable_bookmark()->set_url("http://www.google.com"); 502 503 EXPECT_NE(entity_specifics.SerializeAsString(), 504 node.GetEntitySpecifics().SerializeAsString()); 505 node.SetEntitySpecifics(entity_specifics); 506 EXPECT_EQ(entity_specifics.SerializeAsString(), 507 node.GetEntitySpecifics().SerializeAsString()); 508 } 509 510 TEST_F(SyncApiTest, BaseNodeSetSpecificsPreservesUnknownFields) { 511 int64 child_id = MakeNode(test_user_share_.user_share(), 512 BOOKMARKS, "testtag"); 513 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 514 WriteNode node(&trans); 515 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(child_id)); 516 EXPECT_TRUE(node.GetEntitySpecifics().unknown_fields().empty()); 517 518 sync_pb::EntitySpecifics entity_specifics; 519 entity_specifics.mutable_bookmark()->set_url("http://www.google.com"); 520 entity_specifics.mutable_unknown_fields()->AddFixed32(5, 100); 521 node.SetEntitySpecifics(entity_specifics); 522 EXPECT_FALSE(node.GetEntitySpecifics().unknown_fields().empty()); 523 524 entity_specifics.mutable_unknown_fields()->Clear(); 525 node.SetEntitySpecifics(entity_specifics); 526 EXPECT_FALSE(node.GetEntitySpecifics().unknown_fields().empty()); 527 } 528 529 namespace { 530 531 void CheckNodeValue(const BaseNode& node, const base::DictionaryValue& value, 532 bool is_detailed) { 533 size_t expected_field_count = 4; 534 535 ExpectInt64Value(node.GetId(), value, "id"); 536 { 537 bool is_folder = false; 538 EXPECT_TRUE(value.GetBoolean("isFolder", &is_folder)); 539 EXPECT_EQ(node.GetIsFolder(), is_folder); 540 } 541 ExpectDictStringValue(node.GetTitle(), value, "title"); 542 543 ModelType expected_model_type = node.GetModelType(); 544 std::string type_str; 545 EXPECT_TRUE(value.GetString("type", &type_str)); 546 if (expected_model_type >= FIRST_REAL_MODEL_TYPE) { 547 ModelType model_type = ModelTypeFromString(type_str); 548 EXPECT_EQ(expected_model_type, model_type); 549 } else if (expected_model_type == TOP_LEVEL_FOLDER) { 550 EXPECT_EQ("Top-level folder", type_str); 551 } else if (expected_model_type == UNSPECIFIED) { 552 EXPECT_EQ("Unspecified", type_str); 553 } else { 554 ADD_FAILURE(); 555 } 556 557 if (is_detailed) { 558 { 559 scoped_ptr<base::DictionaryValue> expected_entry( 560 node.GetEntry()->ToValue(NULL)); 561 const base::Value* entry = NULL; 562 EXPECT_TRUE(value.Get("entry", &entry)); 563 EXPECT_TRUE(base::Value::Equals(entry, expected_entry.get())); 564 } 565 566 ExpectInt64Value(node.GetParentId(), value, "parentId"); 567 ExpectTimeValue(node.GetModificationTime(), value, "modificationTime"); 568 ExpectInt64Value(node.GetExternalId(), value, "externalId"); 569 expected_field_count += 4; 570 571 if (value.HasKey("predecessorId")) { 572 ExpectInt64Value(node.GetPredecessorId(), value, "predecessorId"); 573 expected_field_count++; 574 } 575 if (value.HasKey("successorId")) { 576 ExpectInt64Value(node.GetSuccessorId(), value, "successorId"); 577 expected_field_count++; 578 } 579 if (value.HasKey("firstChildId")) { 580 ExpectInt64Value(node.GetFirstChildId(), value, "firstChildId"); 581 expected_field_count++; 582 } 583 } 584 585 EXPECT_EQ(expected_field_count, value.size()); 586 } 587 588 } // namespace 589 590 TEST_F(SyncApiTest, BaseNodeGetSummaryAsValue) { 591 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 592 ReadNode node(&trans); 593 node.InitByRootLookup(); 594 scoped_ptr<base::DictionaryValue> details(node.GetSummaryAsValue()); 595 if (details) { 596 CheckNodeValue(node, *details, false); 597 } else { 598 ADD_FAILURE(); 599 } 600 } 601 602 TEST_F(SyncApiTest, BaseNodeGetDetailsAsValue) { 603 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 604 ReadNode node(&trans); 605 node.InitByRootLookup(); 606 scoped_ptr<base::DictionaryValue> details(node.GetDetailsAsValue()); 607 if (details) { 608 CheckNodeValue(node, *details, true); 609 } else { 610 ADD_FAILURE(); 611 } 612 } 613 614 TEST_F(SyncApiTest, EmptyTags) { 615 WriteTransaction trans(FROM_HERE, test_user_share_.user_share()); 616 ReadNode root_node(&trans); 617 root_node.InitByRootLookup(); 618 WriteNode node(&trans); 619 std::string empty_tag; 620 WriteNode::InitUniqueByCreationResult result = 621 node.InitUniqueByCreation(TYPED_URLS, root_node, empty_tag); 622 EXPECT_NE(WriteNode::INIT_SUCCESS, result); 623 EXPECT_EQ(BaseNode::INIT_FAILED_PRECONDITION, 624 node.InitByTagLookup(empty_tag)); 625 } 626 627 // Test counting nodes when the type's root node has no children. 628 TEST_F(SyncApiTest, GetTotalNodeCountEmpty) { 629 int64 type_root = MakeServerNodeForType(test_user_share_.user_share(), 630 BOOKMARKS); 631 { 632 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 633 ReadNode type_root_node(&trans); 634 EXPECT_EQ(BaseNode::INIT_OK, 635 type_root_node.InitByIdLookup(type_root)); 636 EXPECT_EQ(1, type_root_node.GetTotalNodeCount()); 637 } 638 } 639 640 // Test counting nodes when there is one child beneath the type's root. 641 TEST_F(SyncApiTest, GetTotalNodeCountOneChild) { 642 int64 type_root = MakeServerNodeForType(test_user_share_.user_share(), 643 BOOKMARKS); 644 int64 parent = MakeFolderWithParent(test_user_share_.user_share(), 645 BOOKMARKS, 646 type_root, 647 NULL); 648 { 649 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 650 ReadNode type_root_node(&trans); 651 EXPECT_EQ(BaseNode::INIT_OK, 652 type_root_node.InitByIdLookup(type_root)); 653 EXPECT_EQ(2, type_root_node.GetTotalNodeCount()); 654 ReadNode parent_node(&trans); 655 EXPECT_EQ(BaseNode::INIT_OK, 656 parent_node.InitByIdLookup(parent)); 657 EXPECT_EQ(1, parent_node.GetTotalNodeCount()); 658 } 659 } 660 661 // Test counting nodes when there are multiple children beneath the type root, 662 // and one of those children has children of its own. 663 TEST_F(SyncApiTest, GetTotalNodeCountMultipleChildren) { 664 int64 type_root = MakeServerNodeForType(test_user_share_.user_share(), 665 BOOKMARKS); 666 int64 parent = MakeFolderWithParent(test_user_share_.user_share(), 667 BOOKMARKS, 668 type_root, 669 NULL); 670 ignore_result(MakeFolderWithParent(test_user_share_.user_share(), 671 BOOKMARKS, 672 type_root, 673 NULL)); 674 int64 child1 = MakeFolderWithParent( 675 test_user_share_.user_share(), 676 BOOKMARKS, 677 parent, 678 NULL); 679 ignore_result(MakeBookmarkWithParent( 680 test_user_share_.user_share(), 681 parent, 682 NULL)); 683 ignore_result(MakeBookmarkWithParent( 684 test_user_share_.user_share(), 685 child1, 686 NULL)); 687 688 { 689 ReadTransaction trans(FROM_HERE, test_user_share_.user_share()); 690 ReadNode type_root_node(&trans); 691 EXPECT_EQ(BaseNode::INIT_OK, 692 type_root_node.InitByIdLookup(type_root)); 693 EXPECT_EQ(6, type_root_node.GetTotalNodeCount()); 694 ReadNode node(&trans); 695 EXPECT_EQ(BaseNode::INIT_OK, 696 node.InitByIdLookup(parent)); 697 EXPECT_EQ(4, node.GetTotalNodeCount()); 698 } 699 } 700 701 namespace { 702 703 class TestHttpPostProviderInterface : public HttpPostProviderInterface { 704 public: 705 virtual ~TestHttpPostProviderInterface() {} 706 707 virtual void SetExtraRequestHeaders(const char* headers) OVERRIDE {} 708 virtual void SetURL(const char* url, int port) OVERRIDE {} 709 virtual void SetPostPayload(const char* content_type, 710 int content_length, 711 const char* content) OVERRIDE {} 712 virtual bool MakeSynchronousPost(int* error_code, int* response_code) 713 OVERRIDE { 714 return false; 715 } 716 virtual int GetResponseContentLength() const OVERRIDE { 717 return 0; 718 } 719 virtual const char* GetResponseContent() const OVERRIDE { 720 return ""; 721 } 722 virtual const std::string GetResponseHeaderValue( 723 const std::string& name) const OVERRIDE { 724 return std::string(); 725 } 726 virtual void Abort() OVERRIDE {} 727 }; 728 729 class TestHttpPostProviderFactory : public HttpPostProviderFactory { 730 public: 731 virtual ~TestHttpPostProviderFactory() {} 732 virtual HttpPostProviderInterface* Create() OVERRIDE { 733 return new TestHttpPostProviderInterface(); 734 } 735 virtual void Destroy(HttpPostProviderInterface* http) OVERRIDE { 736 delete static_cast<TestHttpPostProviderInterface*>(http); 737 } 738 }; 739 740 class SyncManagerObserverMock : public SyncManager::Observer { 741 public: 742 MOCK_METHOD1(OnSyncCycleCompleted, 743 void(const SyncSessionSnapshot&)); // NOLINT 744 MOCK_METHOD4(OnInitializationComplete, 745 void(const WeakHandle<JsBackend>&, 746 const WeakHandle<DataTypeDebugInfoListener>&, 747 bool, 748 syncer::ModelTypeSet)); // NOLINT 749 MOCK_METHOD1(OnConnectionStatusChange, void(ConnectionStatus)); // NOLINT 750 MOCK_METHOD0(OnStopSyncingPermanently, void()); // NOLINT 751 MOCK_METHOD1(OnUpdatedToken, void(const std::string&)); // NOLINT 752 MOCK_METHOD1(OnActionableError, 753 void(const SyncProtocolError&)); // NOLINT 754 }; 755 756 class SyncEncryptionHandlerObserverMock 757 : public SyncEncryptionHandler::Observer { 758 public: 759 MOCK_METHOD2(OnPassphraseRequired, 760 void(PassphraseRequiredReason, 761 const sync_pb::EncryptedData&)); // NOLINT 762 MOCK_METHOD0(OnPassphraseAccepted, void()); // NOLINT 763 MOCK_METHOD2(OnBootstrapTokenUpdated, 764 void(const std::string&, BootstrapTokenType type)); // NOLINT 765 MOCK_METHOD2(OnEncryptedTypesChanged, 766 void(ModelTypeSet, bool)); // NOLINT 767 MOCK_METHOD0(OnEncryptionComplete, void()); // NOLINT 768 MOCK_METHOD1(OnCryptographerStateChanged, void(Cryptographer*)); // NOLINT 769 MOCK_METHOD2(OnPassphraseTypeChanged, void(PassphraseType, 770 base::Time)); // NOLINT 771 }; 772 773 } // namespace 774 775 class SyncManagerTest : public testing::Test, 776 public SyncManager::ChangeDelegate { 777 protected: 778 enum NigoriStatus { 779 DONT_WRITE_NIGORI, 780 WRITE_TO_NIGORI 781 }; 782 783 enum EncryptionStatus { 784 UNINITIALIZED, 785 DEFAULT_ENCRYPTION, 786 FULL_ENCRYPTION 787 }; 788 789 SyncManagerTest() 790 : sync_manager_("Test sync manager") { 791 switches_.encryption_method = 792 InternalComponentsFactory::ENCRYPTION_KEYSTORE; 793 } 794 795 virtual ~SyncManagerTest() { 796 } 797 798 // Test implementation. 799 void SetUp() { 800 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 801 802 extensions_activity_ = new ExtensionsActivity(); 803 804 SyncCredentials credentials; 805 credentials.email = "foo (at) bar.com"; 806 credentials.sync_token = "sometoken"; 807 808 sync_manager_.AddObserver(&manager_observer_); 809 EXPECT_CALL(manager_observer_, OnInitializationComplete(_, _, _, _)). 810 WillOnce(SaveArg<0>(&js_backend_)); 811 812 EXPECT_FALSE(js_backend_.IsInitialized()); 813 814 std::vector<ModelSafeWorker*> workers; 815 ModelSafeRoutingInfo routing_info; 816 GetModelSafeRoutingInfo(&routing_info); 817 818 // Takes ownership of |fake_invalidator_|. 819 sync_manager_.Init( 820 temp_dir_.path(), 821 WeakHandle<JsEventHandler>(), 822 "bogus", 823 0, 824 false, 825 scoped_ptr<HttpPostProviderFactory>(new TestHttpPostProviderFactory()), 826 workers, 827 extensions_activity_.get(), 828 this, 829 credentials, 830 "fake_invalidator_client_id", 831 std::string(), 832 std::string(), // bootstrap tokens 833 scoped_ptr<InternalComponentsFactory>(GetFactory()).get(), 834 &encryptor_, 835 scoped_ptr<UnrecoverableErrorHandler>( 836 new TestUnrecoverableErrorHandler).Pass(), 837 NULL, 838 false); 839 840 sync_manager_.GetEncryptionHandler()->AddObserver(&encryption_observer_); 841 842 EXPECT_TRUE(js_backend_.IsInitialized()); 843 844 for (ModelSafeRoutingInfo::iterator i = routing_info.begin(); 845 i != routing_info.end(); ++i) { 846 type_roots_[i->first] = MakeServerNodeForType( 847 sync_manager_.GetUserShare(), i->first); 848 } 849 PumpLoop(); 850 } 851 852 void TearDown() { 853 sync_manager_.RemoveObserver(&manager_observer_); 854 sync_manager_.ShutdownOnSyncThread(); 855 PumpLoop(); 856 } 857 858 void GetModelSafeRoutingInfo(ModelSafeRoutingInfo* out) { 859 (*out)[NIGORI] = GROUP_PASSIVE; 860 (*out)[DEVICE_INFO] = GROUP_PASSIVE; 861 (*out)[EXPERIMENTS] = GROUP_PASSIVE; 862 (*out)[BOOKMARKS] = GROUP_PASSIVE; 863 (*out)[THEMES] = GROUP_PASSIVE; 864 (*out)[SESSIONS] = GROUP_PASSIVE; 865 (*out)[PASSWORDS] = GROUP_PASSIVE; 866 (*out)[PREFERENCES] = GROUP_PASSIVE; 867 (*out)[PRIORITY_PREFERENCES] = GROUP_PASSIVE; 868 } 869 870 virtual void OnChangesApplied( 871 ModelType model_type, 872 int64 model_version, 873 const BaseTransaction* trans, 874 const ImmutableChangeRecordList& changes) OVERRIDE {} 875 876 virtual void OnChangesComplete(ModelType model_type) OVERRIDE {} 877 878 // Helper methods. 879 bool SetUpEncryption(NigoriStatus nigori_status, 880 EncryptionStatus encryption_status) { 881 UserShare* share = sync_manager_.GetUserShare(); 882 883 // We need to create the nigori node as if it were an applied server update. 884 int64 nigori_id = GetIdForDataType(NIGORI); 885 if (nigori_id == kInvalidId) 886 return false; 887 888 // Set the nigori cryptographer information. 889 if (encryption_status == FULL_ENCRYPTION) 890 sync_manager_.GetEncryptionHandler()->EnableEncryptEverything(); 891 892 WriteTransaction trans(FROM_HERE, share); 893 Cryptographer* cryptographer = trans.GetCryptographer(); 894 if (!cryptographer) 895 return false; 896 if (encryption_status != UNINITIALIZED) { 897 KeyParams params = {"localhost", "dummy", "foobar"}; 898 cryptographer->AddKey(params); 899 } else { 900 DCHECK_NE(nigori_status, WRITE_TO_NIGORI); 901 } 902 if (nigori_status == WRITE_TO_NIGORI) { 903 sync_pb::NigoriSpecifics nigori; 904 cryptographer->GetKeys(nigori.mutable_encryption_keybag()); 905 share->directory->GetNigoriHandler()->UpdateNigoriFromEncryptedTypes( 906 &nigori, 907 trans.GetWrappedTrans()); 908 WriteNode node(&trans); 909 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(nigori_id)); 910 node.SetNigoriSpecifics(nigori); 911 } 912 return cryptographer->is_ready(); 913 } 914 915 int64 GetIdForDataType(ModelType type) { 916 if (type_roots_.count(type) == 0) 917 return 0; 918 return type_roots_[type]; 919 } 920 921 void PumpLoop() { 922 message_loop_.RunUntilIdle(); 923 } 924 925 void SendJsMessage(const std::string& name, const JsArgList& args, 926 const WeakHandle<JsReplyHandler>& reply_handler) { 927 js_backend_.Call(FROM_HERE, &JsBackend::ProcessJsMessage, 928 name, args, reply_handler); 929 PumpLoop(); 930 } 931 932 void SetJsEventHandler(const WeakHandle<JsEventHandler>& event_handler) { 933 js_backend_.Call(FROM_HERE, &JsBackend::SetJsEventHandler, 934 event_handler); 935 PumpLoop(); 936 } 937 938 // Looks up an entry by client tag and resets IS_UNSYNCED value to false. 939 // Returns true if entry was previously unsynced, false if IS_UNSYNCED was 940 // already false. 941 bool ResetUnsyncedEntry(ModelType type, 942 const std::string& client_tag) { 943 UserShare* share = sync_manager_.GetUserShare(); 944 syncable::WriteTransaction trans( 945 FROM_HERE, syncable::UNITTEST, share->directory.get()); 946 const std::string hash = syncable::GenerateSyncableHash(type, client_tag); 947 syncable::MutableEntry entry(&trans, syncable::GET_BY_CLIENT_TAG, 948 hash); 949 EXPECT_TRUE(entry.good()); 950 if (!entry.Get(IS_UNSYNCED)) 951 return false; 952 entry.Put(IS_UNSYNCED, false); 953 return true; 954 } 955 956 virtual InternalComponentsFactory* GetFactory() { 957 return new TestInternalComponentsFactory(GetSwitches(), STORAGE_IN_MEMORY); 958 } 959 960 // Returns true if we are currently encrypting all sync data. May 961 // be called on any thread. 962 bool EncryptEverythingEnabledForTest() { 963 return sync_manager_.GetEncryptionHandler()->EncryptEverythingEnabled(); 964 } 965 966 // Gets the set of encrypted types from the cryptographer 967 // Note: opens a transaction. May be called from any thread. 968 ModelTypeSet GetEncryptedTypes() { 969 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 970 return GetEncryptedTypesWithTrans(&trans); 971 } 972 973 ModelTypeSet GetEncryptedTypesWithTrans(BaseTransaction* trans) { 974 return trans->GetDirectory()->GetNigoriHandler()-> 975 GetEncryptedTypes(trans->GetWrappedTrans()); 976 } 977 978 void SimulateInvalidatorStateChangeForTest(InvalidatorState state) { 979 DCHECK(sync_manager_.thread_checker_.CalledOnValidThread()); 980 sync_manager_.OnInvalidatorStateChange(state); 981 } 982 983 void TriggerOnIncomingNotificationForTest(ModelTypeSet model_types) { 984 DCHECK(sync_manager_.thread_checker_.CalledOnValidThread()); 985 ModelTypeInvalidationMap invalidation_map = 986 ModelTypeSetToInvalidationMap(model_types, std::string()); 987 sync_manager_.OnIncomingInvalidation( 988 ModelTypeInvalidationMapToObjectIdInvalidationMap( 989 invalidation_map)); 990 } 991 992 void SetProgressMarkerForType(ModelType type, bool set) { 993 if (set) { 994 sync_pb::DataTypeProgressMarker marker; 995 marker.set_token("token"); 996 marker.set_data_type_id(GetSpecificsFieldNumberFromModelType(type)); 997 sync_manager_.directory()->SetDownloadProgress(type, marker); 998 } else { 999 sync_pb::DataTypeProgressMarker marker; 1000 sync_manager_.directory()->SetDownloadProgress(type, marker); 1001 } 1002 } 1003 1004 InternalComponentsFactory::Switches GetSwitches() const { 1005 return switches_; 1006 } 1007 1008 private: 1009 // Needed by |sync_manager_|. 1010 base::MessageLoop message_loop_; 1011 // Needed by |sync_manager_|. 1012 base::ScopedTempDir temp_dir_; 1013 // Sync Id's for the roots of the enabled datatypes. 1014 std::map<ModelType, int64> type_roots_; 1015 scoped_refptr<ExtensionsActivity> extensions_activity_; 1016 1017 protected: 1018 FakeEncryptor encryptor_; 1019 SyncManagerImpl sync_manager_; 1020 WeakHandle<JsBackend> js_backend_; 1021 StrictMock<SyncManagerObserverMock> manager_observer_; 1022 StrictMock<SyncEncryptionHandlerObserverMock> encryption_observer_; 1023 InternalComponentsFactory::Switches switches_; 1024 }; 1025 1026 TEST_F(SyncManagerTest, ProcessJsMessage) { 1027 const JsArgList kNoArgs; 1028 1029 StrictMock<MockJsReplyHandler> reply_handler; 1030 1031 base::ListValue disabled_args; 1032 disabled_args.Append(new base::StringValue("TRANSIENT_INVALIDATION_ERROR")); 1033 1034 EXPECT_CALL(reply_handler, 1035 HandleJsReply("getNotificationState", 1036 HasArgsAsList(disabled_args))); 1037 1038 // This message should be dropped. 1039 SendJsMessage("unknownMessage", kNoArgs, reply_handler.AsWeakHandle()); 1040 1041 SendJsMessage("getNotificationState", kNoArgs, reply_handler.AsWeakHandle()); 1042 } 1043 1044 TEST_F(SyncManagerTest, ProcessJsMessageGetRootNodeDetails) { 1045 const JsArgList kNoArgs; 1046 1047 StrictMock<MockJsReplyHandler> reply_handler; 1048 1049 JsArgList return_args; 1050 1051 EXPECT_CALL(reply_handler, 1052 HandleJsReply("getRootNodeDetails", _)) 1053 .WillOnce(SaveArg<1>(&return_args)); 1054 1055 SendJsMessage("getRootNodeDetails", kNoArgs, reply_handler.AsWeakHandle()); 1056 1057 EXPECT_EQ(1u, return_args.Get().GetSize()); 1058 const base::DictionaryValue* node_info = NULL; 1059 EXPECT_TRUE(return_args.Get().GetDictionary(0, &node_info)); 1060 if (node_info) { 1061 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1062 ReadNode node(&trans); 1063 node.InitByRootLookup(); 1064 CheckNodeValue(node, *node_info, true); 1065 } else { 1066 ADD_FAILURE(); 1067 } 1068 } 1069 1070 void CheckGetNodesByIdReturnArgs(SyncManager* sync_manager, 1071 const JsArgList& return_args, 1072 int64 id, 1073 bool is_detailed) { 1074 EXPECT_EQ(1u, return_args.Get().GetSize()); 1075 const base::ListValue* nodes = NULL; 1076 ASSERT_TRUE(return_args.Get().GetList(0, &nodes)); 1077 ASSERT_TRUE(nodes); 1078 EXPECT_EQ(1u, nodes->GetSize()); 1079 const base::DictionaryValue* node_info = NULL; 1080 EXPECT_TRUE(nodes->GetDictionary(0, &node_info)); 1081 ASSERT_TRUE(node_info); 1082 ReadTransaction trans(FROM_HERE, sync_manager->GetUserShare()); 1083 ReadNode node(&trans); 1084 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(id)); 1085 CheckNodeValue(node, *node_info, is_detailed); 1086 } 1087 1088 class SyncManagerGetNodesByIdTest : public SyncManagerTest { 1089 protected: 1090 virtual ~SyncManagerGetNodesByIdTest() {} 1091 1092 void RunGetNodesByIdTest(const char* message_name, bool is_detailed) { 1093 int64 root_id = kInvalidId; 1094 { 1095 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1096 ReadNode root_node(&trans); 1097 root_node.InitByRootLookup(); 1098 root_id = root_node.GetId(); 1099 } 1100 1101 int64 child_id = 1102 MakeNode(sync_manager_.GetUserShare(), BOOKMARKS, "testtag"); 1103 1104 StrictMock<MockJsReplyHandler> reply_handler; 1105 1106 JsArgList return_args; 1107 1108 const int64 ids[] = { root_id, child_id }; 1109 1110 EXPECT_CALL(reply_handler, 1111 HandleJsReply(message_name, _)) 1112 .Times(arraysize(ids)).WillRepeatedly(SaveArg<1>(&return_args)); 1113 1114 for (size_t i = 0; i < arraysize(ids); ++i) { 1115 base::ListValue args; 1116 base::ListValue* id_values = new base::ListValue(); 1117 args.Append(id_values); 1118 id_values->Append(new base::StringValue(base::Int64ToString(ids[i]))); 1119 SendJsMessage(message_name, 1120 JsArgList(&args), reply_handler.AsWeakHandle()); 1121 1122 CheckGetNodesByIdReturnArgs(&sync_manager_, return_args, 1123 ids[i], is_detailed); 1124 } 1125 } 1126 1127 void RunGetNodesByIdFailureTest(const char* message_name) { 1128 StrictMock<MockJsReplyHandler> reply_handler; 1129 1130 base::ListValue empty_list_args; 1131 empty_list_args.Append(new base::ListValue()); 1132 1133 EXPECT_CALL(reply_handler, 1134 HandleJsReply(message_name, 1135 HasArgsAsList(empty_list_args))) 1136 .Times(6); 1137 1138 { 1139 base::ListValue args; 1140 SendJsMessage(message_name, 1141 JsArgList(&args), reply_handler.AsWeakHandle()); 1142 } 1143 1144 { 1145 base::ListValue args; 1146 args.Append(new base::ListValue()); 1147 SendJsMessage(message_name, 1148 JsArgList(&args), reply_handler.AsWeakHandle()); 1149 } 1150 1151 { 1152 base::ListValue args; 1153 base::ListValue* ids = new base::ListValue(); 1154 args.Append(ids); 1155 ids->Append(new base::StringValue(std::string())); 1156 SendJsMessage( 1157 message_name, JsArgList(&args), reply_handler.AsWeakHandle()); 1158 } 1159 1160 { 1161 base::ListValue args; 1162 base::ListValue* ids = new base::ListValue(); 1163 args.Append(ids); 1164 ids->Append(new base::StringValue("nonsense")); 1165 SendJsMessage(message_name, 1166 JsArgList(&args), reply_handler.AsWeakHandle()); 1167 } 1168 1169 { 1170 base::ListValue args; 1171 base::ListValue* ids = new base::ListValue(); 1172 args.Append(ids); 1173 ids->Append(new base::StringValue("0")); 1174 SendJsMessage(message_name, 1175 JsArgList(&args), reply_handler.AsWeakHandle()); 1176 } 1177 1178 { 1179 base::ListValue args; 1180 base::ListValue* ids = new base::ListValue(); 1181 args.Append(ids); 1182 ids->Append(new base::StringValue("9999")); 1183 SendJsMessage(message_name, 1184 JsArgList(&args), reply_handler.AsWeakHandle()); 1185 } 1186 } 1187 }; 1188 1189 TEST_F(SyncManagerGetNodesByIdTest, GetNodeSummariesById) { 1190 RunGetNodesByIdTest("getNodeSummariesById", false); 1191 } 1192 1193 TEST_F(SyncManagerGetNodesByIdTest, GetNodeDetailsById) { 1194 RunGetNodesByIdTest("getNodeDetailsById", true); 1195 } 1196 1197 TEST_F(SyncManagerGetNodesByIdTest, GetNodeSummariesByIdFailure) { 1198 RunGetNodesByIdFailureTest("getNodeSummariesById"); 1199 } 1200 1201 TEST_F(SyncManagerGetNodesByIdTest, GetNodeDetailsByIdFailure) { 1202 RunGetNodesByIdFailureTest("getNodeDetailsById"); 1203 } 1204 1205 TEST_F(SyncManagerTest, GetChildNodeIds) { 1206 StrictMock<MockJsReplyHandler> reply_handler; 1207 1208 JsArgList return_args; 1209 1210 EXPECT_CALL(reply_handler, 1211 HandleJsReply("getChildNodeIds", _)) 1212 .Times(1).WillRepeatedly(SaveArg<1>(&return_args)); 1213 1214 { 1215 base::ListValue args; 1216 args.Append(new base::StringValue("1")); 1217 SendJsMessage("getChildNodeIds", 1218 JsArgList(&args), reply_handler.AsWeakHandle()); 1219 } 1220 1221 EXPECT_EQ(1u, return_args.Get().GetSize()); 1222 const base::ListValue* nodes = NULL; 1223 ASSERT_TRUE(return_args.Get().GetList(0, &nodes)); 1224 ASSERT_TRUE(nodes); 1225 EXPECT_EQ(9u, nodes->GetSize()); 1226 } 1227 1228 TEST_F(SyncManagerTest, GetChildNodeIdsFailure) { 1229 StrictMock<MockJsReplyHandler> reply_handler; 1230 1231 base::ListValue empty_list_args; 1232 empty_list_args.Append(new base::ListValue()); 1233 1234 EXPECT_CALL(reply_handler, 1235 HandleJsReply("getChildNodeIds", 1236 HasArgsAsList(empty_list_args))) 1237 .Times(5); 1238 1239 { 1240 base::ListValue args; 1241 SendJsMessage("getChildNodeIds", 1242 JsArgList(&args), reply_handler.AsWeakHandle()); 1243 } 1244 1245 { 1246 base::ListValue args; 1247 args.Append(new base::StringValue(std::string())); 1248 SendJsMessage( 1249 "getChildNodeIds", JsArgList(&args), reply_handler.AsWeakHandle()); 1250 } 1251 1252 { 1253 base::ListValue args; 1254 args.Append(new base::StringValue("nonsense")); 1255 SendJsMessage("getChildNodeIds", 1256 JsArgList(&args), reply_handler.AsWeakHandle()); 1257 } 1258 1259 { 1260 base::ListValue args; 1261 args.Append(new base::StringValue("0")); 1262 SendJsMessage("getChildNodeIds", 1263 JsArgList(&args), reply_handler.AsWeakHandle()); 1264 } 1265 1266 { 1267 base::ListValue args; 1268 args.Append(new base::StringValue("9999")); 1269 SendJsMessage("getChildNodeIds", 1270 JsArgList(&args), reply_handler.AsWeakHandle()); 1271 } 1272 } 1273 1274 TEST_F(SyncManagerTest, GetAllNodesTest) { 1275 StrictMock<MockJsReplyHandler> reply_handler; 1276 JsArgList return_args; 1277 1278 EXPECT_CALL(reply_handler, 1279 HandleJsReply("getAllNodes", _)) 1280 .Times(1).WillRepeatedly(SaveArg<1>(&return_args)); 1281 1282 { 1283 base::ListValue args; 1284 SendJsMessage("getAllNodes", 1285 JsArgList(&args), reply_handler.AsWeakHandle()); 1286 } 1287 1288 // There's not much value in verifying every attribute on every node here. 1289 // Most of the value of this test has already been achieved: we've verified we 1290 // can call the above function without crashing or leaking memory. 1291 // 1292 // Let's just check the list size and a few of its elements. Anything more 1293 // would make this test brittle without greatly increasing our chances of 1294 // catching real bugs. 1295 1296 const base::ListValue* node_list; 1297 const base::DictionaryValue* first_result; 1298 1299 // The resulting argument list should have one argument, a list of nodes. 1300 ASSERT_EQ(1U, return_args.Get().GetSize()); 1301 ASSERT_TRUE(return_args.Get().GetList(0, &node_list)); 1302 1303 // The database creation logic depends on the routing info. 1304 // Refer to setup methods for more information. 1305 ModelSafeRoutingInfo routes; 1306 GetModelSafeRoutingInfo(&routes); 1307 size_t directory_size = routes.size() + 1; 1308 1309 ASSERT_EQ(directory_size, node_list->GetSize()); 1310 ASSERT_TRUE(node_list->GetDictionary(0, &first_result)); 1311 EXPECT_TRUE(first_result->HasKey("ID")); 1312 EXPECT_TRUE(first_result->HasKey("NON_UNIQUE_NAME")); 1313 } 1314 1315 // Simulate various invalidator state changes. Those should propagate 1316 // JS events. 1317 TEST_F(SyncManagerTest, OnInvalidatorStateChangeJsEvents) { 1318 StrictMock<MockJsEventHandler> event_handler; 1319 1320 base::DictionaryValue enabled_details; 1321 enabled_details.SetString("state", "INVALIDATIONS_ENABLED"); 1322 base::DictionaryValue credentials_rejected_details; 1323 credentials_rejected_details.SetString( 1324 "state", "INVALIDATION_CREDENTIALS_REJECTED"); 1325 base::DictionaryValue transient_error_details; 1326 transient_error_details.SetString("state", "TRANSIENT_INVALIDATION_ERROR"); 1327 base::DictionaryValue auth_error_details; 1328 auth_error_details.SetString("status", "CONNECTION_AUTH_ERROR"); 1329 1330 EXPECT_CALL(event_handler, 1331 HandleJsEvent("onNotificationStateChange", 1332 HasDetailsAsDictionary(enabled_details))); 1333 1334 EXPECT_CALL( 1335 event_handler, 1336 HandleJsEvent("onNotificationStateChange", 1337 HasDetailsAsDictionary(credentials_rejected_details))) 1338 .Times(2); 1339 1340 EXPECT_CALL(event_handler, 1341 HandleJsEvent("onNotificationStateChange", 1342 HasDetailsAsDictionary(transient_error_details))); 1343 1344 // Test needs to simulate INVALIDATION_CREDENTIALS_REJECTED with event handler 1345 // attached because this is the only time when CONNECTION_AUTH_ERROR 1346 // notification will be generated, therefore the only chance to verify that 1347 // "onConnectionStatusChange" event is delivered 1348 SetJsEventHandler(event_handler.AsWeakHandle()); 1349 SimulateInvalidatorStateChangeForTest(INVALIDATION_CREDENTIALS_REJECTED); 1350 SetJsEventHandler(WeakHandle<JsEventHandler>()); 1351 1352 SimulateInvalidatorStateChangeForTest(INVALIDATIONS_ENABLED); 1353 SimulateInvalidatorStateChangeForTest(INVALIDATION_CREDENTIALS_REJECTED); 1354 SimulateInvalidatorStateChangeForTest(TRANSIENT_INVALIDATION_ERROR); 1355 1356 SetJsEventHandler(event_handler.AsWeakHandle()); 1357 SimulateInvalidatorStateChangeForTest(INVALIDATIONS_ENABLED); 1358 SimulateInvalidatorStateChangeForTest(INVALIDATION_CREDENTIALS_REJECTED); 1359 SimulateInvalidatorStateChangeForTest(TRANSIENT_INVALIDATION_ERROR); 1360 SetJsEventHandler(WeakHandle<JsEventHandler>()); 1361 1362 SimulateInvalidatorStateChangeForTest(INVALIDATIONS_ENABLED); 1363 SimulateInvalidatorStateChangeForTest(INVALIDATION_CREDENTIALS_REJECTED); 1364 SimulateInvalidatorStateChangeForTest(TRANSIENT_INVALIDATION_ERROR); 1365 1366 // Should trigger the replies. 1367 PumpLoop(); 1368 } 1369 1370 TEST_F(SyncManagerTest, OnIncomingNotification) { 1371 StrictMock<MockJsEventHandler> event_handler; 1372 1373 const ModelTypeSet empty_model_types; 1374 const ModelTypeSet model_types( 1375 BOOKMARKS, THEMES); 1376 1377 // Build expected_args to have a single argument with the string 1378 // equivalents of model_types. 1379 base::DictionaryValue expected_details; 1380 { 1381 base::ListValue* model_type_list = new base::ListValue(); 1382 expected_details.SetString("source", "REMOTE_INVALIDATION"); 1383 expected_details.Set("changedTypes", model_type_list); 1384 for (ModelTypeSet::Iterator it = model_types.First(); 1385 it.Good(); it.Inc()) { 1386 model_type_list->Append( 1387 new base::StringValue(ModelTypeToString(it.Get()))); 1388 } 1389 } 1390 1391 EXPECT_CALL(event_handler, 1392 HandleJsEvent("onIncomingNotification", 1393 HasDetailsAsDictionary(expected_details))); 1394 1395 TriggerOnIncomingNotificationForTest(empty_model_types); 1396 TriggerOnIncomingNotificationForTest(model_types); 1397 1398 SetJsEventHandler(event_handler.AsWeakHandle()); 1399 TriggerOnIncomingNotificationForTest(model_types); 1400 SetJsEventHandler(WeakHandle<JsEventHandler>()); 1401 1402 TriggerOnIncomingNotificationForTest(empty_model_types); 1403 TriggerOnIncomingNotificationForTest(model_types); 1404 1405 // Should trigger the replies. 1406 PumpLoop(); 1407 } 1408 1409 TEST_F(SyncManagerTest, RefreshEncryptionReady) { 1410 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 1411 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1412 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1413 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, false)); 1414 1415 sync_manager_.GetEncryptionHandler()->Init(); 1416 PumpLoop(); 1417 1418 const ModelTypeSet encrypted_types = GetEncryptedTypes(); 1419 EXPECT_TRUE(encrypted_types.Has(PASSWORDS)); 1420 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1421 1422 { 1423 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1424 ReadNode node(&trans); 1425 EXPECT_EQ(BaseNode::INIT_OK, 1426 node.InitByIdLookup(GetIdForDataType(NIGORI))); 1427 sync_pb::NigoriSpecifics nigori = node.GetNigoriSpecifics(); 1428 EXPECT_TRUE(nigori.has_encryption_keybag()); 1429 Cryptographer* cryptographer = trans.GetCryptographer(); 1430 EXPECT_TRUE(cryptographer->is_ready()); 1431 EXPECT_TRUE(cryptographer->CanDecrypt(nigori.encryption_keybag())); 1432 } 1433 } 1434 1435 // Attempt to refresh encryption when nigori not downloaded. 1436 TEST_F(SyncManagerTest, RefreshEncryptionNotReady) { 1437 // Don't set up encryption (no nigori node created). 1438 1439 // Should fail. Triggers an OnPassphraseRequired because the cryptographer 1440 // is not ready. 1441 EXPECT_CALL(encryption_observer_, OnPassphraseRequired(_, _)).Times(1); 1442 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1443 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, false)); 1444 sync_manager_.GetEncryptionHandler()->Init(); 1445 PumpLoop(); 1446 1447 const ModelTypeSet encrypted_types = GetEncryptedTypes(); 1448 EXPECT_TRUE(encrypted_types.Has(PASSWORDS)); // Hardcoded. 1449 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1450 } 1451 1452 // Attempt to refresh encryption when nigori is empty. 1453 TEST_F(SyncManagerTest, RefreshEncryptionEmptyNigori) { 1454 EXPECT_TRUE(SetUpEncryption(DONT_WRITE_NIGORI, DEFAULT_ENCRYPTION)); 1455 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()).Times(1); 1456 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1457 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, false)); 1458 1459 // Should write to nigori. 1460 sync_manager_.GetEncryptionHandler()->Init(); 1461 PumpLoop(); 1462 1463 const ModelTypeSet encrypted_types = GetEncryptedTypes(); 1464 EXPECT_TRUE(encrypted_types.Has(PASSWORDS)); // Hardcoded. 1465 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1466 1467 { 1468 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1469 ReadNode node(&trans); 1470 EXPECT_EQ(BaseNode::INIT_OK, 1471 node.InitByIdLookup(GetIdForDataType(NIGORI))); 1472 sync_pb::NigoriSpecifics nigori = node.GetNigoriSpecifics(); 1473 EXPECT_TRUE(nigori.has_encryption_keybag()); 1474 Cryptographer* cryptographer = trans.GetCryptographer(); 1475 EXPECT_TRUE(cryptographer->is_ready()); 1476 EXPECT_TRUE(cryptographer->CanDecrypt(nigori.encryption_keybag())); 1477 } 1478 } 1479 1480 TEST_F(SyncManagerTest, EncryptDataTypesWithNoData) { 1481 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 1482 EXPECT_CALL(encryption_observer_, 1483 OnEncryptedTypesChanged( 1484 HasModelTypes(EncryptableUserTypes()), true)); 1485 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1486 sync_manager_.GetEncryptionHandler()->EnableEncryptEverything(); 1487 EXPECT_TRUE(EncryptEverythingEnabledForTest()); 1488 } 1489 1490 TEST_F(SyncManagerTest, EncryptDataTypesWithData) { 1491 size_t batch_size = 5; 1492 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 1493 1494 // Create some unencrypted unsynced data. 1495 int64 folder = MakeFolderWithParent(sync_manager_.GetUserShare(), 1496 BOOKMARKS, 1497 GetIdForDataType(BOOKMARKS), 1498 NULL); 1499 // First batch_size nodes are children of folder. 1500 size_t i; 1501 for (i = 0; i < batch_size; ++i) { 1502 MakeBookmarkWithParent(sync_manager_.GetUserShare(), folder, NULL); 1503 } 1504 // Next batch_size nodes are a different type and on their own. 1505 for (; i < 2*batch_size; ++i) { 1506 MakeNode(sync_manager_.GetUserShare(), SESSIONS, 1507 base::StringPrintf("%" PRIuS "", i)); 1508 } 1509 // Last batch_size nodes are a third type that will not need encryption. 1510 for (; i < 3*batch_size; ++i) { 1511 MakeNode(sync_manager_.GetUserShare(), THEMES, 1512 base::StringPrintf("%" PRIuS "", i)); 1513 } 1514 1515 { 1516 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1517 EXPECT_TRUE(GetEncryptedTypesWithTrans(&trans).Equals( 1518 SyncEncryptionHandler::SensitiveTypes())); 1519 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 1520 trans.GetWrappedTrans(), 1521 BOOKMARKS, 1522 false /* not encrypted */)); 1523 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 1524 trans.GetWrappedTrans(), 1525 SESSIONS, 1526 false /* not encrypted */)); 1527 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 1528 trans.GetWrappedTrans(), 1529 THEMES, 1530 false /* not encrypted */)); 1531 } 1532 1533 EXPECT_CALL(encryption_observer_, 1534 OnEncryptedTypesChanged( 1535 HasModelTypes(EncryptableUserTypes()), true)); 1536 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1537 sync_manager_.GetEncryptionHandler()->EnableEncryptEverything(); 1538 EXPECT_TRUE(EncryptEverythingEnabledForTest()); 1539 { 1540 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1541 EXPECT_TRUE(GetEncryptedTypesWithTrans(&trans).Equals( 1542 EncryptableUserTypes())); 1543 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 1544 trans.GetWrappedTrans(), 1545 BOOKMARKS, 1546 true /* is encrypted */)); 1547 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 1548 trans.GetWrappedTrans(), 1549 SESSIONS, 1550 true /* is encrypted */)); 1551 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 1552 trans.GetWrappedTrans(), 1553 THEMES, 1554 true /* is encrypted */)); 1555 } 1556 1557 // Trigger's a ReEncryptEverything with new passphrase. 1558 testing::Mock::VerifyAndClearExpectations(&encryption_observer_); 1559 EXPECT_CALL(encryption_observer_, 1560 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 1561 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 1562 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1563 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1564 EXPECT_CALL(encryption_observer_, 1565 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); 1566 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase( 1567 "new_passphrase", true); 1568 EXPECT_TRUE(EncryptEverythingEnabledForTest()); 1569 { 1570 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1571 EXPECT_TRUE(GetEncryptedTypesWithTrans(&trans).Equals( 1572 EncryptableUserTypes())); 1573 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 1574 trans.GetWrappedTrans(), 1575 BOOKMARKS, 1576 true /* is encrypted */)); 1577 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 1578 trans.GetWrappedTrans(), 1579 SESSIONS, 1580 true /* is encrypted */)); 1581 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 1582 trans.GetWrappedTrans(), 1583 THEMES, 1584 true /* is encrypted */)); 1585 } 1586 // Calling EncryptDataTypes with an empty encrypted types should not trigger 1587 // a reencryption and should just notify immediately. 1588 testing::Mock::VerifyAndClearExpectations(&encryption_observer_); 1589 EXPECT_CALL(encryption_observer_, 1590 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)).Times(0); 1591 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()).Times(0); 1592 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()).Times(0); 1593 sync_manager_.GetEncryptionHandler()->EnableEncryptEverything(); 1594 } 1595 1596 // Test that when there are no pending keys and the cryptographer is not 1597 // initialized, we add a key based on the current GAIA password. 1598 // (case 1 in SyncManager::SyncInternal::SetEncryptionPassphrase) 1599 TEST_F(SyncManagerTest, SetInitialGaiaPass) { 1600 EXPECT_FALSE(SetUpEncryption(DONT_WRITE_NIGORI, UNINITIALIZED)); 1601 EXPECT_CALL(encryption_observer_, 1602 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 1603 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 1604 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1605 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1606 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase( 1607 "new_passphrase", 1608 false); 1609 EXPECT_EQ(IMPLICIT_PASSPHRASE, 1610 sync_manager_.GetEncryptionHandler()->GetPassphraseType()); 1611 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1612 { 1613 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1614 ReadNode node(&trans); 1615 EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag)); 1616 sync_pb::NigoriSpecifics nigori = node.GetNigoriSpecifics(); 1617 Cryptographer* cryptographer = trans.GetCryptographer(); 1618 EXPECT_TRUE(cryptographer->is_ready()); 1619 EXPECT_TRUE(cryptographer->CanDecrypt(nigori.encryption_keybag())); 1620 } 1621 } 1622 1623 // Test that when there are no pending keys and we have on the old GAIA 1624 // password, we update and re-encrypt everything with the new GAIA password. 1625 // (case 1 in SyncManager::SyncInternal::SetEncryptionPassphrase) 1626 TEST_F(SyncManagerTest, UpdateGaiaPass) { 1627 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 1628 Cryptographer verifier(&encryptor_); 1629 { 1630 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1631 Cryptographer* cryptographer = trans.GetCryptographer(); 1632 std::string bootstrap_token; 1633 cryptographer->GetBootstrapToken(&bootstrap_token); 1634 verifier.Bootstrap(bootstrap_token); 1635 } 1636 EXPECT_CALL(encryption_observer_, 1637 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 1638 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 1639 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1640 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1641 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase( 1642 "new_passphrase", 1643 false); 1644 EXPECT_EQ(IMPLICIT_PASSPHRASE, 1645 sync_manager_.GetEncryptionHandler()->GetPassphraseType()); 1646 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1647 { 1648 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1649 Cryptographer* cryptographer = trans.GetCryptographer(); 1650 EXPECT_TRUE(cryptographer->is_ready()); 1651 // Verify the default key has changed. 1652 sync_pb::EncryptedData encrypted; 1653 cryptographer->GetKeys(&encrypted); 1654 EXPECT_FALSE(verifier.CanDecrypt(encrypted)); 1655 } 1656 } 1657 1658 // Sets a new explicit passphrase. This should update the bootstrap token 1659 // and re-encrypt everything. 1660 // (case 2 in SyncManager::SyncInternal::SetEncryptionPassphrase) 1661 TEST_F(SyncManagerTest, SetPassphraseWithPassword) { 1662 Cryptographer verifier(&encryptor_); 1663 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 1664 { 1665 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1666 // Store the default (soon to be old) key. 1667 Cryptographer* cryptographer = trans.GetCryptographer(); 1668 std::string bootstrap_token; 1669 cryptographer->GetBootstrapToken(&bootstrap_token); 1670 verifier.Bootstrap(bootstrap_token); 1671 1672 ReadNode root_node(&trans); 1673 root_node.InitByRootLookup(); 1674 1675 WriteNode password_node(&trans); 1676 WriteNode::InitUniqueByCreationResult result = 1677 password_node.InitUniqueByCreation(PASSWORDS, 1678 root_node, "foo"); 1679 EXPECT_EQ(WriteNode::INIT_SUCCESS, result); 1680 sync_pb::PasswordSpecificsData data; 1681 data.set_password_value("secret"); 1682 password_node.SetPasswordSpecifics(data); 1683 } 1684 EXPECT_CALL(encryption_observer_, 1685 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 1686 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 1687 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1688 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1689 EXPECT_CALL(encryption_observer_, 1690 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); 1691 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase( 1692 "new_passphrase", 1693 true); 1694 EXPECT_EQ(CUSTOM_PASSPHRASE, 1695 sync_manager_.GetEncryptionHandler()->GetPassphraseType()); 1696 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1697 { 1698 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1699 Cryptographer* cryptographer = trans.GetCryptographer(); 1700 EXPECT_TRUE(cryptographer->is_ready()); 1701 // Verify the default key has changed. 1702 sync_pb::EncryptedData encrypted; 1703 cryptographer->GetKeys(&encrypted); 1704 EXPECT_FALSE(verifier.CanDecrypt(encrypted)); 1705 1706 ReadNode password_node(&trans); 1707 EXPECT_EQ(BaseNode::INIT_OK, 1708 password_node.InitByClientTagLookup(PASSWORDS, 1709 "foo")); 1710 const sync_pb::PasswordSpecificsData& data = 1711 password_node.GetPasswordSpecifics(); 1712 EXPECT_EQ("secret", data.password_value()); 1713 } 1714 } 1715 1716 // Manually set the pending keys in the cryptographer/nigori to reflect the data 1717 // being encrypted with a new (unprovided) GAIA password, then supply the 1718 // password. 1719 // (case 7 in SyncManager::SyncInternal::SetDecryptionPassphrase) 1720 TEST_F(SyncManagerTest, SupplyPendingGAIAPass) { 1721 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 1722 Cryptographer other_cryptographer(&encryptor_); 1723 { 1724 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1725 Cryptographer* cryptographer = trans.GetCryptographer(); 1726 std::string bootstrap_token; 1727 cryptographer->GetBootstrapToken(&bootstrap_token); 1728 other_cryptographer.Bootstrap(bootstrap_token); 1729 1730 // Now update the nigori to reflect the new keys, and update the 1731 // cryptographer to have pending keys. 1732 KeyParams params = {"localhost", "dummy", "passphrase2"}; 1733 other_cryptographer.AddKey(params); 1734 WriteNode node(&trans); 1735 EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag)); 1736 sync_pb::NigoriSpecifics nigori; 1737 other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); 1738 cryptographer->SetPendingKeys(nigori.encryption_keybag()); 1739 EXPECT_TRUE(cryptographer->has_pending_keys()); 1740 node.SetNigoriSpecifics(nigori); 1741 } 1742 EXPECT_CALL(encryption_observer_, 1743 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 1744 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 1745 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1746 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1747 sync_manager_.GetEncryptionHandler()->SetDecryptionPassphrase("passphrase2"); 1748 EXPECT_EQ(IMPLICIT_PASSPHRASE, 1749 sync_manager_.GetEncryptionHandler()->GetPassphraseType()); 1750 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1751 { 1752 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1753 Cryptographer* cryptographer = trans.GetCryptographer(); 1754 EXPECT_TRUE(cryptographer->is_ready()); 1755 // Verify we're encrypting with the new key. 1756 sync_pb::EncryptedData encrypted; 1757 cryptographer->GetKeys(&encrypted); 1758 EXPECT_TRUE(other_cryptographer.CanDecrypt(encrypted)); 1759 } 1760 } 1761 1762 // Manually set the pending keys in the cryptographer/nigori to reflect the data 1763 // being encrypted with an old (unprovided) GAIA password. Attempt to supply 1764 // the current GAIA password and verify the bootstrap token is updated. Then 1765 // supply the old GAIA password, and verify we re-encrypt all data with the 1766 // new GAIA password. 1767 // (cases 4 and 5 in SyncManager::SyncInternal::SetEncryptionPassphrase) 1768 TEST_F(SyncManagerTest, SupplyPendingOldGAIAPass) { 1769 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 1770 Cryptographer other_cryptographer(&encryptor_); 1771 { 1772 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1773 Cryptographer* cryptographer = trans.GetCryptographer(); 1774 std::string bootstrap_token; 1775 cryptographer->GetBootstrapToken(&bootstrap_token); 1776 other_cryptographer.Bootstrap(bootstrap_token); 1777 1778 // Now update the nigori to reflect the new keys, and update the 1779 // cryptographer to have pending keys. 1780 KeyParams params = {"localhost", "dummy", "old_gaia"}; 1781 other_cryptographer.AddKey(params); 1782 WriteNode node(&trans); 1783 EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag)); 1784 sync_pb::NigoriSpecifics nigori; 1785 other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); 1786 node.SetNigoriSpecifics(nigori); 1787 cryptographer->SetPendingKeys(nigori.encryption_keybag()); 1788 1789 // other_cryptographer now contains all encryption keys, and is encrypting 1790 // with the newest gaia. 1791 KeyParams new_params = {"localhost", "dummy", "new_gaia"}; 1792 other_cryptographer.AddKey(new_params); 1793 } 1794 // The bootstrap token should have been updated. Save it to ensure it's based 1795 // on the new GAIA password. 1796 std::string bootstrap_token; 1797 EXPECT_CALL(encryption_observer_, 1798 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)) 1799 .WillOnce(SaveArg<0>(&bootstrap_token)); 1800 EXPECT_CALL(encryption_observer_, OnPassphraseRequired(_,_)); 1801 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1802 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase( 1803 "new_gaia", 1804 false); 1805 EXPECT_EQ(IMPLICIT_PASSPHRASE, 1806 sync_manager_.GetEncryptionHandler()->GetPassphraseType()); 1807 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1808 testing::Mock::VerifyAndClearExpectations(&encryption_observer_); 1809 { 1810 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1811 Cryptographer* cryptographer = trans.GetCryptographer(); 1812 EXPECT_TRUE(cryptographer->is_initialized()); 1813 EXPECT_FALSE(cryptographer->is_ready()); 1814 // Verify we're encrypting with the new key, even though we have pending 1815 // keys. 1816 sync_pb::EncryptedData encrypted; 1817 other_cryptographer.GetKeys(&encrypted); 1818 EXPECT_TRUE(cryptographer->CanDecrypt(encrypted)); 1819 } 1820 EXPECT_CALL(encryption_observer_, 1821 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 1822 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 1823 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1824 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1825 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase( 1826 "old_gaia", 1827 false); 1828 EXPECT_EQ(IMPLICIT_PASSPHRASE, 1829 sync_manager_.GetEncryptionHandler()->GetPassphraseType()); 1830 { 1831 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1832 Cryptographer* cryptographer = trans.GetCryptographer(); 1833 EXPECT_TRUE(cryptographer->is_ready()); 1834 1835 // Verify we're encrypting with the new key. 1836 sync_pb::EncryptedData encrypted; 1837 other_cryptographer.GetKeys(&encrypted); 1838 EXPECT_TRUE(cryptographer->CanDecrypt(encrypted)); 1839 1840 // Verify the saved bootstrap token is based on the new gaia password. 1841 Cryptographer temp_cryptographer(&encryptor_); 1842 temp_cryptographer.Bootstrap(bootstrap_token); 1843 EXPECT_TRUE(temp_cryptographer.CanDecrypt(encrypted)); 1844 } 1845 } 1846 1847 // Manually set the pending keys in the cryptographer/nigori to reflect the data 1848 // being encrypted with an explicit (unprovided) passphrase, then supply the 1849 // passphrase. 1850 // (case 9 in SyncManager::SyncInternal::SetDecryptionPassphrase) 1851 TEST_F(SyncManagerTest, SupplyPendingExplicitPass) { 1852 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 1853 Cryptographer other_cryptographer(&encryptor_); 1854 { 1855 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1856 Cryptographer* cryptographer = trans.GetCryptographer(); 1857 std::string bootstrap_token; 1858 cryptographer->GetBootstrapToken(&bootstrap_token); 1859 other_cryptographer.Bootstrap(bootstrap_token); 1860 1861 // Now update the nigori to reflect the new keys, and update the 1862 // cryptographer to have pending keys. 1863 KeyParams params = {"localhost", "dummy", "explicit"}; 1864 other_cryptographer.AddKey(params); 1865 WriteNode node(&trans); 1866 EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag)); 1867 sync_pb::NigoriSpecifics nigori; 1868 other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); 1869 cryptographer->SetPendingKeys(nigori.encryption_keybag()); 1870 EXPECT_TRUE(cryptographer->has_pending_keys()); 1871 nigori.set_keybag_is_frozen(true); 1872 node.SetNigoriSpecifics(nigori); 1873 } 1874 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1875 EXPECT_CALL(encryption_observer_, 1876 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); 1877 EXPECT_CALL(encryption_observer_, OnPassphraseRequired(_, _)); 1878 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, false)); 1879 sync_manager_.GetEncryptionHandler()->Init(); 1880 EXPECT_CALL(encryption_observer_, 1881 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 1882 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 1883 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1884 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1885 sync_manager_.GetEncryptionHandler()->SetDecryptionPassphrase("explicit"); 1886 EXPECT_EQ(CUSTOM_PASSPHRASE, 1887 sync_manager_.GetEncryptionHandler()->GetPassphraseType()); 1888 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1889 { 1890 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1891 Cryptographer* cryptographer = trans.GetCryptographer(); 1892 EXPECT_TRUE(cryptographer->is_ready()); 1893 // Verify we're encrypting with the new key. 1894 sync_pb::EncryptedData encrypted; 1895 cryptographer->GetKeys(&encrypted); 1896 EXPECT_TRUE(other_cryptographer.CanDecrypt(encrypted)); 1897 } 1898 } 1899 1900 // Manually set the pending keys in the cryptographer/nigori to reflect the data 1901 // being encrypted with a new (unprovided) GAIA password, then supply the 1902 // password as a user-provided password. 1903 // This is the android case 7/8. 1904 TEST_F(SyncManagerTest, SupplyPendingGAIAPassUserProvided) { 1905 EXPECT_FALSE(SetUpEncryption(DONT_WRITE_NIGORI, UNINITIALIZED)); 1906 Cryptographer other_cryptographer(&encryptor_); 1907 { 1908 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1909 Cryptographer* cryptographer = trans.GetCryptographer(); 1910 // Now update the nigori to reflect the new keys, and update the 1911 // cryptographer to have pending keys. 1912 KeyParams params = {"localhost", "dummy", "passphrase"}; 1913 other_cryptographer.AddKey(params); 1914 WriteNode node(&trans); 1915 EXPECT_EQ(BaseNode::INIT_OK, node.InitByTagLookup(kNigoriTag)); 1916 sync_pb::NigoriSpecifics nigori; 1917 other_cryptographer.GetKeys(nigori.mutable_encryption_keybag()); 1918 node.SetNigoriSpecifics(nigori); 1919 cryptographer->SetPendingKeys(nigori.encryption_keybag()); 1920 EXPECT_FALSE(cryptographer->is_ready()); 1921 } 1922 EXPECT_CALL(encryption_observer_, 1923 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 1924 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 1925 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1926 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1927 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase( 1928 "passphrase", 1929 false); 1930 EXPECT_EQ(IMPLICIT_PASSPHRASE, 1931 sync_manager_.GetEncryptionHandler()->GetPassphraseType()); 1932 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1933 { 1934 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1935 Cryptographer* cryptographer = trans.GetCryptographer(); 1936 EXPECT_TRUE(cryptographer->is_ready()); 1937 } 1938 } 1939 1940 TEST_F(SyncManagerTest, SetPassphraseWithEmptyPasswordNode) { 1941 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 1942 int64 node_id = 0; 1943 std::string tag = "foo"; 1944 { 1945 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1946 ReadNode root_node(&trans); 1947 root_node.InitByRootLookup(); 1948 1949 WriteNode password_node(&trans); 1950 WriteNode::InitUniqueByCreationResult result = 1951 password_node.InitUniqueByCreation(PASSWORDS, root_node, tag); 1952 EXPECT_EQ(WriteNode::INIT_SUCCESS, result); 1953 node_id = password_node.GetId(); 1954 } 1955 EXPECT_CALL(encryption_observer_, 1956 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 1957 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 1958 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 1959 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 1960 EXPECT_CALL(encryption_observer_, 1961 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); 1962 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase( 1963 "new_passphrase", 1964 true); 1965 EXPECT_EQ(CUSTOM_PASSPHRASE, 1966 sync_manager_.GetEncryptionHandler()->GetPassphraseType()); 1967 EXPECT_FALSE(EncryptEverythingEnabledForTest()); 1968 { 1969 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1970 ReadNode password_node(&trans); 1971 EXPECT_EQ(BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY, 1972 password_node.InitByClientTagLookup(PASSWORDS, 1973 tag)); 1974 } 1975 { 1976 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 1977 ReadNode password_node(&trans); 1978 EXPECT_EQ(BaseNode::INIT_FAILED_DECRYPT_IF_NECESSARY, 1979 password_node.InitByIdLookup(node_id)); 1980 } 1981 } 1982 1983 TEST_F(SyncManagerTest, NudgeDelayTest) { 1984 EXPECT_EQ(sync_manager_.GetNudgeDelayTimeDelta(BOOKMARKS), 1985 base::TimeDelta::FromMilliseconds( 1986 SyncManagerImpl::GetDefaultNudgeDelay())); 1987 1988 EXPECT_EQ(sync_manager_.GetNudgeDelayTimeDelta(AUTOFILL), 1989 base::TimeDelta::FromSeconds( 1990 kDefaultShortPollIntervalSeconds)); 1991 1992 EXPECT_EQ(sync_manager_.GetNudgeDelayTimeDelta(PREFERENCES), 1993 base::TimeDelta::FromMilliseconds( 1994 SyncManagerImpl::GetPreferencesNudgeDelay())); 1995 } 1996 1997 // Friended by WriteNode, so can't be in an anonymouse namespace. 1998 TEST_F(SyncManagerTest, EncryptBookmarksWithLegacyData) { 1999 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 2000 std::string title; 2001 SyncAPINameToServerName("Google", &title); 2002 std::string url = "http://www.google.com"; 2003 std::string raw_title2 = ".."; // An invalid cosmo title. 2004 std::string title2; 2005 SyncAPINameToServerName(raw_title2, &title2); 2006 std::string url2 = "http://www.bla.com"; 2007 2008 // Create a bookmark using the legacy format. 2009 int64 node_id1 = MakeNode(sync_manager_.GetUserShare(), 2010 BOOKMARKS, 2011 "testtag"); 2012 int64 node_id2 = MakeNode(sync_manager_.GetUserShare(), 2013 BOOKMARKS, 2014 "testtag2"); 2015 { 2016 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2017 WriteNode node(&trans); 2018 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(node_id1)); 2019 2020 sync_pb::EntitySpecifics entity_specifics; 2021 entity_specifics.mutable_bookmark()->set_url(url); 2022 node.SetEntitySpecifics(entity_specifics); 2023 2024 // Set the old style title. 2025 syncable::MutableEntry* node_entry = node.entry_; 2026 node_entry->Put(syncable::NON_UNIQUE_NAME, title); 2027 2028 WriteNode node2(&trans); 2029 EXPECT_EQ(BaseNode::INIT_OK, node2.InitByIdLookup(node_id2)); 2030 2031 sync_pb::EntitySpecifics entity_specifics2; 2032 entity_specifics2.mutable_bookmark()->set_url(url2); 2033 node2.SetEntitySpecifics(entity_specifics2); 2034 2035 // Set the old style title. 2036 syncable::MutableEntry* node_entry2 = node2.entry_; 2037 node_entry2->Put(syncable::NON_UNIQUE_NAME, title2); 2038 } 2039 2040 { 2041 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2042 ReadNode node(&trans); 2043 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(node_id1)); 2044 EXPECT_EQ(BOOKMARKS, node.GetModelType()); 2045 EXPECT_EQ(title, node.GetTitle()); 2046 EXPECT_EQ(title, node.GetBookmarkSpecifics().title()); 2047 EXPECT_EQ(url, node.GetBookmarkSpecifics().url()); 2048 2049 ReadNode node2(&trans); 2050 EXPECT_EQ(BaseNode::INIT_OK, node2.InitByIdLookup(node_id2)); 2051 EXPECT_EQ(BOOKMARKS, node2.GetModelType()); 2052 // We should de-canonicalize the title in GetTitle(), but the title in the 2053 // specifics should be stored in the server legal form. 2054 EXPECT_EQ(raw_title2, node2.GetTitle()); 2055 EXPECT_EQ(title2, node2.GetBookmarkSpecifics().title()); 2056 EXPECT_EQ(url2, node2.GetBookmarkSpecifics().url()); 2057 } 2058 2059 { 2060 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2061 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 2062 trans.GetWrappedTrans(), 2063 BOOKMARKS, 2064 false /* not encrypted */)); 2065 } 2066 2067 EXPECT_CALL(encryption_observer_, 2068 OnEncryptedTypesChanged( 2069 HasModelTypes(EncryptableUserTypes()), true)); 2070 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 2071 sync_manager_.GetEncryptionHandler()->EnableEncryptEverything(); 2072 EXPECT_TRUE(EncryptEverythingEnabledForTest()); 2073 2074 { 2075 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2076 EXPECT_TRUE(GetEncryptedTypesWithTrans(&trans).Equals( 2077 EncryptableUserTypes())); 2078 EXPECT_TRUE(syncable::VerifyDataTypeEncryptionForTest( 2079 trans.GetWrappedTrans(), 2080 BOOKMARKS, 2081 true /* is encrypted */)); 2082 2083 ReadNode node(&trans); 2084 EXPECT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(node_id1)); 2085 EXPECT_EQ(BOOKMARKS, node.GetModelType()); 2086 EXPECT_EQ(title, node.GetTitle()); 2087 EXPECT_EQ(title, node.GetBookmarkSpecifics().title()); 2088 EXPECT_EQ(url, node.GetBookmarkSpecifics().url()); 2089 2090 ReadNode node2(&trans); 2091 EXPECT_EQ(BaseNode::INIT_OK, node2.InitByIdLookup(node_id2)); 2092 EXPECT_EQ(BOOKMARKS, node2.GetModelType()); 2093 // We should de-canonicalize the title in GetTitle(), but the title in the 2094 // specifics should be stored in the server legal form. 2095 EXPECT_EQ(raw_title2, node2.GetTitle()); 2096 EXPECT_EQ(title2, node2.GetBookmarkSpecifics().title()); 2097 EXPECT_EQ(url2, node2.GetBookmarkSpecifics().url()); 2098 } 2099 } 2100 2101 // Create a bookmark and set the title/url, then verify the data was properly 2102 // set. This replicates the unique way bookmarks have of creating sync nodes. 2103 // See BookmarkChangeProcessor::PlaceSyncNode(..). 2104 TEST_F(SyncManagerTest, CreateLocalBookmark) { 2105 std::string title = "title"; 2106 std::string url = "url"; 2107 { 2108 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2109 ReadNode bookmark_root(&trans); 2110 ASSERT_EQ(BaseNode::INIT_OK, 2111 bookmark_root.InitByTagLookup(ModelTypeToRootTag(BOOKMARKS))); 2112 WriteNode node(&trans); 2113 ASSERT_TRUE(node.InitBookmarkByCreation(bookmark_root, NULL)); 2114 node.SetIsFolder(false); 2115 node.SetTitle(UTF8ToWide(title)); 2116 2117 sync_pb::BookmarkSpecifics bookmark_specifics(node.GetBookmarkSpecifics()); 2118 bookmark_specifics.set_url(url); 2119 node.SetBookmarkSpecifics(bookmark_specifics); 2120 } 2121 { 2122 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2123 ReadNode bookmark_root(&trans); 2124 ASSERT_EQ(BaseNode::INIT_OK, 2125 bookmark_root.InitByTagLookup(ModelTypeToRootTag(BOOKMARKS))); 2126 int64 child_id = bookmark_root.GetFirstChildId(); 2127 2128 ReadNode node(&trans); 2129 ASSERT_EQ(BaseNode::INIT_OK, node.InitByIdLookup(child_id)); 2130 EXPECT_FALSE(node.GetIsFolder()); 2131 EXPECT_EQ(title, node.GetTitle()); 2132 EXPECT_EQ(url, node.GetBookmarkSpecifics().url()); 2133 } 2134 } 2135 2136 // Verifies WriteNode::UpdateEntryWithEncryption does not make unnecessary 2137 // changes. 2138 TEST_F(SyncManagerTest, UpdateEntryWithEncryption) { 2139 std::string client_tag = "title"; 2140 sync_pb::EntitySpecifics entity_specifics; 2141 entity_specifics.mutable_bookmark()->set_url("url"); 2142 entity_specifics.mutable_bookmark()->set_title("title"); 2143 MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag, 2144 syncable::GenerateSyncableHash(BOOKMARKS, 2145 client_tag), 2146 entity_specifics); 2147 // New node shouldn't start off unsynced. 2148 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2149 // Manually change to the same data. Should not set is_unsynced. 2150 { 2151 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2152 WriteNode node(&trans); 2153 EXPECT_EQ(BaseNode::INIT_OK, 2154 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2155 node.SetEntitySpecifics(entity_specifics); 2156 } 2157 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2158 2159 // Encrypt the datatatype, should set is_unsynced. 2160 EXPECT_CALL(encryption_observer_, 2161 OnEncryptedTypesChanged( 2162 HasModelTypes(EncryptableUserTypes()), true)); 2163 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 2164 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, FULL_ENCRYPTION)); 2165 2166 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 2167 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, true)); 2168 sync_manager_.GetEncryptionHandler()->Init(); 2169 PumpLoop(); 2170 { 2171 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2172 ReadNode node(&trans); 2173 EXPECT_EQ(BaseNode::INIT_OK, 2174 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2175 const syncable::Entry* node_entry = node.GetEntry(); 2176 const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS); 2177 EXPECT_TRUE(specifics.has_encrypted()); 2178 EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME)); 2179 Cryptographer* cryptographer = trans.GetCryptographer(); 2180 EXPECT_TRUE(cryptographer->is_ready()); 2181 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey( 2182 specifics.encrypted())); 2183 } 2184 EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2185 2186 // Set a new passphrase. Should set is_unsynced. 2187 testing::Mock::VerifyAndClearExpectations(&encryption_observer_); 2188 EXPECT_CALL(encryption_observer_, 2189 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 2190 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 2191 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 2192 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 2193 EXPECT_CALL(encryption_observer_, 2194 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); 2195 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase( 2196 "new_passphrase", 2197 true); 2198 { 2199 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2200 ReadNode node(&trans); 2201 EXPECT_EQ(BaseNode::INIT_OK, 2202 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2203 const syncable::Entry* node_entry = node.GetEntry(); 2204 const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS); 2205 EXPECT_TRUE(specifics.has_encrypted()); 2206 EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME)); 2207 Cryptographer* cryptographer = trans.GetCryptographer(); 2208 EXPECT_TRUE(cryptographer->is_ready()); 2209 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey( 2210 specifics.encrypted())); 2211 } 2212 EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2213 2214 // Force a re-encrypt everything. Should not set is_unsynced. 2215 testing::Mock::VerifyAndClearExpectations(&encryption_observer_); 2216 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 2217 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 2218 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, true)); 2219 2220 sync_manager_.GetEncryptionHandler()->Init(); 2221 PumpLoop(); 2222 2223 { 2224 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2225 ReadNode node(&trans); 2226 EXPECT_EQ(BaseNode::INIT_OK, 2227 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2228 const syncable::Entry* node_entry = node.GetEntry(); 2229 const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS); 2230 EXPECT_TRUE(specifics.has_encrypted()); 2231 EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME)); 2232 Cryptographer* cryptographer = trans.GetCryptographer(); 2233 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey( 2234 specifics.encrypted())); 2235 } 2236 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2237 2238 // Manually change to the same data. Should not set is_unsynced. 2239 { 2240 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2241 WriteNode node(&trans); 2242 EXPECT_EQ(BaseNode::INIT_OK, 2243 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2244 node.SetEntitySpecifics(entity_specifics); 2245 const syncable::Entry* node_entry = node.GetEntry(); 2246 const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS); 2247 EXPECT_TRUE(specifics.has_encrypted()); 2248 EXPECT_FALSE(node_entry->Get(IS_UNSYNCED)); 2249 EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME)); 2250 Cryptographer* cryptographer = trans.GetCryptographer(); 2251 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey( 2252 specifics.encrypted())); 2253 } 2254 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2255 2256 // Manually change to different data. Should set is_unsynced. 2257 { 2258 entity_specifics.mutable_bookmark()->set_url("url2"); 2259 entity_specifics.mutable_bookmark()->set_title("title2"); 2260 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2261 WriteNode node(&trans); 2262 EXPECT_EQ(BaseNode::INIT_OK, 2263 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2264 node.SetEntitySpecifics(entity_specifics); 2265 const syncable::Entry* node_entry = node.GetEntry(); 2266 const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS); 2267 EXPECT_TRUE(specifics.has_encrypted()); 2268 EXPECT_TRUE(node_entry->Get(IS_UNSYNCED)); 2269 EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME)); 2270 Cryptographer* cryptographer = trans.GetCryptographer(); 2271 EXPECT_TRUE(cryptographer->CanDecryptUsingDefaultKey( 2272 specifics.encrypted())); 2273 } 2274 } 2275 2276 // Passwords have their own handling for encryption. Verify it does not result 2277 // in unnecessary writes via SetEntitySpecifics. 2278 TEST_F(SyncManagerTest, UpdatePasswordSetEntitySpecificsNoChange) { 2279 std::string client_tag = "title"; 2280 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 2281 sync_pb::EntitySpecifics entity_specifics; 2282 { 2283 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2284 Cryptographer* cryptographer = trans.GetCryptographer(); 2285 sync_pb::PasswordSpecificsData data; 2286 data.set_password_value("secret"); 2287 cryptographer->Encrypt( 2288 data, 2289 entity_specifics.mutable_password()-> 2290 mutable_encrypted()); 2291 } 2292 MakeServerNode(sync_manager_.GetUserShare(), PASSWORDS, client_tag, 2293 syncable::GenerateSyncableHash(PASSWORDS, 2294 client_tag), 2295 entity_specifics); 2296 // New node shouldn't start off unsynced. 2297 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag)); 2298 2299 // Manually change to the same data via SetEntitySpecifics. Should not set 2300 // is_unsynced. 2301 { 2302 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2303 WriteNode node(&trans); 2304 EXPECT_EQ(BaseNode::INIT_OK, 2305 node.InitByClientTagLookup(PASSWORDS, client_tag)); 2306 node.SetEntitySpecifics(entity_specifics); 2307 } 2308 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag)); 2309 } 2310 2311 // Passwords have their own handling for encryption. Verify it does not result 2312 // in unnecessary writes via SetPasswordSpecifics. 2313 TEST_F(SyncManagerTest, UpdatePasswordSetPasswordSpecifics) { 2314 std::string client_tag = "title"; 2315 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 2316 sync_pb::EntitySpecifics entity_specifics; 2317 { 2318 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2319 Cryptographer* cryptographer = trans.GetCryptographer(); 2320 sync_pb::PasswordSpecificsData data; 2321 data.set_password_value("secret"); 2322 cryptographer->Encrypt( 2323 data, 2324 entity_specifics.mutable_password()-> 2325 mutable_encrypted()); 2326 } 2327 MakeServerNode(sync_manager_.GetUserShare(), PASSWORDS, client_tag, 2328 syncable::GenerateSyncableHash(PASSWORDS, 2329 client_tag), 2330 entity_specifics); 2331 // New node shouldn't start off unsynced. 2332 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag)); 2333 2334 // Manually change to the same data via SetPasswordSpecifics. Should not set 2335 // is_unsynced. 2336 { 2337 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2338 WriteNode node(&trans); 2339 EXPECT_EQ(BaseNode::INIT_OK, 2340 node.InitByClientTagLookup(PASSWORDS, client_tag)); 2341 node.SetPasswordSpecifics(node.GetPasswordSpecifics()); 2342 } 2343 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag)); 2344 2345 // Manually change to different data. Should set is_unsynced. 2346 { 2347 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2348 WriteNode node(&trans); 2349 EXPECT_EQ(BaseNode::INIT_OK, 2350 node.InitByClientTagLookup(PASSWORDS, client_tag)); 2351 Cryptographer* cryptographer = trans.GetCryptographer(); 2352 sync_pb::PasswordSpecificsData data; 2353 data.set_password_value("secret2"); 2354 cryptographer->Encrypt( 2355 data, 2356 entity_specifics.mutable_password()->mutable_encrypted()); 2357 node.SetPasswordSpecifics(data); 2358 const syncable::Entry* node_entry = node.GetEntry(); 2359 EXPECT_TRUE(node_entry->Get(IS_UNSYNCED)); 2360 } 2361 } 2362 2363 // Passwords have their own handling for encryption. Verify setting a new 2364 // passphrase updates the data. 2365 TEST_F(SyncManagerTest, UpdatePasswordNewPassphrase) { 2366 std::string client_tag = "title"; 2367 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 2368 sync_pb::EntitySpecifics entity_specifics; 2369 { 2370 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2371 Cryptographer* cryptographer = trans.GetCryptographer(); 2372 sync_pb::PasswordSpecificsData data; 2373 data.set_password_value("secret"); 2374 cryptographer->Encrypt( 2375 data, 2376 entity_specifics.mutable_password()->mutable_encrypted()); 2377 } 2378 MakeServerNode(sync_manager_.GetUserShare(), PASSWORDS, client_tag, 2379 syncable::GenerateSyncableHash(PASSWORDS, 2380 client_tag), 2381 entity_specifics); 2382 // New node shouldn't start off unsynced. 2383 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag)); 2384 2385 // Set a new passphrase. Should set is_unsynced. 2386 testing::Mock::VerifyAndClearExpectations(&encryption_observer_); 2387 EXPECT_CALL(encryption_observer_, 2388 OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN)); 2389 EXPECT_CALL(encryption_observer_, OnPassphraseAccepted()); 2390 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 2391 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 2392 EXPECT_CALL(encryption_observer_, 2393 OnPassphraseTypeChanged(CUSTOM_PASSPHRASE, _)); 2394 sync_manager_.GetEncryptionHandler()->SetEncryptionPassphrase( 2395 "new_passphrase", 2396 true); 2397 EXPECT_EQ(CUSTOM_PASSPHRASE, 2398 sync_manager_.GetEncryptionHandler()->GetPassphraseType()); 2399 EXPECT_TRUE(ResetUnsyncedEntry(PASSWORDS, client_tag)); 2400 } 2401 2402 // Passwords have their own handling for encryption. Verify it does not result 2403 // in unnecessary writes via ReencryptEverything. 2404 TEST_F(SyncManagerTest, UpdatePasswordReencryptEverything) { 2405 std::string client_tag = "title"; 2406 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 2407 sync_pb::EntitySpecifics entity_specifics; 2408 { 2409 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2410 Cryptographer* cryptographer = trans.GetCryptographer(); 2411 sync_pb::PasswordSpecificsData data; 2412 data.set_password_value("secret"); 2413 cryptographer->Encrypt( 2414 data, 2415 entity_specifics.mutable_password()->mutable_encrypted()); 2416 } 2417 MakeServerNode(sync_manager_.GetUserShare(), PASSWORDS, client_tag, 2418 syncable::GenerateSyncableHash(PASSWORDS, 2419 client_tag), 2420 entity_specifics); 2421 // New node shouldn't start off unsynced. 2422 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag)); 2423 2424 // Force a re-encrypt everything. Should not set is_unsynced. 2425 testing::Mock::VerifyAndClearExpectations(&encryption_observer_); 2426 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 2427 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 2428 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, false)); 2429 sync_manager_.GetEncryptionHandler()->Init(); 2430 PumpLoop(); 2431 EXPECT_FALSE(ResetUnsyncedEntry(PASSWORDS, client_tag)); 2432 } 2433 2434 // Verify SetTitle(..) doesn't unnecessarily set IS_UNSYNCED for bookmarks 2435 // when we write the same data, but does set it when we write new data. 2436 TEST_F(SyncManagerTest, SetBookmarkTitle) { 2437 std::string client_tag = "title"; 2438 sync_pb::EntitySpecifics entity_specifics; 2439 entity_specifics.mutable_bookmark()->set_url("url"); 2440 entity_specifics.mutable_bookmark()->set_title("title"); 2441 MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag, 2442 syncable::GenerateSyncableHash(BOOKMARKS, 2443 client_tag), 2444 entity_specifics); 2445 // New node shouldn't start off unsynced. 2446 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2447 2448 // Manually change to the same title. Should not set is_unsynced. 2449 { 2450 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2451 WriteNode node(&trans); 2452 EXPECT_EQ(BaseNode::INIT_OK, 2453 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2454 node.SetTitle(UTF8ToWide(client_tag)); 2455 } 2456 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2457 2458 // Manually change to new title. Should set is_unsynced. 2459 { 2460 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2461 WriteNode node(&trans); 2462 EXPECT_EQ(BaseNode::INIT_OK, 2463 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2464 node.SetTitle(UTF8ToWide("title2")); 2465 } 2466 EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2467 } 2468 2469 // Verify SetTitle(..) doesn't unnecessarily set IS_UNSYNCED for encrypted 2470 // bookmarks when we write the same data, but does set it when we write new 2471 // data. 2472 TEST_F(SyncManagerTest, SetBookmarkTitleWithEncryption) { 2473 std::string client_tag = "title"; 2474 sync_pb::EntitySpecifics entity_specifics; 2475 entity_specifics.mutable_bookmark()->set_url("url"); 2476 entity_specifics.mutable_bookmark()->set_title("title"); 2477 MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag, 2478 syncable::GenerateSyncableHash(BOOKMARKS, 2479 client_tag), 2480 entity_specifics); 2481 // New node shouldn't start off unsynced. 2482 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2483 2484 // Encrypt the datatatype, should set is_unsynced. 2485 EXPECT_CALL(encryption_observer_, 2486 OnEncryptedTypesChanged( 2487 HasModelTypes(EncryptableUserTypes()), true)); 2488 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 2489 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, FULL_ENCRYPTION)); 2490 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 2491 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, true)); 2492 sync_manager_.GetEncryptionHandler()->Init(); 2493 PumpLoop(); 2494 EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2495 2496 // Manually change to the same title. Should not set is_unsynced. 2497 // NON_UNIQUE_NAME should be kEncryptedString. 2498 { 2499 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2500 WriteNode node(&trans); 2501 EXPECT_EQ(BaseNode::INIT_OK, 2502 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2503 node.SetTitle(UTF8ToWide(client_tag)); 2504 const syncable::Entry* node_entry = node.GetEntry(); 2505 const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS); 2506 EXPECT_TRUE(specifics.has_encrypted()); 2507 EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME)); 2508 } 2509 EXPECT_FALSE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2510 2511 // Manually change to new title. Should set is_unsynced. NON_UNIQUE_NAME 2512 // should still be kEncryptedString. 2513 { 2514 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2515 WriteNode node(&trans); 2516 EXPECT_EQ(BaseNode::INIT_OK, 2517 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2518 node.SetTitle(UTF8ToWide("title2")); 2519 const syncable::Entry* node_entry = node.GetEntry(); 2520 const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS); 2521 EXPECT_TRUE(specifics.has_encrypted()); 2522 EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME)); 2523 } 2524 EXPECT_TRUE(ResetUnsyncedEntry(BOOKMARKS, client_tag)); 2525 } 2526 2527 // Verify SetTitle(..) doesn't unnecessarily set IS_UNSYNCED for non-bookmarks 2528 // when we write the same data, but does set it when we write new data. 2529 TEST_F(SyncManagerTest, SetNonBookmarkTitle) { 2530 std::string client_tag = "title"; 2531 sync_pb::EntitySpecifics entity_specifics; 2532 entity_specifics.mutable_preference()->set_name("name"); 2533 entity_specifics.mutable_preference()->set_value("value"); 2534 MakeServerNode(sync_manager_.GetUserShare(), 2535 PREFERENCES, 2536 client_tag, 2537 syncable::GenerateSyncableHash(PREFERENCES, 2538 client_tag), 2539 entity_specifics); 2540 // New node shouldn't start off unsynced. 2541 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag)); 2542 2543 // Manually change to the same title. Should not set is_unsynced. 2544 { 2545 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2546 WriteNode node(&trans); 2547 EXPECT_EQ(BaseNode::INIT_OK, 2548 node.InitByClientTagLookup(PREFERENCES, client_tag)); 2549 node.SetTitle(UTF8ToWide(client_tag)); 2550 } 2551 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag)); 2552 2553 // Manually change to new title. Should set is_unsynced. 2554 { 2555 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2556 WriteNode node(&trans); 2557 EXPECT_EQ(BaseNode::INIT_OK, 2558 node.InitByClientTagLookup(PREFERENCES, client_tag)); 2559 node.SetTitle(UTF8ToWide("title2")); 2560 } 2561 EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, client_tag)); 2562 } 2563 2564 // Verify SetTitle(..) doesn't unnecessarily set IS_UNSYNCED for encrypted 2565 // non-bookmarks when we write the same data or when we write new data 2566 // data (should remained kEncryptedString). 2567 TEST_F(SyncManagerTest, SetNonBookmarkTitleWithEncryption) { 2568 std::string client_tag = "title"; 2569 sync_pb::EntitySpecifics entity_specifics; 2570 entity_specifics.mutable_preference()->set_name("name"); 2571 entity_specifics.mutable_preference()->set_value("value"); 2572 MakeServerNode(sync_manager_.GetUserShare(), 2573 PREFERENCES, 2574 client_tag, 2575 syncable::GenerateSyncableHash(PREFERENCES, 2576 client_tag), 2577 entity_specifics); 2578 // New node shouldn't start off unsynced. 2579 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag)); 2580 2581 // Encrypt the datatatype, should set is_unsynced. 2582 EXPECT_CALL(encryption_observer_, 2583 OnEncryptedTypesChanged( 2584 HasModelTypes(EncryptableUserTypes()), true)); 2585 EXPECT_CALL(encryption_observer_, OnEncryptionComplete()); 2586 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, FULL_ENCRYPTION)); 2587 EXPECT_CALL(encryption_observer_, OnCryptographerStateChanged(_)); 2588 EXPECT_CALL(encryption_observer_, OnEncryptedTypesChanged(_, true)); 2589 sync_manager_.GetEncryptionHandler()->Init(); 2590 PumpLoop(); 2591 EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, client_tag)); 2592 2593 // Manually change to the same title. Should not set is_unsynced. 2594 // NON_UNIQUE_NAME should be kEncryptedString. 2595 { 2596 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2597 WriteNode node(&trans); 2598 EXPECT_EQ(BaseNode::INIT_OK, 2599 node.InitByClientTagLookup(PREFERENCES, client_tag)); 2600 node.SetTitle(UTF8ToWide(client_tag)); 2601 const syncable::Entry* node_entry = node.GetEntry(); 2602 const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS); 2603 EXPECT_TRUE(specifics.has_encrypted()); 2604 EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME)); 2605 } 2606 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, client_tag)); 2607 2608 // Manually change to new title. Should not set is_unsynced because the 2609 // NON_UNIQUE_NAME should still be kEncryptedString. 2610 { 2611 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2612 WriteNode node(&trans); 2613 EXPECT_EQ(BaseNode::INIT_OK, 2614 node.InitByClientTagLookup(PREFERENCES, client_tag)); 2615 node.SetTitle(UTF8ToWide("title2")); 2616 const syncable::Entry* node_entry = node.GetEntry(); 2617 const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS); 2618 EXPECT_TRUE(specifics.has_encrypted()); 2619 EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME)); 2620 EXPECT_FALSE(node_entry->Get(IS_UNSYNCED)); 2621 } 2622 } 2623 2624 // Ensure that titles are truncated to 255 bytes, and attempting to reset 2625 // them to their longer version does not set IS_UNSYNCED. 2626 TEST_F(SyncManagerTest, SetLongTitle) { 2627 const int kNumChars = 512; 2628 const std::string kClientTag = "tag"; 2629 std::string title(kNumChars, '0'); 2630 sync_pb::EntitySpecifics entity_specifics; 2631 entity_specifics.mutable_preference()->set_name("name"); 2632 entity_specifics.mutable_preference()->set_value("value"); 2633 MakeServerNode(sync_manager_.GetUserShare(), 2634 PREFERENCES, 2635 "short_title", 2636 syncable::GenerateSyncableHash(PREFERENCES, 2637 kClientTag), 2638 entity_specifics); 2639 // New node shouldn't start off unsynced. 2640 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, kClientTag)); 2641 2642 // Manually change to the long title. Should set is_unsynced. 2643 { 2644 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2645 WriteNode node(&trans); 2646 EXPECT_EQ(BaseNode::INIT_OK, 2647 node.InitByClientTagLookup(PREFERENCES, kClientTag)); 2648 node.SetTitle(UTF8ToWide(title)); 2649 EXPECT_EQ(node.GetTitle(), title.substr(0, 255)); 2650 } 2651 EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, kClientTag)); 2652 2653 // Manually change to the same title. Should not set is_unsynced. 2654 { 2655 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2656 WriteNode node(&trans); 2657 EXPECT_EQ(BaseNode::INIT_OK, 2658 node.InitByClientTagLookup(PREFERENCES, kClientTag)); 2659 node.SetTitle(UTF8ToWide(title)); 2660 EXPECT_EQ(node.GetTitle(), title.substr(0, 255)); 2661 } 2662 EXPECT_FALSE(ResetUnsyncedEntry(PREFERENCES, kClientTag)); 2663 2664 // Manually change to new title. Should set is_unsynced. 2665 { 2666 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2667 WriteNode node(&trans); 2668 EXPECT_EQ(BaseNode::INIT_OK, 2669 node.InitByClientTagLookup(PREFERENCES, kClientTag)); 2670 node.SetTitle(UTF8ToWide("title2")); 2671 } 2672 EXPECT_TRUE(ResetUnsyncedEntry(PREFERENCES, kClientTag)); 2673 } 2674 2675 // Create an encrypted entry when the cryptographer doesn't think the type is 2676 // marked for encryption. Ensure reads/writes don't break and don't unencrypt 2677 // the data. 2678 TEST_F(SyncManagerTest, SetPreviouslyEncryptedSpecifics) { 2679 std::string client_tag = "tag"; 2680 std::string url = "url"; 2681 std::string url2 = "new_url"; 2682 std::string title = "title"; 2683 sync_pb::EntitySpecifics entity_specifics; 2684 EXPECT_TRUE(SetUpEncryption(WRITE_TO_NIGORI, DEFAULT_ENCRYPTION)); 2685 { 2686 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2687 Cryptographer* crypto = trans.GetCryptographer(); 2688 sync_pb::EntitySpecifics bm_specifics; 2689 bm_specifics.mutable_bookmark()->set_title("title"); 2690 bm_specifics.mutable_bookmark()->set_url("url"); 2691 sync_pb::EncryptedData encrypted; 2692 crypto->Encrypt(bm_specifics, &encrypted); 2693 entity_specifics.mutable_encrypted()->CopyFrom(encrypted); 2694 AddDefaultFieldValue(BOOKMARKS, &entity_specifics); 2695 } 2696 MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag, 2697 syncable::GenerateSyncableHash(BOOKMARKS, 2698 client_tag), 2699 entity_specifics); 2700 2701 { 2702 // Verify the data. 2703 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2704 ReadNode node(&trans); 2705 EXPECT_EQ(BaseNode::INIT_OK, 2706 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2707 EXPECT_EQ(title, node.GetTitle()); 2708 EXPECT_EQ(url, node.GetBookmarkSpecifics().url()); 2709 } 2710 2711 { 2712 // Overwrite the url (which overwrites the specifics). 2713 WriteTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2714 WriteNode node(&trans); 2715 EXPECT_EQ(BaseNode::INIT_OK, 2716 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2717 2718 sync_pb::BookmarkSpecifics bookmark_specifics(node.GetBookmarkSpecifics()); 2719 bookmark_specifics.set_url(url2); 2720 node.SetBookmarkSpecifics(bookmark_specifics); 2721 } 2722 2723 { 2724 // Verify it's still encrypted and it has the most recent url. 2725 ReadTransaction trans(FROM_HERE, sync_manager_.GetUserShare()); 2726 ReadNode node(&trans); 2727 EXPECT_EQ(BaseNode::INIT_OK, 2728 node.InitByClientTagLookup(BOOKMARKS, client_tag)); 2729 EXPECT_EQ(title, node.GetTitle()); 2730 EXPECT_EQ(url2, node.GetBookmarkSpecifics().url()); 2731 const syncable::Entry* node_entry = node.GetEntry(); 2732 EXPECT_EQ(kEncryptedString, node_entry->Get(NON_UNIQUE_NAME)); 2733 const sync_pb::EntitySpecifics& specifics = node_entry->Get(SPECIFICS); 2734 EXPECT_TRUE(specifics.has_encrypted()); 2735 } 2736 } 2737 2738 // Verify transaction version of a model type is incremented when node of 2739 // that type is updated. 2740 TEST_F(SyncManagerTest, IncrementTransactionVersion) { 2741 ModelSafeRoutingInfo routing_info; 2742 GetModelSafeRoutingInfo(&routing_info); 2743 2744 { 2745 ReadTransaction read_trans(FROM_HERE, sync_manager_.GetUserShare()); 2746 for (ModelSafeRoutingInfo::iterator i = routing_info.begin(); 2747 i != routing_info.end(); ++i) { 2748 // Transaction version is incremented when SyncManagerTest::SetUp() 2749 // creates a node of each type. 2750 EXPECT_EQ(1, 2751 sync_manager_.GetUserShare()->directory-> 2752 GetTransactionVersion(i->first)); 2753 } 2754 } 2755 2756 // Create bookmark node to increment transaction version of bookmark model. 2757 std::string client_tag = "title"; 2758 sync_pb::EntitySpecifics entity_specifics; 2759 entity_specifics.mutable_bookmark()->set_url("url"); 2760 entity_specifics.mutable_bookmark()->set_title("title"); 2761 MakeServerNode(sync_manager_.GetUserShare(), BOOKMARKS, client_tag, 2762 syncable::GenerateSyncableHash(BOOKMARKS, 2763 client_tag), 2764 entity_specifics); 2765 2766 { 2767 ReadTransaction read_trans(FROM_HERE, sync_manager_.GetUserShare()); 2768 for (ModelSafeRoutingInfo::iterator i = routing_info.begin(); 2769 i != routing_info.end(); ++i) { 2770 EXPECT_EQ(i->first == BOOKMARKS ? 2 : 1, 2771 sync_manager_.GetUserShare()->directory-> 2772 GetTransactionVersion(i->first)); 2773 } 2774 } 2775 } 2776 2777 class MockSyncScheduler : public FakeSyncScheduler { 2778 public: 2779 MockSyncScheduler() : FakeSyncScheduler() {} 2780 virtual ~MockSyncScheduler() {} 2781 2782 MOCK_METHOD1(Start, void(SyncScheduler::Mode)); 2783 MOCK_METHOD1(ScheduleConfiguration, bool(const ConfigurationParams&)); 2784 }; 2785 2786 class ComponentsFactory : public TestInternalComponentsFactory { 2787 public: 2788 ComponentsFactory(const Switches& switches, 2789 SyncScheduler* scheduler_to_use, 2790 sessions::SyncSessionContext** session_context) 2791 : TestInternalComponentsFactory(switches, syncer::STORAGE_IN_MEMORY), 2792 scheduler_to_use_(scheduler_to_use), 2793 session_context_(session_context) {} 2794 virtual ~ComponentsFactory() {} 2795 2796 virtual scoped_ptr<SyncScheduler> BuildScheduler( 2797 const std::string& name, 2798 sessions::SyncSessionContext* context) OVERRIDE { 2799 *session_context_ = context; 2800 return scheduler_to_use_.Pass(); 2801 } 2802 2803 private: 2804 scoped_ptr<SyncScheduler> scheduler_to_use_; 2805 sessions::SyncSessionContext** session_context_; 2806 }; 2807 2808 class SyncManagerTestWithMockScheduler : public SyncManagerTest { 2809 public: 2810 SyncManagerTestWithMockScheduler() : scheduler_(NULL) {} 2811 virtual InternalComponentsFactory* GetFactory() OVERRIDE { 2812 scheduler_ = new MockSyncScheduler(); 2813 return new ComponentsFactory(GetSwitches(), scheduler_, &session_context_); 2814 } 2815 2816 MockSyncScheduler* scheduler() { return scheduler_; } 2817 sessions::SyncSessionContext* session_context() { 2818 return session_context_; 2819 } 2820 2821 private: 2822 MockSyncScheduler* scheduler_; 2823 sessions::SyncSessionContext* session_context_; 2824 }; 2825 2826 // Test that the configuration params are properly created and sent to 2827 // ScheduleConfigure. No callback should be invoked. Any disabled datatypes 2828 // should be purged. 2829 TEST_F(SyncManagerTestWithMockScheduler, BasicConfiguration) { 2830 ConfigureReason reason = CONFIGURE_REASON_RECONFIGURATION; 2831 ModelTypeSet types_to_download(BOOKMARKS, PREFERENCES); 2832 ModelSafeRoutingInfo new_routing_info; 2833 GetModelSafeRoutingInfo(&new_routing_info); 2834 ModelTypeSet enabled_types = GetRoutingInfoTypes(new_routing_info); 2835 ModelTypeSet disabled_types = Difference(ModelTypeSet::All(), enabled_types); 2836 2837 ConfigurationParams params; 2838 EXPECT_CALL(*scheduler(), Start(SyncScheduler::CONFIGURATION_MODE)); 2839 EXPECT_CALL(*scheduler(), ScheduleConfiguration(_)). 2840 WillOnce(DoAll(SaveArg<0>(¶ms), Return(true))); 2841 2842 // Set data for all types. 2843 ModelTypeSet protocol_types = ProtocolTypes(); 2844 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good(); 2845 iter.Inc()) { 2846 SetProgressMarkerForType(iter.Get(), true); 2847 } 2848 2849 CallbackCounter ready_task_counter, retry_task_counter; 2850 sync_manager_.ConfigureSyncer( 2851 reason, 2852 types_to_download, 2853 disabled_types, 2854 ModelTypeSet(), 2855 ModelTypeSet(), 2856 new_routing_info, 2857 base::Bind(&CallbackCounter::Callback, 2858 base::Unretained(&ready_task_counter)), 2859 base::Bind(&CallbackCounter::Callback, 2860 base::Unretained(&retry_task_counter))); 2861 EXPECT_EQ(0, ready_task_counter.times_called()); 2862 EXPECT_EQ(0, retry_task_counter.times_called()); 2863 EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::RECONFIGURATION, 2864 params.source); 2865 EXPECT_TRUE(types_to_download.Equals(params.types_to_download)); 2866 EXPECT_EQ(new_routing_info, params.routing_info); 2867 2868 // Verify all the disabled types were purged. 2869 EXPECT_TRUE(sync_manager_.InitialSyncEndedTypes().Equals( 2870 enabled_types)); 2871 EXPECT_TRUE(sync_manager_.GetTypesWithEmptyProgressMarkerToken( 2872 ModelTypeSet::All()).Equals(disabled_types)); 2873 } 2874 2875 // Test that on a reconfiguration (configuration where the session context 2876 // already has routing info), only those recently disabled types are purged. 2877 TEST_F(SyncManagerTestWithMockScheduler, ReConfiguration) { 2878 ConfigureReason reason = CONFIGURE_REASON_RECONFIGURATION; 2879 ModelTypeSet types_to_download(BOOKMARKS, PREFERENCES); 2880 ModelTypeSet disabled_types = ModelTypeSet(THEMES, SESSIONS); 2881 ModelSafeRoutingInfo old_routing_info; 2882 ModelSafeRoutingInfo new_routing_info; 2883 GetModelSafeRoutingInfo(&old_routing_info); 2884 new_routing_info = old_routing_info; 2885 new_routing_info.erase(THEMES); 2886 new_routing_info.erase(SESSIONS); 2887 ModelTypeSet enabled_types = GetRoutingInfoTypes(new_routing_info); 2888 2889 ConfigurationParams params; 2890 EXPECT_CALL(*scheduler(), Start(SyncScheduler::CONFIGURATION_MODE)); 2891 EXPECT_CALL(*scheduler(), ScheduleConfiguration(_)). 2892 WillOnce(DoAll(SaveArg<0>(¶ms), Return(true))); 2893 2894 // Set data for all types except those recently disabled (so we can verify 2895 // only those recently disabled are purged) . 2896 ModelTypeSet protocol_types = ProtocolTypes(); 2897 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good(); 2898 iter.Inc()) { 2899 if (!disabled_types.Has(iter.Get())) { 2900 SetProgressMarkerForType(iter.Get(), true); 2901 } else { 2902 SetProgressMarkerForType(iter.Get(), false); 2903 } 2904 } 2905 2906 // Set the context to have the old routing info. 2907 session_context()->set_routing_info(old_routing_info); 2908 2909 CallbackCounter ready_task_counter, retry_task_counter; 2910 sync_manager_.ConfigureSyncer( 2911 reason, 2912 types_to_download, 2913 ModelTypeSet(), 2914 ModelTypeSet(), 2915 ModelTypeSet(), 2916 new_routing_info, 2917 base::Bind(&CallbackCounter::Callback, 2918 base::Unretained(&ready_task_counter)), 2919 base::Bind(&CallbackCounter::Callback, 2920 base::Unretained(&retry_task_counter))); 2921 EXPECT_EQ(0, ready_task_counter.times_called()); 2922 EXPECT_EQ(0, retry_task_counter.times_called()); 2923 EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::RECONFIGURATION, 2924 params.source); 2925 EXPECT_TRUE(types_to_download.Equals(params.types_to_download)); 2926 EXPECT_EQ(new_routing_info, params.routing_info); 2927 2928 // Verify only the recently disabled types were purged. 2929 EXPECT_TRUE(sync_manager_.GetTypesWithEmptyProgressMarkerToken( 2930 ProtocolTypes()).Equals(disabled_types)); 2931 } 2932 2933 // Test that the retry callback is invoked on configuration failure. 2934 TEST_F(SyncManagerTestWithMockScheduler, ConfigurationRetry) { 2935 ConfigureReason reason = CONFIGURE_REASON_RECONFIGURATION; 2936 ModelTypeSet types_to_download(BOOKMARKS, PREFERENCES); 2937 ModelSafeRoutingInfo new_routing_info; 2938 GetModelSafeRoutingInfo(&new_routing_info); 2939 2940 ConfigurationParams params; 2941 EXPECT_CALL(*scheduler(), Start(SyncScheduler::CONFIGURATION_MODE)); 2942 EXPECT_CALL(*scheduler(), ScheduleConfiguration(_)). 2943 WillOnce(DoAll(SaveArg<0>(¶ms), Return(false))); 2944 2945 CallbackCounter ready_task_counter, retry_task_counter; 2946 sync_manager_.ConfigureSyncer( 2947 reason, 2948 types_to_download, 2949 ModelTypeSet(), 2950 ModelTypeSet(), 2951 ModelTypeSet(), 2952 new_routing_info, 2953 base::Bind(&CallbackCounter::Callback, 2954 base::Unretained(&ready_task_counter)), 2955 base::Bind(&CallbackCounter::Callback, 2956 base::Unretained(&retry_task_counter))); 2957 EXPECT_EQ(0, ready_task_counter.times_called()); 2958 EXPECT_EQ(1, retry_task_counter.times_called()); 2959 EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::RECONFIGURATION, 2960 params.source); 2961 EXPECT_TRUE(types_to_download.Equals(params.types_to_download)); 2962 EXPECT_EQ(new_routing_info, params.routing_info); 2963 } 2964 2965 // Test that PurgePartiallySyncedTypes purges only those types that have not 2966 // fully completed their initial download and apply. 2967 TEST_F(SyncManagerTest, PurgePartiallySyncedTypes) { 2968 ModelSafeRoutingInfo routing_info; 2969 GetModelSafeRoutingInfo(&routing_info); 2970 ModelTypeSet enabled_types = GetRoutingInfoTypes(routing_info); 2971 2972 UserShare* share = sync_manager_.GetUserShare(); 2973 2974 // The test harness automatically initializes all types in the routing info. 2975 // Check that autofill is not among them. 2976 ASSERT_FALSE(enabled_types.Has(AUTOFILL)); 2977 2978 // Further ensure that the test harness did not create its root node. 2979 { 2980 syncable::ReadTransaction trans(FROM_HERE, share->directory.get()); 2981 syncable::Entry autofill_root_node(&trans, syncable::GET_BY_SERVER_TAG, 2982 ModelTypeToRootTag(AUTOFILL)); 2983 ASSERT_FALSE(autofill_root_node.good()); 2984 } 2985 2986 // One more redundant check. 2987 ASSERT_FALSE(sync_manager_.InitialSyncEndedTypes().Has(AUTOFILL)); 2988 2989 // Give autofill a progress marker. 2990 sync_pb::DataTypeProgressMarker autofill_marker; 2991 autofill_marker.set_data_type_id( 2992 GetSpecificsFieldNumberFromModelType(AUTOFILL)); 2993 autofill_marker.set_token("token"); 2994 share->directory->SetDownloadProgress(AUTOFILL, autofill_marker); 2995 2996 // Also add a pending autofill root node update from the server. 2997 TestEntryFactory factory_(share->directory.get()); 2998 int autofill_meta = factory_.CreateUnappliedRootNode(AUTOFILL); 2999 3000 // Preferences is an enabled type. Check that the harness initialized it. 3001 ASSERT_TRUE(enabled_types.Has(PREFERENCES)); 3002 ASSERT_TRUE(sync_manager_.InitialSyncEndedTypes().Has(PREFERENCES)); 3003 3004 // Give preferencse a progress marker. 3005 sync_pb::DataTypeProgressMarker prefs_marker; 3006 prefs_marker.set_data_type_id( 3007 GetSpecificsFieldNumberFromModelType(PREFERENCES)); 3008 prefs_marker.set_token("token"); 3009 share->directory->SetDownloadProgress(PREFERENCES, prefs_marker); 3010 3011 // Add a fully synced preferences node under the root. 3012 std::string pref_client_tag = "prefABC"; 3013 std::string pref_hashed_tag = "hashXYZ"; 3014 sync_pb::EntitySpecifics pref_specifics; 3015 AddDefaultFieldValue(PREFERENCES, &pref_specifics); 3016 int pref_meta = MakeServerNode( 3017 share, PREFERENCES, pref_client_tag, pref_hashed_tag, pref_specifics); 3018 3019 // And now, the purge. 3020 EXPECT_TRUE(sync_manager_.PurgePartiallySyncedTypes()); 3021 3022 // Ensure that autofill lost its progress marker, but preferences did not. 3023 ModelTypeSet empty_tokens = 3024 sync_manager_.GetTypesWithEmptyProgressMarkerToken(ModelTypeSet::All()); 3025 EXPECT_TRUE(empty_tokens.Has(AUTOFILL)); 3026 EXPECT_FALSE(empty_tokens.Has(PREFERENCES)); 3027 3028 // Ensure that autofill lots its node, but preferences did not. 3029 { 3030 syncable::ReadTransaction trans(FROM_HERE, share->directory.get()); 3031 syncable::Entry autofill_node(&trans, GET_BY_HANDLE, autofill_meta); 3032 syncable::Entry pref_node(&trans, GET_BY_HANDLE, pref_meta); 3033 EXPECT_FALSE(autofill_node.good()); 3034 EXPECT_TRUE(pref_node.good()); 3035 } 3036 } 3037 3038 // Test CleanupDisabledTypes properly purges all disabled types as specified 3039 // by the previous and current enabled params. 3040 TEST_F(SyncManagerTest, PurgeDisabledTypes) { 3041 ModelSafeRoutingInfo routing_info; 3042 GetModelSafeRoutingInfo(&routing_info); 3043 ModelTypeSet enabled_types = GetRoutingInfoTypes(routing_info); 3044 ModelTypeSet disabled_types = Difference(ModelTypeSet::All(), enabled_types); 3045 3046 // The harness should have initialized the enabled_types for us. 3047 EXPECT_TRUE(enabled_types.Equals(sync_manager_.InitialSyncEndedTypes())); 3048 3049 // Set progress markers for all types. 3050 ModelTypeSet protocol_types = ProtocolTypes(); 3051 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good(); 3052 iter.Inc()) { 3053 SetProgressMarkerForType(iter.Get(), true); 3054 } 3055 3056 // Verify all the enabled types remain after cleanup, and all the disabled 3057 // types were purged. 3058 sync_manager_.PurgeDisabledTypes(disabled_types, 3059 ModelTypeSet(), 3060 ModelTypeSet()); 3061 EXPECT_TRUE(enabled_types.Equals(sync_manager_.InitialSyncEndedTypes())); 3062 EXPECT_TRUE(disabled_types.Equals( 3063 sync_manager_.GetTypesWithEmptyProgressMarkerToken(ModelTypeSet::All()))); 3064 3065 // Disable some more types. 3066 disabled_types.Put(BOOKMARKS); 3067 disabled_types.Put(PREFERENCES); 3068 ModelTypeSet new_enabled_types = 3069 Difference(ModelTypeSet::All(), disabled_types); 3070 3071 // Verify only the non-disabled types remain after cleanup. 3072 sync_manager_.PurgeDisabledTypes(disabled_types, 3073 ModelTypeSet(), 3074 ModelTypeSet()); 3075 EXPECT_TRUE(new_enabled_types.Equals(sync_manager_.InitialSyncEndedTypes())); 3076 EXPECT_TRUE(disabled_types.Equals( 3077 sync_manager_.GetTypesWithEmptyProgressMarkerToken(ModelTypeSet::All()))); 3078 } 3079 3080 // Test PurgeDisabledTypes properly unapplies types by deleting their local data 3081 // and preserving their server data and progress marker. 3082 TEST_F(SyncManagerTest, PurgeUnappliedTypes) { 3083 ModelSafeRoutingInfo routing_info; 3084 GetModelSafeRoutingInfo(&routing_info); 3085 ModelTypeSet unapplied_types = ModelTypeSet(BOOKMARKS, PREFERENCES); 3086 ModelTypeSet enabled_types = GetRoutingInfoTypes(routing_info); 3087 ModelTypeSet disabled_types = Difference(ModelTypeSet::All(), enabled_types); 3088 3089 // The harness should have initialized the enabled_types for us. 3090 EXPECT_TRUE(enabled_types.Equals(sync_manager_.InitialSyncEndedTypes())); 3091 3092 // Set progress markers for all types. 3093 ModelTypeSet protocol_types = ProtocolTypes(); 3094 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good(); 3095 iter.Inc()) { 3096 SetProgressMarkerForType(iter.Get(), true); 3097 } 3098 3099 // Add the following kinds of items: 3100 // 1. Fully synced preference. 3101 // 2. Locally created preference, server unknown, unsynced 3102 // 3. Locally deleted preference, server known, unsynced 3103 // 4. Server deleted preference, locally known. 3104 // 5. Server created preference, locally unknown, unapplied. 3105 // 6. A fully synced bookmark (no unique_client_tag). 3106 UserShare* share = sync_manager_.GetUserShare(); 3107 sync_pb::EntitySpecifics pref_specifics; 3108 AddDefaultFieldValue(PREFERENCES, &pref_specifics); 3109 sync_pb::EntitySpecifics bm_specifics; 3110 AddDefaultFieldValue(BOOKMARKS, &bm_specifics); 3111 int pref1_meta = MakeServerNode( 3112 share, PREFERENCES, "pref1", "hash1", pref_specifics); 3113 int64 pref2_meta = MakeNode(share, PREFERENCES, "pref2"); 3114 int pref3_meta = MakeServerNode( 3115 share, PREFERENCES, "pref3", "hash3", pref_specifics); 3116 int pref4_meta = MakeServerNode( 3117 share, PREFERENCES, "pref4", "hash4", pref_specifics); 3118 int pref5_meta = MakeServerNode( 3119 share, PREFERENCES, "pref5", "hash5", pref_specifics); 3120 int bookmark_meta = MakeServerNode( 3121 share, BOOKMARKS, "bookmark", "", bm_specifics); 3122 3123 { 3124 syncable::WriteTransaction trans(FROM_HERE, 3125 syncable::SYNCER, 3126 share->directory.get()); 3127 // Pref's 1 and 2 are already set up properly. 3128 // Locally delete pref 3. 3129 syncable::MutableEntry pref3(&trans, GET_BY_HANDLE, pref3_meta); 3130 pref3.Put(IS_DEL, true); 3131 pref3.Put(IS_UNSYNCED, true); 3132 // Delete pref 4 at the server. 3133 syncable::MutableEntry pref4(&trans, GET_BY_HANDLE, pref4_meta); 3134 pref4.Put(syncable::SERVER_IS_DEL, true); 3135 pref4.Put(syncable::IS_UNAPPLIED_UPDATE, true); 3136 pref4.Put(syncable::SERVER_VERSION, 2); 3137 // Pref 5 is an new unapplied update. 3138 syncable::MutableEntry pref5(&trans, GET_BY_HANDLE, pref5_meta); 3139 pref5.Put(syncable::IS_UNAPPLIED_UPDATE, true); 3140 pref5.Put(syncable::IS_DEL, true); 3141 pref5.Put(syncable::BASE_VERSION, -1); 3142 // Bookmark is already set up properly 3143 } 3144 3145 // Take a snapshot to clear all the dirty bits. 3146 share->directory.get()->SaveChanges(); 3147 3148 // Now request a purge for the unapplied types. 3149 disabled_types.PutAll(unapplied_types); 3150 sync_manager_.PurgeDisabledTypes(disabled_types, 3151 ModelTypeSet(), 3152 unapplied_types); 3153 3154 // Verify the unapplied types still have progress markers and initial sync 3155 // ended after cleanup. 3156 EXPECT_TRUE(sync_manager_.InitialSyncEndedTypes().HasAll(unapplied_types)); 3157 EXPECT_TRUE( 3158 sync_manager_.GetTypesWithEmptyProgressMarkerToken(unapplied_types). 3159 Empty()); 3160 3161 // Ensure the items were unapplied as necessary. 3162 { 3163 syncable::ReadTransaction trans(FROM_HERE, share->directory.get()); 3164 syncable::Entry pref_node(&trans, GET_BY_HANDLE, pref1_meta); 3165 ASSERT_TRUE(pref_node.good()); 3166 EXPECT_TRUE(pref_node.GetKernelCopy().is_dirty()); 3167 EXPECT_FALSE(pref_node.Get(syncable::IS_UNSYNCED)); 3168 EXPECT_TRUE(pref_node.Get(syncable::IS_UNAPPLIED_UPDATE)); 3169 EXPECT_TRUE(pref_node.Get(IS_DEL)); 3170 EXPECT_GT(pref_node.Get(syncable::SERVER_VERSION), 0); 3171 EXPECT_EQ(pref_node.Get(syncable::BASE_VERSION), -1); 3172 3173 // Pref 2 should just be locally deleted. 3174 syncable::Entry pref2_node(&trans, GET_BY_HANDLE, pref2_meta); 3175 ASSERT_TRUE(pref2_node.good()); 3176 EXPECT_TRUE(pref2_node.GetKernelCopy().is_dirty()); 3177 EXPECT_FALSE(pref2_node.Get(syncable::IS_UNSYNCED)); 3178 EXPECT_TRUE(pref2_node.Get(syncable::IS_DEL)); 3179 EXPECT_FALSE(pref2_node.Get(syncable::IS_UNAPPLIED_UPDATE)); 3180 EXPECT_TRUE(pref2_node.Get(IS_DEL)); 3181 EXPECT_EQ(pref2_node.Get(syncable::SERVER_VERSION), 0); 3182 EXPECT_EQ(pref2_node.Get(syncable::BASE_VERSION), -1); 3183 3184 syncable::Entry pref3_node(&trans, GET_BY_HANDLE, pref3_meta); 3185 ASSERT_TRUE(pref3_node.good()); 3186 EXPECT_TRUE(pref3_node.GetKernelCopy().is_dirty()); 3187 EXPECT_FALSE(pref3_node.Get(syncable::IS_UNSYNCED)); 3188 EXPECT_TRUE(pref3_node.Get(syncable::IS_UNAPPLIED_UPDATE)); 3189 EXPECT_TRUE(pref3_node.Get(IS_DEL)); 3190 EXPECT_GT(pref3_node.Get(syncable::SERVER_VERSION), 0); 3191 EXPECT_EQ(pref3_node.Get(syncable::BASE_VERSION), -1); 3192 3193 syncable::Entry pref4_node(&trans, GET_BY_HANDLE, pref4_meta); 3194 ASSERT_TRUE(pref4_node.good()); 3195 EXPECT_TRUE(pref4_node.GetKernelCopy().is_dirty()); 3196 EXPECT_FALSE(pref4_node.Get(syncable::IS_UNSYNCED)); 3197 EXPECT_TRUE(pref4_node.Get(syncable::IS_UNAPPLIED_UPDATE)); 3198 EXPECT_TRUE(pref4_node.Get(IS_DEL)); 3199 EXPECT_GT(pref4_node.Get(syncable::SERVER_VERSION), 0); 3200 EXPECT_EQ(pref4_node.Get(syncable::BASE_VERSION), -1); 3201 3202 // Pref 5 should remain untouched. 3203 syncable::Entry pref5_node(&trans, GET_BY_HANDLE, pref5_meta); 3204 ASSERT_TRUE(pref5_node.good()); 3205 EXPECT_FALSE(pref5_node.GetKernelCopy().is_dirty()); 3206 EXPECT_FALSE(pref5_node.Get(syncable::IS_UNSYNCED)); 3207 EXPECT_TRUE(pref5_node.Get(syncable::IS_UNAPPLIED_UPDATE)); 3208 EXPECT_TRUE(pref5_node.Get(IS_DEL)); 3209 EXPECT_GT(pref5_node.Get(syncable::SERVER_VERSION), 0); 3210 EXPECT_EQ(pref5_node.Get(syncable::BASE_VERSION), -1); 3211 3212 syncable::Entry bookmark_node(&trans, GET_BY_HANDLE, bookmark_meta); 3213 ASSERT_TRUE(bookmark_node.good()); 3214 EXPECT_TRUE(bookmark_node.GetKernelCopy().is_dirty()); 3215 EXPECT_FALSE(bookmark_node.Get(syncable::IS_UNSYNCED)); 3216 EXPECT_TRUE(bookmark_node.Get(syncable::IS_UNAPPLIED_UPDATE)); 3217 EXPECT_TRUE(bookmark_node.Get(IS_DEL)); 3218 EXPECT_GT(bookmark_node.Get(syncable::SERVER_VERSION), 0); 3219 EXPECT_EQ(bookmark_node.Get(syncable::BASE_VERSION), -1); 3220 } 3221 } 3222 3223 // A test harness to exercise the code that processes and passes changes from 3224 // the "SYNCER"-WriteTransaction destructor, through the SyncManager, to the 3225 // ChangeProcessor. 3226 class SyncManagerChangeProcessingTest : public SyncManagerTest { 3227 public: 3228 virtual void OnChangesApplied( 3229 ModelType model_type, 3230 int64 model_version, 3231 const BaseTransaction* trans, 3232 const ImmutableChangeRecordList& changes) OVERRIDE { 3233 last_changes_ = changes; 3234 } 3235 3236 virtual void OnChangesComplete(ModelType model_type) OVERRIDE {} 3237 3238 const ImmutableChangeRecordList& GetRecentChangeList() { 3239 return last_changes_; 3240 } 3241 3242 UserShare* share() { 3243 return sync_manager_.GetUserShare(); 3244 } 3245 3246 // Set some flags so our nodes reasonably approximate the real world scenario 3247 // and can get past CheckTreeInvariants. 3248 // 3249 // It's never going to be truly accurate, since we're squashing update 3250 // receipt, processing and application into a single transaction. 3251 void SetNodeProperties(syncable::MutableEntry *entry) { 3252 entry->Put(syncable::ID, id_factory_.NewServerId()); 3253 entry->Put(syncable::BASE_VERSION, 10); 3254 entry->Put(syncable::SERVER_VERSION, 10); 3255 } 3256 3257 // Looks for the given change in the list. Returns the index at which it was 3258 // found. Returns -1 on lookup failure. 3259 size_t FindChangeInList(int64 id, ChangeRecord::Action action) { 3260 SCOPED_TRACE(id); 3261 for (size_t i = 0; i < last_changes_.Get().size(); ++i) { 3262 if (last_changes_.Get()[i].id == id 3263 && last_changes_.Get()[i].action == action) { 3264 return i; 3265 } 3266 } 3267 ADD_FAILURE() << "Failed to find specified change"; 3268 return -1; 3269 } 3270 3271 // Returns the current size of the change list. 3272 // 3273 // Note that spurious changes do not necessarily indicate a problem. 3274 // Assertions on change list size can help detect problems, but it may be 3275 // necessary to reduce their strictness if the implementation changes. 3276 size_t GetChangeListSize() { 3277 return last_changes_.Get().size(); 3278 } 3279 3280 protected: 3281 ImmutableChangeRecordList last_changes_; 3282 TestIdFactory id_factory_; 3283 }; 3284 3285 // Test creation of a folder and a bookmark. 3286 TEST_F(SyncManagerChangeProcessingTest, AddBookmarks) { 3287 int64 type_root = GetIdForDataType(BOOKMARKS); 3288 int64 folder_id = kInvalidId; 3289 int64 child_id = kInvalidId; 3290 3291 // Create a folder and a bookmark under it. 3292 { 3293 syncable::WriteTransaction trans( 3294 FROM_HERE, syncable::SYNCER, share()->directory.get()); 3295 syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root); 3296 ASSERT_TRUE(root.good()); 3297 3298 syncable::MutableEntry folder(&trans, syncable::CREATE, 3299 BOOKMARKS, root.Get(syncable::ID), "folder"); 3300 ASSERT_TRUE(folder.good()); 3301 SetNodeProperties(&folder); 3302 folder.Put(syncable::IS_DIR, true); 3303 folder_id = folder.Get(syncable::META_HANDLE); 3304 3305 syncable::MutableEntry child(&trans, syncable::CREATE, 3306 BOOKMARKS, folder.Get(syncable::ID), "child"); 3307 ASSERT_TRUE(child.good()); 3308 SetNodeProperties(&child); 3309 child_id = child.Get(syncable::META_HANDLE); 3310 } 3311 3312 // The closing of the above scope will delete the transaction. Its processed 3313 // changes should be waiting for us in a member of the test harness. 3314 EXPECT_EQ(2UL, GetChangeListSize()); 3315 3316 // We don't need to check these return values here. The function will add a 3317 // non-fatal failure if these changes are not found. 3318 size_t folder_change_pos = 3319 FindChangeInList(folder_id, ChangeRecord::ACTION_ADD); 3320 size_t child_change_pos = 3321 FindChangeInList(child_id, ChangeRecord::ACTION_ADD); 3322 3323 // Parents are delivered before children. 3324 EXPECT_LT(folder_change_pos, child_change_pos); 3325 } 3326 3327 // Test moving a bookmark into an empty folder. 3328 TEST_F(SyncManagerChangeProcessingTest, MoveBookmarkIntoEmptyFolder) { 3329 int64 type_root = GetIdForDataType(BOOKMARKS); 3330 int64 folder_b_id = kInvalidId; 3331 int64 child_id = kInvalidId; 3332 3333 // Create two folders. Place a child under folder A. 3334 { 3335 syncable::WriteTransaction trans( 3336 FROM_HERE, syncable::SYNCER, share()->directory.get()); 3337 syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root); 3338 ASSERT_TRUE(root.good()); 3339 3340 syncable::MutableEntry folder_a(&trans, syncable::CREATE, 3341 BOOKMARKS, root.Get(syncable::ID), "folderA"); 3342 ASSERT_TRUE(folder_a.good()); 3343 SetNodeProperties(&folder_a); 3344 folder_a.Put(syncable::IS_DIR, true); 3345 3346 syncable::MutableEntry folder_b(&trans, syncable::CREATE, 3347 BOOKMARKS, root.Get(syncable::ID), "folderB"); 3348 ASSERT_TRUE(folder_b.good()); 3349 SetNodeProperties(&folder_b); 3350 folder_b.Put(syncable::IS_DIR, true); 3351 folder_b_id = folder_b.Get(syncable::META_HANDLE); 3352 3353 syncable::MutableEntry child(&trans, syncable::CREATE, 3354 BOOKMARKS, folder_a.Get(syncable::ID), 3355 "child"); 3356 ASSERT_TRUE(child.good()); 3357 SetNodeProperties(&child); 3358 child_id = child.Get(syncable::META_HANDLE); 3359 } 3360 3361 // Close that transaction. The above was to setup the initial scenario. The 3362 // real test starts now. 3363 3364 // Move the child from folder A to folder B. 3365 { 3366 syncable::WriteTransaction trans( 3367 FROM_HERE, syncable::SYNCER, share()->directory.get()); 3368 3369 syncable::Entry folder_b(&trans, syncable::GET_BY_HANDLE, folder_b_id); 3370 syncable::MutableEntry child(&trans, syncable::GET_BY_HANDLE, child_id); 3371 3372 child.Put(syncable::PARENT_ID, folder_b.Get(syncable::ID)); 3373 } 3374 3375 EXPECT_EQ(1UL, GetChangeListSize()); 3376 3377 // Verify that this was detected as a real change. An early version of the 3378 // UniquePosition code had a bug where moves from one folder to another were 3379 // ignored unless the moved node's UniquePosition value was also changed in 3380 // some way. 3381 FindChangeInList(child_id, ChangeRecord::ACTION_UPDATE); 3382 } 3383 3384 // Test moving a bookmark into a non-empty folder. 3385 TEST_F(SyncManagerChangeProcessingTest, MoveIntoPopulatedFolder) { 3386 int64 type_root = GetIdForDataType(BOOKMARKS); 3387 int64 child_a_id = kInvalidId; 3388 int64 child_b_id = kInvalidId; 3389 3390 // Create two folders. Place one child each under folder A and folder B. 3391 { 3392 syncable::WriteTransaction trans( 3393 FROM_HERE, syncable::SYNCER, share()->directory.get()); 3394 syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root); 3395 ASSERT_TRUE(root.good()); 3396 3397 syncable::MutableEntry folder_a(&trans, syncable::CREATE, 3398 BOOKMARKS, root.Get(syncable::ID), "folderA"); 3399 ASSERT_TRUE(folder_a.good()); 3400 SetNodeProperties(&folder_a); 3401 folder_a.Put(syncable::IS_DIR, true); 3402 3403 syncable::MutableEntry folder_b(&trans, syncable::CREATE, 3404 BOOKMARKS, root.Get(syncable::ID), "folderB"); 3405 ASSERT_TRUE(folder_b.good()); 3406 SetNodeProperties(&folder_b); 3407 folder_b.Put(syncable::IS_DIR, true); 3408 3409 syncable::MutableEntry child_a(&trans, syncable::CREATE, 3410 BOOKMARKS, folder_a.Get(syncable::ID), 3411 "childA"); 3412 ASSERT_TRUE(child_a.good()); 3413 SetNodeProperties(&child_a); 3414 child_a_id = child_a.Get(syncable::META_HANDLE); 3415 3416 syncable::MutableEntry child_b(&trans, syncable::CREATE, 3417 BOOKMARKS, folder_b.Get(syncable::ID), 3418 "childB"); 3419 SetNodeProperties(&child_b); 3420 child_b_id = child_b.Get(syncable::META_HANDLE); 3421 3422 } 3423 3424 // Close that transaction. The above was to setup the initial scenario. The 3425 // real test starts now. 3426 3427 { 3428 syncable::WriteTransaction trans( 3429 FROM_HERE, syncable::SYNCER, share()->directory.get()); 3430 3431 syncable::MutableEntry child_a(&trans, syncable::GET_BY_HANDLE, child_a_id); 3432 syncable::MutableEntry child_b(&trans, syncable::GET_BY_HANDLE, child_b_id); 3433 3434 // Move child A from folder A to folder B and update its position. 3435 child_a.Put(syncable::PARENT_ID, child_b.Get(syncable::PARENT_ID)); 3436 child_a.PutPredecessor(child_b.Get(syncable::ID)); 3437 } 3438 3439 EXPECT_EQ(1UL, GetChangeListSize()); 3440 3441 // Verify that only child a is in the change list. 3442 // (This function will add a failure if the lookup fails.) 3443 FindChangeInList(child_a_id, ChangeRecord::ACTION_UPDATE); 3444 } 3445 3446 // Tests the ordering of deletion changes. 3447 TEST_F(SyncManagerChangeProcessingTest, DeletionsAndChanges) { 3448 int64 type_root = GetIdForDataType(BOOKMARKS); 3449 int64 folder_a_id = kInvalidId; 3450 int64 folder_b_id = kInvalidId; 3451 int64 child_id = kInvalidId; 3452 3453 // Create two folders. Place a child under folder A. 3454 { 3455 syncable::WriteTransaction trans( 3456 FROM_HERE, syncable::SYNCER, share()->directory.get()); 3457 syncable::Entry root(&trans, syncable::GET_BY_HANDLE, type_root); 3458 ASSERT_TRUE(root.good()); 3459 3460 syncable::MutableEntry folder_a(&trans, syncable::CREATE, 3461 BOOKMARKS, root.Get(syncable::ID), "folderA"); 3462 ASSERT_TRUE(folder_a.good()); 3463 SetNodeProperties(&folder_a); 3464 folder_a.Put(syncable::IS_DIR, true); 3465 folder_a_id = folder_a.Get(syncable::META_HANDLE); 3466 3467 syncable::MutableEntry folder_b(&trans, syncable::CREATE, 3468 BOOKMARKS, root.Get(syncable::ID), "folderB"); 3469 ASSERT_TRUE(folder_b.good()); 3470 SetNodeProperties(&folder_b); 3471 folder_b.Put(syncable::IS_DIR, true); 3472 folder_b_id = folder_b.Get(syncable::META_HANDLE); 3473 3474 syncable::MutableEntry child(&trans, syncable::CREATE, 3475 BOOKMARKS, folder_a.Get(syncable::ID), 3476 "child"); 3477 ASSERT_TRUE(child.good()); 3478 SetNodeProperties(&child); 3479 child_id = child.Get(syncable::META_HANDLE); 3480 } 3481 3482 // Close that transaction. The above was to setup the initial scenario. The 3483 // real test starts now. 3484 3485 { 3486 syncable::WriteTransaction trans( 3487 FROM_HERE, syncable::SYNCER, share()->directory.get()); 3488 3489 syncable::MutableEntry folder_a( 3490 &trans, syncable::GET_BY_HANDLE, folder_a_id); 3491 syncable::MutableEntry folder_b( 3492 &trans, syncable::GET_BY_HANDLE, folder_b_id); 3493 syncable::MutableEntry child(&trans, syncable::GET_BY_HANDLE, child_id); 3494 3495 // Delete folder B and its child. 3496 child.Put(syncable::IS_DEL, true); 3497 folder_b.Put(syncable::IS_DEL, true); 3498 3499 // Make an unrelated change to folder A. 3500 folder_a.Put(syncable::NON_UNIQUE_NAME, "NewNameA"); 3501 } 3502 3503 EXPECT_EQ(3UL, GetChangeListSize()); 3504 3505 size_t folder_a_pos = 3506 FindChangeInList(folder_a_id, ChangeRecord::ACTION_UPDATE); 3507 size_t folder_b_pos = 3508 FindChangeInList(folder_b_id, ChangeRecord::ACTION_DELETE); 3509 size_t child_pos = FindChangeInList(child_id, ChangeRecord::ACTION_DELETE); 3510 3511 // Deletes should appear before updates. 3512 EXPECT_LT(child_pos, folder_a_pos); 3513 EXPECT_LT(folder_b_pos, folder_a_pos); 3514 } 3515 3516 } // namespace 3517