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