1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/sync/syncable/syncable.h" 6 7 #include "build/build_config.h" 8 9 #include <sys/types.h> 10 11 #include <limits> 12 #include <string> 13 14 #if !defined(OS_WIN) 15 #define MAX_PATH PATH_MAX 16 #include <ostream> 17 #include <stdio.h> 18 #include <sys/ipc.h> 19 #include <sys/sem.h> 20 #include <sys/times.h> 21 #endif // !defined(OS_WIN) 22 23 #include "base/file_path.h" 24 #include "base/file_util.h" 25 #include "base/logging.h" 26 #include "base/memory/scoped_ptr.h" 27 #include "base/memory/scoped_temp_dir.h" 28 #include "base/string_util.h" 29 #include "base/threading/platform_thread.h" 30 #include "base/values.h" 31 #include "chrome/browser/sync/engine/syncproto.h" 32 #include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" 33 #include "chrome/browser/sync/syncable/directory_backing_store.h" 34 #include "chrome/browser/sync/syncable/directory_manager.h" 35 #include "chrome/common/deprecated/event_sys-inl.h" 36 #include "chrome/test/sync/engine/test_id_factory.h" 37 #include "chrome/test/sync/engine/test_syncable_utils.h" 38 #include "chrome/test/values_test_util.h" 39 #include "testing/gtest/include/gtest/gtest.h" 40 #include "third_party/sqlite/sqlite3.h" 41 42 using browser_sync::TestIdFactory; 43 using test::ExpectBooleanValue; 44 using test::ExpectStringValue; 45 46 namespace syncable { 47 48 class SyncableKernelTest : public testing::Test {}; 49 50 TEST_F(SyncableKernelTest, ToValue) { 51 EntryKernel kernel; 52 scoped_ptr<DictionaryValue> value(kernel.ToValue()); 53 if (value.get()) { 54 // Not much to check without repeating the ToValue() code. 55 EXPECT_TRUE(value->HasKey("isDirty")); 56 // The extra +1 is for "isDirty". 57 EXPECT_EQ(BIT_TEMPS_END - BEGIN_FIELDS + 1, 58 static_cast<int>(value->size())); 59 } else { 60 ADD_FAILURE(); 61 } 62 } 63 64 namespace { 65 void PutDataAsBookmarkFavicon(WriteTransaction* wtrans, 66 MutableEntry* e, 67 const char* bytes, 68 size_t bytes_length) { 69 sync_pb::EntitySpecifics specifics; 70 specifics.MutableExtension(sync_pb::bookmark)->set_url("http://demo/"); 71 specifics.MutableExtension(sync_pb::bookmark)->set_favicon(bytes, 72 bytes_length); 73 e->Put(SPECIFICS, specifics); 74 } 75 76 void ExpectDataFromBookmarkFaviconEquals(BaseTransaction* trans, 77 Entry* e, 78 const char* bytes, 79 size_t bytes_length) { 80 ASSERT_TRUE(e->good()); 81 ASSERT_TRUE(e->Get(SPECIFICS).HasExtension(sync_pb::bookmark)); 82 ASSERT_EQ("http://demo/", 83 e->Get(SPECIFICS).GetExtension(sync_pb::bookmark).url()); 84 ASSERT_EQ(std::string(bytes, bytes_length), 85 e->Get(SPECIFICS).GetExtension(sync_pb::bookmark).favicon()); 86 } 87 } // namespace 88 89 class SyncableGeneralTest : public testing::Test { 90 public: 91 virtual void SetUp() { 92 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 93 db_path_ = temp_dir_.path().Append( 94 FILE_PATH_LITERAL("SyncableTest.sqlite3")); 95 } 96 97 virtual void TearDown() { 98 } 99 protected: 100 ScopedTempDir temp_dir_; 101 FilePath db_path_; 102 }; 103 104 TEST_F(SyncableGeneralTest, General) { 105 Directory dir; 106 dir.Open(db_path_, "SimpleTest"); 107 108 int64 written_metahandle; 109 const Id id = TestIdFactory::FromNumber(99); 110 std::string name = "Jeff"; 111 // Test simple read operations on an empty DB. 112 { 113 ReadTransaction rtrans(&dir, __FILE__, __LINE__); 114 Entry e(&rtrans, GET_BY_ID, id); 115 ASSERT_FALSE(e.good()); // Hasn't been written yet. 116 117 Directory::ChildHandles child_handles; 118 dir.GetChildHandles(&rtrans, rtrans.root_id(), &child_handles); 119 EXPECT_TRUE(child_handles.empty()); 120 } 121 122 // Test creating a new meta entry. 123 { 124 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__); 125 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), name); 126 ASSERT_TRUE(me.good()); 127 me.Put(ID, id); 128 me.Put(BASE_VERSION, 1); 129 written_metahandle = me.Get(META_HANDLE); 130 } 131 132 // Test GetChildHandles after something is now in the DB. 133 // Also check that GET_BY_ID works. 134 { 135 ReadTransaction rtrans(&dir, __FILE__, __LINE__); 136 Entry e(&rtrans, GET_BY_ID, id); 137 ASSERT_TRUE(e.good()); 138 139 Directory::ChildHandles child_handles; 140 dir.GetChildHandles(&rtrans, rtrans.root_id(), &child_handles); 141 EXPECT_EQ(1u, child_handles.size()); 142 143 for (Directory::ChildHandles::iterator i = child_handles.begin(); 144 i != child_handles.end(); ++i) { 145 EXPECT_EQ(*i, written_metahandle); 146 } 147 } 148 149 // Test writing data to an entity. Also check that GET_BY_HANDLE works. 150 static const char s[] = "Hello World."; 151 { 152 WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__); 153 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle); 154 ASSERT_TRUE(e.good()); 155 PutDataAsBookmarkFavicon(&trans, &e, s, sizeof(s)); 156 } 157 158 // Test reading back the contents that we just wrote. 159 { 160 WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__); 161 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle); 162 ASSERT_TRUE(e.good()); 163 ExpectDataFromBookmarkFaviconEquals(&trans, &e, s, sizeof(s)); 164 } 165 166 // Verify it exists in the folder. 167 { 168 ReadTransaction rtrans(&dir, __FILE__, __LINE__); 169 EXPECT_EQ(1, CountEntriesWithName(&rtrans, rtrans.root_id(), name)); 170 } 171 172 // Now delete it. 173 { 174 WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__); 175 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle); 176 e.Put(IS_DEL, true); 177 178 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name)); 179 } 180 181 dir.SaveChanges(); 182 } 183 184 TEST_F(SyncableGeneralTest, ClientIndexRebuildsProperly) { 185 int64 written_metahandle; 186 TestIdFactory factory; 187 const Id id = factory.NewServerId(); 188 std::string name = "cheesepuffs"; 189 std::string tag = "dietcoke"; 190 191 // Test creating a new meta entry. 192 { 193 Directory dir; 194 dir.Open(db_path_, "IndexTest"); 195 { 196 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__); 197 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), name); 198 ASSERT_TRUE(me.good()); 199 me.Put(ID, id); 200 me.Put(BASE_VERSION, 1); 201 me.Put(UNIQUE_CLIENT_TAG, tag); 202 written_metahandle = me.Get(META_HANDLE); 203 } 204 dir.SaveChanges(); 205 } 206 207 // The DB was closed. Now reopen it. This will cause index regeneration. 208 { 209 Directory dir; 210 dir.Open(db_path_, "IndexTest"); 211 212 ReadTransaction trans(&dir, __FILE__, __LINE__); 213 Entry me(&trans, GET_BY_CLIENT_TAG, tag); 214 ASSERT_TRUE(me.good()); 215 EXPECT_EQ(me.Get(ID), id); 216 EXPECT_EQ(me.Get(BASE_VERSION), 1); 217 EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), tag); 218 EXPECT_EQ(me.Get(META_HANDLE), written_metahandle); 219 } 220 } 221 222 TEST_F(SyncableGeneralTest, ClientIndexRebuildsDeletedProperly) { 223 TestIdFactory factory; 224 const Id id = factory.NewServerId(); 225 std::string tag = "dietcoke"; 226 227 // Test creating a deleted, unsynced, server meta entry. 228 { 229 Directory dir; 230 dir.Open(db_path_, "IndexTest"); 231 { 232 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__); 233 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), "deleted"); 234 ASSERT_TRUE(me.good()); 235 me.Put(ID, id); 236 me.Put(BASE_VERSION, 1); 237 me.Put(UNIQUE_CLIENT_TAG, tag); 238 me.Put(IS_DEL, true); 239 me.Put(IS_UNSYNCED, true); // Or it might be purged. 240 } 241 dir.SaveChanges(); 242 } 243 244 // The DB was closed. Now reopen it. This will cause index regeneration. 245 // Should still be present and valid in the client tag index. 246 { 247 Directory dir; 248 dir.Open(db_path_, "IndexTest"); 249 250 ReadTransaction trans(&dir, __FILE__, __LINE__); 251 Entry me(&trans, GET_BY_CLIENT_TAG, tag); 252 ASSERT_TRUE(me.good()); 253 EXPECT_EQ(me.Get(ID), id); 254 EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), tag); 255 EXPECT_TRUE(me.Get(IS_DEL)); 256 EXPECT_TRUE(me.Get(IS_UNSYNCED)); 257 } 258 } 259 260 TEST_F(SyncableGeneralTest, ToValue) { 261 Directory dir; 262 dir.Open(db_path_, "SimpleTest"); 263 264 const Id id = TestIdFactory::FromNumber(99); 265 { 266 ReadTransaction rtrans(&dir, __FILE__, __LINE__); 267 Entry e(&rtrans, GET_BY_ID, id); 268 EXPECT_FALSE(e.good()); // Hasn't been written yet. 269 270 scoped_ptr<DictionaryValue> value(e.ToValue()); 271 ExpectBooleanValue(false, *value, "good"); 272 EXPECT_EQ(1u, value->size()); 273 } 274 275 // Test creating a new meta entry. 276 { 277 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__); 278 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), "new"); 279 ASSERT_TRUE(me.good()); 280 me.Put(ID, id); 281 me.Put(BASE_VERSION, 1); 282 283 scoped_ptr<DictionaryValue> value(me.ToValue()); 284 ExpectBooleanValue(true, *value, "good"); 285 EXPECT_TRUE(value->HasKey("kernel")); 286 ExpectStringValue("Unspecified", *value, "serverModelType"); 287 ExpectStringValue("Unspecified", *value, "modelType"); 288 ExpectBooleanValue(false, *value, "shouldMaintainPosition"); 289 ExpectBooleanValue(true, *value, "existsOnClientBecauseNameIsNonEmpty"); 290 ExpectBooleanValue(false, *value, "isRoot"); 291 } 292 293 dir.SaveChanges(); 294 } 295 296 // A Directory whose backing store always fails SaveChanges by returning false. 297 class TestUnsaveableDirectory : public Directory { 298 public: 299 class UnsaveableBackingStore : public DirectoryBackingStore { 300 public: 301 UnsaveableBackingStore(const std::string& dir_name, 302 const FilePath& backing_filepath) 303 : DirectoryBackingStore(dir_name, backing_filepath) { } 304 virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot) { 305 return false; 306 } 307 }; 308 virtual DirectoryBackingStore* CreateBackingStore( 309 const std::string& dir_name, 310 const FilePath& backing_filepath) { 311 return new UnsaveableBackingStore(dir_name, backing_filepath); 312 } 313 }; 314 315 // Test suite for syncable::Directory. 316 class SyncableDirectoryTest : public testing::Test { 317 protected: 318 static const FilePath::CharType kFilePath[]; 319 static const char kName[]; 320 static const Id kId; 321 322 // SetUp() is called before each test case is run. 323 // The sqlite3 DB is deleted before each test is run. 324 virtual void SetUp() { 325 file_path_ = FilePath(kFilePath); 326 file_util::Delete(file_path_, true); 327 dir_.reset(new Directory()); 328 ASSERT_TRUE(dir_.get()); 329 ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName)); 330 ASSERT_TRUE(dir_->good()); 331 } 332 333 virtual void TearDown() { 334 // This also closes file handles. 335 dir_->SaveChanges(); 336 dir_.reset(); 337 file_util::Delete(file_path_, true); 338 } 339 340 void ReloadDir() { 341 dir_.reset(new Directory()); 342 ASSERT_TRUE(dir_.get()); 343 ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName)); 344 } 345 346 void SaveAndReloadDir() { 347 dir_->SaveChanges(); 348 ReloadDir(); 349 } 350 351 bool IsInDirtyMetahandles(int64 metahandle) { 352 return 1 == dir_->kernel_->dirty_metahandles->count(metahandle); 353 } 354 355 bool IsInMetahandlesToPurge(int64 metahandle) { 356 return 1 == dir_->kernel_->metahandles_to_purge->count(metahandle); 357 } 358 359 void CheckPurgeEntriesWithTypeInSucceeded(const ModelTypeSet& types_to_purge, 360 bool before_reload) { 361 SCOPED_TRACE(testing::Message("Before reload: ") << before_reload); 362 { 363 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 364 MetahandleSet all_set; 365 dir_->GetAllMetaHandles(&trans, &all_set); 366 EXPECT_EQ(3U, all_set.size()); 367 if (before_reload) 368 EXPECT_EQ(4U, dir_->kernel_->metahandles_to_purge->size()); 369 for (MetahandleSet::iterator iter = all_set.begin(); 370 iter != all_set.end(); ++iter) { 371 Entry e(&trans, GET_BY_HANDLE, *iter); 372 if ((types_to_purge.count(e.GetModelType()) || 373 types_to_purge.count(e.GetServerModelType()))) { 374 FAIL() << "Illegal type should have been deleted."; 375 } 376 } 377 } 378 379 EXPECT_FALSE(dir_->initial_sync_ended_for_type(PREFERENCES)); 380 EXPECT_FALSE(dir_->initial_sync_ended_for_type(AUTOFILL)); 381 EXPECT_TRUE(dir_->initial_sync_ended_for_type(BOOKMARKS)); 382 } 383 384 scoped_ptr<Directory> dir_; 385 FilePath file_path_; 386 387 // Creates an empty entry and sets the ID field to the default kId. 388 void CreateEntry(const std::string& entryname) { 389 CreateEntry(entryname, kId); 390 } 391 392 // Creates an empty entry and sets the ID field to id. 393 void CreateEntry(const std::string& entryname, const int id) { 394 CreateEntry(entryname, TestIdFactory::FromNumber(id)); 395 } 396 void CreateEntry(const std::string& entryname, Id id) { 397 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__); 398 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), entryname); 399 ASSERT_TRUE(me.good()); 400 me.Put(ID, id); 401 me.Put(IS_UNSYNCED, true); 402 } 403 404 void ValidateEntry(BaseTransaction* trans, 405 int64 id, 406 bool check_name, 407 const std::string& name, 408 int64 base_version, 409 int64 server_version, 410 bool is_del); 411 }; 412 413 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsMetahandlesToPurge) { 414 const int metas_to_create = 50; 415 MetahandleSet expected_purges; 416 MetahandleSet all_handles; 417 { 418 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 419 for (int i = 0; i < metas_to_create; i++) { 420 MutableEntry e(&trans, CREATE, trans.root_id(), "foo"); 421 e.Put(IS_UNSYNCED, true); 422 sync_pb::EntitySpecifics specs; 423 if (i % 2 == 0) { 424 AddDefaultExtensionValue(BOOKMARKS, &specs); 425 expected_purges.insert(e.Get(META_HANDLE)); 426 all_handles.insert(e.Get(META_HANDLE)); 427 } else { 428 AddDefaultExtensionValue(PREFERENCES, &specs); 429 all_handles.insert(e.Get(META_HANDLE)); 430 } 431 e.Put(SPECIFICS, specs); 432 e.Put(SERVER_SPECIFICS, specs); 433 } 434 } 435 436 ModelTypeSet to_purge; 437 to_purge.insert(BOOKMARKS); 438 dir_->PurgeEntriesWithTypeIn(to_purge); 439 440 Directory::SaveChangesSnapshot snapshot1; 441 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 442 dir_->TakeSnapshotForSaveChanges(&snapshot1); 443 EXPECT_TRUE(expected_purges == snapshot1.metahandles_to_purge); 444 445 to_purge.clear(); 446 to_purge.insert(PREFERENCES); 447 dir_->PurgeEntriesWithTypeIn(to_purge); 448 449 dir_->HandleSaveChangesFailure(snapshot1); 450 451 Directory::SaveChangesSnapshot snapshot2; 452 dir_->TakeSnapshotForSaveChanges(&snapshot2); 453 EXPECT_TRUE(all_handles == snapshot2.metahandles_to_purge); 454 } 455 456 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsAllDirtyHandlesTest) { 457 const int metahandles_to_create = 100; 458 std::vector<int64> expected_dirty_metahandles; 459 { 460 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 461 for (int i = 0; i < metahandles_to_create; i++) { 462 MutableEntry e(&trans, CREATE, trans.root_id(), "foo"); 463 expected_dirty_metahandles.push_back(e.Get(META_HANDLE)); 464 e.Put(IS_UNSYNCED, true); 465 } 466 } 467 // Fake SaveChanges() and make sure we got what we expected. 468 { 469 Directory::SaveChangesSnapshot snapshot; 470 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 471 dir_->TakeSnapshotForSaveChanges(&snapshot); 472 // Make sure there's an entry for each new metahandle. Make sure all 473 // entries are marked dirty. 474 ASSERT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size()); 475 for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin(); 476 i != snapshot.dirty_metas.end(); ++i) { 477 ASSERT_TRUE(i->is_dirty()); 478 } 479 dir_->VacuumAfterSaveChanges(snapshot); 480 } 481 // Put a new value with existing transactions as well as adding new ones. 482 { 483 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 484 std::vector<int64> new_dirty_metahandles; 485 for (std::vector<int64>::const_iterator i = 486 expected_dirty_metahandles.begin(); 487 i != expected_dirty_metahandles.end(); ++i) { 488 // Change existing entries to directories to dirty them. 489 MutableEntry e1(&trans, GET_BY_HANDLE, *i); 490 e1.Put(IS_DIR, true); 491 e1.Put(IS_UNSYNCED, true); 492 // Add new entries 493 MutableEntry e2(&trans, CREATE, trans.root_id(), "bar"); 494 e2.Put(IS_UNSYNCED, true); 495 new_dirty_metahandles.push_back(e2.Get(META_HANDLE)); 496 } 497 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(), 498 new_dirty_metahandles.begin(), new_dirty_metahandles.end()); 499 } 500 // Fake SaveChanges() and make sure we got what we expected. 501 { 502 Directory::SaveChangesSnapshot snapshot; 503 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 504 dir_->TakeSnapshotForSaveChanges(&snapshot); 505 // Make sure there's an entry for each new metahandle. Make sure all 506 // entries are marked dirty. 507 EXPECT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size()); 508 for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin(); 509 i != snapshot.dirty_metas.end(); ++i) { 510 EXPECT_TRUE(i->is_dirty()); 511 } 512 dir_->VacuumAfterSaveChanges(snapshot); 513 } 514 } 515 516 TEST_F(SyncableDirectoryTest, TestPurgeEntriesWithTypeIn) { 517 sync_pb::EntitySpecifics bookmark_specs; 518 sync_pb::EntitySpecifics autofill_specs; 519 sync_pb::EntitySpecifics preference_specs; 520 AddDefaultExtensionValue(BOOKMARKS, &bookmark_specs); 521 AddDefaultExtensionValue(PREFERENCES, &preference_specs); 522 AddDefaultExtensionValue(AUTOFILL, &autofill_specs); 523 dir_->set_initial_sync_ended_for_type(BOOKMARKS, true); 524 dir_->set_initial_sync_ended_for_type(PREFERENCES, true); 525 dir_->set_initial_sync_ended_for_type(AUTOFILL, true); 526 527 std::set<ModelType> types_to_purge; 528 types_to_purge.insert(PREFERENCES); 529 types_to_purge.insert(AUTOFILL); 530 531 TestIdFactory id_factory; 532 // Create some items for each type. 533 { 534 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 535 MutableEntry item1(&trans, CREATE, trans.root_id(), "Item"); 536 ASSERT_TRUE(item1.good()); 537 item1.Put(SPECIFICS, bookmark_specs); 538 item1.Put(SERVER_SPECIFICS, bookmark_specs); 539 item1.Put(IS_UNSYNCED, true); 540 541 MutableEntry item2(&trans, CREATE_NEW_UPDATE_ITEM, 542 id_factory.NewServerId()); 543 ASSERT_TRUE(item2.good()); 544 item2.Put(SERVER_SPECIFICS, bookmark_specs); 545 item2.Put(IS_UNAPPLIED_UPDATE, true); 546 547 MutableEntry item3(&trans, CREATE, trans.root_id(), "Item"); 548 ASSERT_TRUE(item3.good()); 549 item3.Put(SPECIFICS, preference_specs); 550 item3.Put(SERVER_SPECIFICS, preference_specs); 551 item3.Put(IS_UNSYNCED, true); 552 553 MutableEntry item4(&trans, CREATE_NEW_UPDATE_ITEM, 554 id_factory.NewServerId()); 555 ASSERT_TRUE(item4.good()); 556 item4.Put(SERVER_SPECIFICS, preference_specs); 557 item4.Put(IS_UNAPPLIED_UPDATE, true); 558 559 MutableEntry item5(&trans, CREATE, trans.root_id(), "Item"); 560 ASSERT_TRUE(item5.good()); 561 item5.Put(SPECIFICS, autofill_specs); 562 item5.Put(SERVER_SPECIFICS, autofill_specs); 563 item5.Put(IS_UNSYNCED, true); 564 565 MutableEntry item6(&trans, CREATE_NEW_UPDATE_ITEM, 566 id_factory.NewServerId()); 567 ASSERT_TRUE(item6.good()); 568 item6.Put(SERVER_SPECIFICS, autofill_specs); 569 item6.Put(IS_UNAPPLIED_UPDATE, true); 570 } 571 572 dir_->SaveChanges(); 573 { 574 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 575 MetahandleSet all_set; 576 dir_->GetAllMetaHandles(&trans, &all_set); 577 ASSERT_EQ(7U, all_set.size()); 578 } 579 580 dir_->PurgeEntriesWithTypeIn(types_to_purge); 581 582 // We first query the in-memory data, and then reload the directory (without 583 // saving) to verify that disk does not still have the data. 584 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, true); 585 SaveAndReloadDir(); 586 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, false); 587 } 588 589 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsOnlyDirtyHandlesTest) { 590 const int metahandles_to_create = 100; 591 592 // half of 2 * metahandles_to_create 593 const unsigned int number_changed = 100u; 594 std::vector<int64> expected_dirty_metahandles; 595 { 596 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 597 for (int i = 0; i < metahandles_to_create; i++) { 598 MutableEntry e(&trans, CREATE, trans.root_id(), "foo"); 599 expected_dirty_metahandles.push_back(e.Get(META_HANDLE)); 600 e.Put(IS_UNSYNCED, true); 601 } 602 } 603 dir_->SaveChanges(); 604 // Put a new value with existing transactions as well as adding new ones. 605 { 606 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 607 std::vector<int64> new_dirty_metahandles; 608 for (std::vector<int64>::const_iterator i = 609 expected_dirty_metahandles.begin(); 610 i != expected_dirty_metahandles.end(); ++i) { 611 // Change existing entries to directories to dirty them. 612 MutableEntry e1(&trans, GET_BY_HANDLE, *i); 613 ASSERT_TRUE(e1.good()); 614 e1.Put(IS_DIR, true); 615 e1.Put(IS_UNSYNCED, true); 616 // Add new entries 617 MutableEntry e2(&trans, CREATE, trans.root_id(), "bar"); 618 e2.Put(IS_UNSYNCED, true); 619 new_dirty_metahandles.push_back(e2.Get(META_HANDLE)); 620 } 621 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(), 622 new_dirty_metahandles.begin(), new_dirty_metahandles.end()); 623 } 624 dir_->SaveChanges(); 625 // Don't make any changes whatsoever and ensure nothing comes back. 626 { 627 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 628 for (std::vector<int64>::const_iterator i = 629 expected_dirty_metahandles.begin(); 630 i != expected_dirty_metahandles.end(); ++i) { 631 MutableEntry e(&trans, GET_BY_HANDLE, *i); 632 ASSERT_TRUE(e.good()); 633 // We aren't doing anything to dirty these entries. 634 } 635 } 636 // Fake SaveChanges() and make sure we got what we expected. 637 { 638 Directory::SaveChangesSnapshot snapshot; 639 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 640 dir_->TakeSnapshotForSaveChanges(&snapshot); 641 // Make sure there are no dirty_metahandles. 642 EXPECT_EQ(0u, snapshot.dirty_metas.size()); 643 dir_->VacuumAfterSaveChanges(snapshot); 644 } 645 { 646 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 647 bool should_change = false; 648 for (std::vector<int64>::const_iterator i = 649 expected_dirty_metahandles.begin(); 650 i != expected_dirty_metahandles.end(); ++i) { 651 // Maybe change entries by flipping IS_DIR. 652 MutableEntry e(&trans, GET_BY_HANDLE, *i); 653 ASSERT_TRUE(e.good()); 654 should_change = !should_change; 655 if (should_change) { 656 bool not_dir = !e.Get(IS_DIR); 657 e.Put(IS_DIR, not_dir); 658 e.Put(IS_UNSYNCED, true); 659 } 660 } 661 } 662 // Fake SaveChanges() and make sure we got what we expected. 663 { 664 Directory::SaveChangesSnapshot snapshot; 665 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 666 dir_->TakeSnapshotForSaveChanges(&snapshot); 667 // Make sure there's an entry for each changed metahandle. Make sure all 668 // entries are marked dirty. 669 EXPECT_EQ(number_changed, snapshot.dirty_metas.size()); 670 for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin(); 671 i != snapshot.dirty_metas.end(); ++i) { 672 EXPECT_TRUE(i->is_dirty()); 673 } 674 dir_->VacuumAfterSaveChanges(snapshot); 675 } 676 } 677 678 const FilePath::CharType SyncableDirectoryTest::kFilePath[] = 679 FILE_PATH_LITERAL("Test.sqlite3"); 680 const char SyncableDirectoryTest::kName[] = "Foo"; 681 const Id SyncableDirectoryTest::kId(TestIdFactory::FromNumber(-99)); 682 683 namespace { 684 TEST_F(SyncableDirectoryTest, TestBasicLookupNonExistantID) { 685 ReadTransaction rtrans(dir_.get(), __FILE__, __LINE__); 686 Entry e(&rtrans, GET_BY_ID, kId); 687 ASSERT_FALSE(e.good()); 688 } 689 690 TEST_F(SyncableDirectoryTest, TestBasicLookupValidID) { 691 CreateEntry("rtc"); 692 ReadTransaction rtrans(dir_.get(), __FILE__, __LINE__); 693 Entry e(&rtrans, GET_BY_ID, kId); 694 ASSERT_TRUE(e.good()); 695 } 696 697 TEST_F(SyncableDirectoryTest, TestDelete) { 698 std::string name = "peanut butter jelly time"; 699 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 700 MutableEntry e1(&trans, CREATE, trans.root_id(), name); 701 ASSERT_TRUE(e1.good()); 702 ASSERT_TRUE(e1.Put(IS_DEL, true)); 703 MutableEntry e2(&trans, CREATE, trans.root_id(), name); 704 ASSERT_TRUE(e2.good()); 705 ASSERT_TRUE(e2.Put(IS_DEL, true)); 706 MutableEntry e3(&trans, CREATE, trans.root_id(), name); 707 ASSERT_TRUE(e3.good()); 708 ASSERT_TRUE(e3.Put(IS_DEL, true)); 709 710 ASSERT_TRUE(e1.Put(IS_DEL, false)); 711 ASSERT_TRUE(e2.Put(IS_DEL, false)); 712 ASSERT_TRUE(e3.Put(IS_DEL, false)); 713 714 ASSERT_TRUE(e1.Put(IS_DEL, true)); 715 ASSERT_TRUE(e2.Put(IS_DEL, true)); 716 ASSERT_TRUE(e3.Put(IS_DEL, true)); 717 } 718 719 TEST_F(SyncableDirectoryTest, TestGetUnsynced) { 720 Directory::UnsyncedMetaHandles handles; 721 int64 handle1, handle2; 722 { 723 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 724 725 dir_->GetUnsyncedMetaHandles(&trans, &handles); 726 ASSERT_TRUE(0 == handles.size()); 727 728 MutableEntry e1(&trans, CREATE, trans.root_id(), "abba"); 729 ASSERT_TRUE(e1.good()); 730 handle1 = e1.Get(META_HANDLE); 731 e1.Put(BASE_VERSION, 1); 732 e1.Put(IS_DIR, true); 733 e1.Put(ID, TestIdFactory::FromNumber(101)); 734 735 MutableEntry e2(&trans, CREATE, e1.Get(ID), "bread"); 736 ASSERT_TRUE(e2.good()); 737 handle2 = e2.Get(META_HANDLE); 738 e2.Put(BASE_VERSION, 1); 739 e2.Put(ID, TestIdFactory::FromNumber(102)); 740 } 741 dir_->SaveChanges(); 742 { 743 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 744 745 dir_->GetUnsyncedMetaHandles(&trans, &handles); 746 ASSERT_TRUE(0 == handles.size()); 747 748 MutableEntry e3(&trans, GET_BY_HANDLE, handle1); 749 ASSERT_TRUE(e3.good()); 750 e3.Put(IS_UNSYNCED, true); 751 } 752 dir_->SaveChanges(); 753 { 754 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 755 dir_->GetUnsyncedMetaHandles(&trans, &handles); 756 ASSERT_TRUE(1 == handles.size()); 757 ASSERT_TRUE(handle1 == handles[0]); 758 759 MutableEntry e4(&trans, GET_BY_HANDLE, handle2); 760 ASSERT_TRUE(e4.good()); 761 e4.Put(IS_UNSYNCED, true); 762 } 763 dir_->SaveChanges(); 764 { 765 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 766 dir_->GetUnsyncedMetaHandles(&trans, &handles); 767 ASSERT_TRUE(2 == handles.size()); 768 if (handle1 == handles[0]) { 769 ASSERT_TRUE(handle2 == handles[1]); 770 } else { 771 ASSERT_TRUE(handle2 == handles[0]); 772 ASSERT_TRUE(handle1 == handles[1]); 773 } 774 775 MutableEntry e5(&trans, GET_BY_HANDLE, handle1); 776 ASSERT_TRUE(e5.good()); 777 ASSERT_TRUE(e5.Get(IS_UNSYNCED)); 778 ASSERT_TRUE(e5.Put(IS_UNSYNCED, false)); 779 ASSERT_FALSE(e5.Get(IS_UNSYNCED)); 780 } 781 dir_->SaveChanges(); 782 { 783 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 784 dir_->GetUnsyncedMetaHandles(&trans, &handles); 785 ASSERT_TRUE(1 == handles.size()); 786 ASSERT_TRUE(handle2 == handles[0]); 787 } 788 } 789 790 TEST_F(SyncableDirectoryTest, TestGetUnappliedUpdates) { 791 Directory::UnappliedUpdateMetaHandles handles; 792 int64 handle1, handle2; 793 { 794 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 795 796 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles); 797 ASSERT_TRUE(0 == handles.size()); 798 799 MutableEntry e1(&trans, CREATE, trans.root_id(), "abba"); 800 ASSERT_TRUE(e1.good()); 801 handle1 = e1.Get(META_HANDLE); 802 e1.Put(IS_UNAPPLIED_UPDATE, false); 803 e1.Put(BASE_VERSION, 1); 804 e1.Put(ID, TestIdFactory::FromNumber(101)); 805 e1.Put(IS_DIR, true); 806 807 MutableEntry e2(&trans, CREATE, e1.Get(ID), "bread"); 808 ASSERT_TRUE(e2.good()); 809 handle2 = e2.Get(META_HANDLE); 810 e2.Put(IS_UNAPPLIED_UPDATE, false); 811 e2.Put(BASE_VERSION, 1); 812 e2.Put(ID, TestIdFactory::FromNumber(102)); 813 } 814 dir_->SaveChanges(); 815 { 816 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 817 818 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles); 819 ASSERT_TRUE(0 == handles.size()); 820 821 MutableEntry e3(&trans, GET_BY_HANDLE, handle1); 822 ASSERT_TRUE(e3.good()); 823 e3.Put(IS_UNAPPLIED_UPDATE, true); 824 } 825 dir_->SaveChanges(); 826 { 827 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 828 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles); 829 ASSERT_TRUE(1 == handles.size()); 830 ASSERT_TRUE(handle1 == handles[0]); 831 832 MutableEntry e4(&trans, GET_BY_HANDLE, handle2); 833 ASSERT_TRUE(e4.good()); 834 e4.Put(IS_UNAPPLIED_UPDATE, true); 835 } 836 dir_->SaveChanges(); 837 { 838 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 839 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles); 840 ASSERT_TRUE(2 == handles.size()); 841 if (handle1 == handles[0]) { 842 ASSERT_TRUE(handle2 == handles[1]); 843 } else { 844 ASSERT_TRUE(handle2 == handles[0]); 845 ASSERT_TRUE(handle1 == handles[1]); 846 } 847 848 MutableEntry e5(&trans, GET_BY_HANDLE, handle1); 849 ASSERT_TRUE(e5.good()); 850 e5.Put(IS_UNAPPLIED_UPDATE, false); 851 } 852 dir_->SaveChanges(); 853 { 854 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 855 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles); 856 ASSERT_TRUE(1 == handles.size()); 857 ASSERT_TRUE(handle2 == handles[0]); 858 } 859 } 860 861 862 TEST_F(SyncableDirectoryTest, DeleteBug_531383) { 863 // Try to evoke a check failure... 864 TestIdFactory id_factory; 865 int64 grandchild_handle, twin_handle; 866 { 867 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__); 868 MutableEntry parent(&wtrans, CREATE, id_factory.root(), "Bob"); 869 ASSERT_TRUE(parent.good()); 870 parent.Put(IS_DIR, true); 871 parent.Put(ID, id_factory.NewServerId()); 872 parent.Put(BASE_VERSION, 1); 873 MutableEntry child(&wtrans, CREATE, parent.Get(ID), "Bob"); 874 ASSERT_TRUE(child.good()); 875 child.Put(IS_DIR, true); 876 child.Put(ID, id_factory.NewServerId()); 877 child.Put(BASE_VERSION, 1); 878 MutableEntry grandchild(&wtrans, CREATE, child.Get(ID), "Bob"); 879 ASSERT_TRUE(grandchild.good()); 880 grandchild.Put(ID, id_factory.NewServerId()); 881 grandchild.Put(BASE_VERSION, 1); 882 ASSERT_TRUE(grandchild.Put(IS_DEL, true)); 883 MutableEntry twin(&wtrans, CREATE, child.Get(ID), "Bob"); 884 ASSERT_TRUE(twin.good()); 885 ASSERT_TRUE(twin.Put(IS_DEL, true)); 886 ASSERT_TRUE(grandchild.Put(IS_DEL, false)); 887 888 grandchild_handle = grandchild.Get(META_HANDLE); 889 twin_handle = twin.Get(META_HANDLE); 890 } 891 dir_->SaveChanges(); 892 { 893 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__); 894 MutableEntry grandchild(&wtrans, GET_BY_HANDLE, grandchild_handle); 895 grandchild.Put(IS_DEL, true); // Used to CHECK fail here. 896 } 897 } 898 899 static inline bool IsLegalNewParent(const Entry& a, const Entry& b) { 900 return IsLegalNewParent(a.trans(), a.Get(ID), b.Get(ID)); 901 } 902 903 TEST_F(SyncableDirectoryTest, TestIsLegalNewParent) { 904 TestIdFactory id_factory; 905 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__); 906 Entry root(&wtrans, GET_BY_ID, id_factory.root()); 907 ASSERT_TRUE(root.good()); 908 MutableEntry parent(&wtrans, CREATE, root.Get(ID), "Bob"); 909 ASSERT_TRUE(parent.good()); 910 parent.Put(IS_DIR, true); 911 parent.Put(ID, id_factory.NewServerId()); 912 parent.Put(BASE_VERSION, 1); 913 MutableEntry child(&wtrans, CREATE, parent.Get(ID), "Bob"); 914 ASSERT_TRUE(child.good()); 915 child.Put(IS_DIR, true); 916 child.Put(ID, id_factory.NewServerId()); 917 child.Put(BASE_VERSION, 1); 918 MutableEntry grandchild(&wtrans, CREATE, child.Get(ID), "Bob"); 919 ASSERT_TRUE(grandchild.good()); 920 grandchild.Put(ID, id_factory.NewServerId()); 921 grandchild.Put(BASE_VERSION, 1); 922 923 MutableEntry parent2(&wtrans, CREATE, root.Get(ID), "Pete"); 924 ASSERT_TRUE(parent2.good()); 925 parent2.Put(IS_DIR, true); 926 parent2.Put(ID, id_factory.NewServerId()); 927 parent2.Put(BASE_VERSION, 1); 928 MutableEntry child2(&wtrans, CREATE, parent2.Get(ID), "Pete"); 929 ASSERT_TRUE(child2.good()); 930 child2.Put(IS_DIR, true); 931 child2.Put(ID, id_factory.NewServerId()); 932 child2.Put(BASE_VERSION, 1); 933 MutableEntry grandchild2(&wtrans, CREATE, child2.Get(ID), "Pete"); 934 ASSERT_TRUE(grandchild2.good()); 935 grandchild2.Put(ID, id_factory.NewServerId()); 936 grandchild2.Put(BASE_VERSION, 1); 937 // resulting tree 938 // root 939 // / | 940 // parent parent2 941 // | | 942 // child child2 943 // | | 944 // grandchild grandchild2 945 ASSERT_TRUE(IsLegalNewParent(child, root)); 946 ASSERT_TRUE(IsLegalNewParent(child, parent)); 947 ASSERT_FALSE(IsLegalNewParent(child, child)); 948 ASSERT_FALSE(IsLegalNewParent(child, grandchild)); 949 ASSERT_TRUE(IsLegalNewParent(child, parent2)); 950 ASSERT_TRUE(IsLegalNewParent(child, grandchild2)); 951 ASSERT_FALSE(IsLegalNewParent(parent, grandchild)); 952 ASSERT_FALSE(IsLegalNewParent(root, grandchild)); 953 ASSERT_FALSE(IsLegalNewParent(parent, grandchild)); 954 } 955 956 TEST_F(SyncableDirectoryTest, TestEntryIsInFolder) { 957 // Create a subdir and an entry. 958 int64 entry_handle; 959 syncable::Id folder_id; 960 syncable::Id entry_id; 961 std::string entry_name = "entry"; 962 963 { 964 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 965 MutableEntry folder(&trans, CREATE, trans.root_id(), "folder"); 966 ASSERT_TRUE(folder.good()); 967 EXPECT_TRUE(folder.Put(IS_DIR, true)); 968 EXPECT_TRUE(folder.Put(IS_UNSYNCED, true)); 969 folder_id = folder.Get(ID); 970 971 MutableEntry entry(&trans, CREATE, folder.Get(ID), entry_name); 972 ASSERT_TRUE(entry.good()); 973 entry_handle = entry.Get(META_HANDLE); 974 entry.Put(IS_UNSYNCED, true); 975 entry_id = entry.Get(ID); 976 } 977 978 // Make sure we can find the entry in the folder. 979 { 980 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 981 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), entry_name)); 982 EXPECT_EQ(1, CountEntriesWithName(&trans, folder_id, entry_name)); 983 984 Entry entry(&trans, GET_BY_ID, entry_id); 985 ASSERT_TRUE(entry.good()); 986 EXPECT_EQ(entry_handle, entry.Get(META_HANDLE)); 987 EXPECT_TRUE(entry.Get(NON_UNIQUE_NAME) == entry_name); 988 EXPECT_TRUE(entry.Get(PARENT_ID) == folder_id); 989 } 990 } 991 992 TEST_F(SyncableDirectoryTest, TestParentIdIndexUpdate) { 993 std::string child_name = "child"; 994 995 WriteTransaction wt(dir_.get(), UNITTEST, __FILE__, __LINE__); 996 MutableEntry parent_folder(&wt, CREATE, wt.root_id(), "folder1"); 997 parent_folder.Put(IS_UNSYNCED, true); 998 EXPECT_TRUE(parent_folder.Put(IS_DIR, true)); 999 1000 MutableEntry parent_folder2(&wt, CREATE, wt.root_id(), "folder2"); 1001 parent_folder2.Put(IS_UNSYNCED, true); 1002 EXPECT_TRUE(parent_folder2.Put(IS_DIR, true)); 1003 1004 MutableEntry child(&wt, CREATE, parent_folder.Get(ID), child_name); 1005 EXPECT_TRUE(child.Put(IS_DIR, true)); 1006 child.Put(IS_UNSYNCED, true); 1007 1008 ASSERT_TRUE(child.good()); 1009 1010 EXPECT_EQ(0, CountEntriesWithName(&wt, wt.root_id(), child_name)); 1011 EXPECT_EQ(parent_folder.Get(ID), child.Get(PARENT_ID)); 1012 EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder.Get(ID), child_name)); 1013 EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder2.Get(ID), child_name)); 1014 child.Put(PARENT_ID, parent_folder2.Get(ID)); 1015 EXPECT_EQ(parent_folder2.Get(ID), child.Get(PARENT_ID)); 1016 EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder.Get(ID), child_name)); 1017 EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder2.Get(ID), child_name)); 1018 } 1019 1020 TEST_F(SyncableDirectoryTest, TestNoReindexDeletedItems) { 1021 std::string folder_name = "folder"; 1022 std::string new_name = "new_name"; 1023 1024 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1025 MutableEntry folder(&trans, CREATE, trans.root_id(), folder_name); 1026 ASSERT_TRUE(folder.good()); 1027 ASSERT_TRUE(folder.Put(IS_DIR, true)); 1028 ASSERT_TRUE(folder.Put(IS_DEL, true)); 1029 1030 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name)); 1031 1032 MutableEntry deleted(&trans, GET_BY_ID, folder.Get(ID)); 1033 ASSERT_TRUE(deleted.good()); 1034 ASSERT_TRUE(deleted.Put(PARENT_ID, trans.root_id())); 1035 ASSERT_TRUE(deleted.Put(NON_UNIQUE_NAME, new_name)); 1036 1037 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name)); 1038 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), new_name)); 1039 } 1040 1041 TEST_F(SyncableDirectoryTest, TestCaseChangeRename) { 1042 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1043 MutableEntry folder(&trans, CREATE, trans.root_id(), "CaseChange"); 1044 ASSERT_TRUE(folder.good()); 1045 EXPECT_TRUE(folder.Put(PARENT_ID, trans.root_id())); 1046 EXPECT_TRUE(folder.Put(NON_UNIQUE_NAME, "CASECHANGE")); 1047 EXPECT_TRUE(folder.Put(IS_DEL, true)); 1048 } 1049 1050 TEST_F(SyncableDirectoryTest, TestShareInfo) { 1051 dir_->set_initial_sync_ended_for_type(AUTOFILL, true); 1052 dir_->set_store_birthday("Jan 31st"); 1053 dir_->SetNotificationState("notification_state"); 1054 { 1055 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1056 EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL)); 1057 EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS)); 1058 EXPECT_EQ("Jan 31st", dir_->store_birthday()); 1059 EXPECT_EQ("notification_state", dir_->GetAndClearNotificationState()); 1060 EXPECT_EQ("", dir_->GetAndClearNotificationState()); 1061 } 1062 dir_->set_store_birthday("April 10th"); 1063 dir_->SetNotificationState("notification_state2"); 1064 dir_->SaveChanges(); 1065 { 1066 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1067 EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL)); 1068 EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS)); 1069 EXPECT_EQ("April 10th", dir_->store_birthday()); 1070 EXPECT_EQ("notification_state2", dir_->GetAndClearNotificationState()); 1071 EXPECT_EQ("", dir_->GetAndClearNotificationState()); 1072 } 1073 dir_->SetNotificationState("notification_state2"); 1074 // Restore the directory from disk. Make sure that nothing's changed. 1075 SaveAndReloadDir(); 1076 { 1077 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1078 EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL)); 1079 EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS)); 1080 EXPECT_EQ("April 10th", dir_->store_birthday()); 1081 EXPECT_EQ("notification_state2", dir_->GetAndClearNotificationState()); 1082 EXPECT_EQ("", dir_->GetAndClearNotificationState()); 1083 } 1084 } 1085 1086 TEST_F(SyncableDirectoryTest, TestSimpleFieldsPreservedDuringSaveChanges) { 1087 Id update_id = TestIdFactory::FromNumber(1); 1088 Id create_id; 1089 EntryKernel create_pre_save, update_pre_save; 1090 EntryKernel create_post_save, update_post_save; 1091 std::string create_name = "Create"; 1092 1093 { 1094 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1095 MutableEntry create(&trans, CREATE, trans.root_id(), create_name); 1096 MutableEntry update(&trans, CREATE_NEW_UPDATE_ITEM, update_id); 1097 create.Put(IS_UNSYNCED, true); 1098 update.Put(IS_UNAPPLIED_UPDATE, true); 1099 sync_pb::EntitySpecifics specifics; 1100 specifics.MutableExtension(sync_pb::bookmark)->set_favicon("PNG"); 1101 specifics.MutableExtension(sync_pb::bookmark)->set_url("http://nowhere"); 1102 create.Put(SPECIFICS, specifics); 1103 create_pre_save = create.GetKernelCopy(); 1104 update_pre_save = update.GetKernelCopy(); 1105 create_id = create.Get(ID); 1106 } 1107 1108 dir_->SaveChanges(); 1109 dir_.reset(new Directory()); 1110 ASSERT_TRUE(dir_.get()); 1111 ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName)); 1112 ASSERT_TRUE(dir_->good()); 1113 1114 { 1115 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1116 Entry create(&trans, GET_BY_ID, create_id); 1117 EXPECT_EQ(1, CountEntriesWithName(&trans, trans.root_id(), create_name)); 1118 Entry update(&trans, GET_BY_ID, update_id); 1119 create_post_save = create.GetKernelCopy(); 1120 update_post_save = update.GetKernelCopy(); 1121 } 1122 int i = BEGIN_FIELDS; 1123 for ( ; i < INT64_FIELDS_END ; ++i) { 1124 EXPECT_EQ(create_pre_save.ref((Int64Field)i), 1125 create_post_save.ref((Int64Field)i)) 1126 << "int64 field #" << i << " changed during save/load"; 1127 EXPECT_EQ(update_pre_save.ref((Int64Field)i), 1128 update_post_save.ref((Int64Field)i)) 1129 << "int64 field #" << i << " changed during save/load"; 1130 } 1131 for ( ; i < ID_FIELDS_END ; ++i) { 1132 EXPECT_EQ(create_pre_save.ref((IdField)i), 1133 create_post_save.ref((IdField)i)) 1134 << "id field #" << i << " changed during save/load"; 1135 EXPECT_EQ(update_pre_save.ref((IdField)i), 1136 update_pre_save.ref((IdField)i)) 1137 << "id field #" << i << " changed during save/load"; 1138 } 1139 for ( ; i < BIT_FIELDS_END ; ++i) { 1140 EXPECT_EQ(create_pre_save.ref((BitField)i), 1141 create_post_save.ref((BitField)i)) 1142 << "Bit field #" << i << " changed during save/load"; 1143 EXPECT_EQ(update_pre_save.ref((BitField)i), 1144 update_post_save.ref((BitField)i)) 1145 << "Bit field #" << i << " changed during save/load"; 1146 } 1147 for ( ; i < STRING_FIELDS_END ; ++i) { 1148 EXPECT_EQ(create_pre_save.ref((StringField)i), 1149 create_post_save.ref((StringField)i)) 1150 << "String field #" << i << " changed during save/load"; 1151 EXPECT_EQ(update_pre_save.ref((StringField)i), 1152 update_post_save.ref((StringField)i)) 1153 << "String field #" << i << " changed during save/load"; 1154 } 1155 for ( ; i < PROTO_FIELDS_END; ++i) { 1156 EXPECT_EQ(create_pre_save.ref((ProtoField)i).SerializeAsString(), 1157 create_post_save.ref((ProtoField)i).SerializeAsString()) 1158 << "Blob field #" << i << " changed during save/load"; 1159 EXPECT_EQ(update_pre_save.ref((ProtoField)i).SerializeAsString(), 1160 update_post_save.ref((ProtoField)i).SerializeAsString()) 1161 << "Blob field #" << i << " changed during save/load"; 1162 } 1163 } 1164 1165 TEST_F(SyncableDirectoryTest, TestSaveChangesFailure) { 1166 int64 handle1 = 0; 1167 // Set up an item using a regular, saveable directory. 1168 { 1169 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1170 1171 MutableEntry e1(&trans, CREATE, trans.root_id(), "aguilera"); 1172 ASSERT_TRUE(e1.good()); 1173 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1174 handle1 = e1.Get(META_HANDLE); 1175 e1.Put(BASE_VERSION, 1); 1176 e1.Put(IS_DIR, true); 1177 e1.Put(ID, TestIdFactory::FromNumber(101)); 1178 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1179 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1180 } 1181 ASSERT_TRUE(dir_->SaveChanges()); 1182 1183 // Make sure the item is no longer dirty after saving, 1184 // and make a modification. 1185 { 1186 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1187 1188 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1); 1189 ASSERT_TRUE(aguilera.good()); 1190 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 1191 EXPECT_EQ(aguilera.Get(NON_UNIQUE_NAME), "aguilera"); 1192 aguilera.Put(NON_UNIQUE_NAME, "overwritten"); 1193 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty()); 1194 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1195 } 1196 ASSERT_TRUE(dir_->SaveChanges()); 1197 1198 // Now do some operations using a directory for which SaveChanges will 1199 // always fail. 1200 dir_.reset(new TestUnsaveableDirectory()); 1201 ASSERT_TRUE(dir_.get()); 1202 ASSERT_TRUE(OPENED == dir_->Open(FilePath(kFilePath), kName)); 1203 ASSERT_TRUE(dir_->good()); 1204 int64 handle2 = 0; 1205 { 1206 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1207 1208 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1); 1209 ASSERT_TRUE(aguilera.good()); 1210 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 1211 EXPECT_EQ(aguilera.Get(NON_UNIQUE_NAME), "overwritten"); 1212 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 1213 EXPECT_FALSE(IsInDirtyMetahandles(handle1)); 1214 aguilera.Put(NON_UNIQUE_NAME, "christina"); 1215 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty()); 1216 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1217 1218 // New item. 1219 MutableEntry kids_on_block(&trans, CREATE, trans.root_id(), "kids"); 1220 ASSERT_TRUE(kids_on_block.good()); 1221 handle2 = kids_on_block.Get(META_HANDLE); 1222 kids_on_block.Put(BASE_VERSION, 1); 1223 kids_on_block.Put(IS_DIR, true); 1224 kids_on_block.Put(ID, TestIdFactory::FromNumber(102)); 1225 EXPECT_TRUE(kids_on_block.GetKernelCopy().is_dirty()); 1226 EXPECT_TRUE(IsInDirtyMetahandles(handle2)); 1227 } 1228 1229 // We are using an unsaveable directory, so this can't succeed. However, 1230 // the HandleSaveChangesFailure code path should have been triggered. 1231 ASSERT_FALSE(dir_->SaveChanges()); 1232 1233 // Make sure things were rolled back and the world is as it was before call. 1234 { 1235 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1236 Entry e1(&trans, GET_BY_HANDLE, handle1); 1237 ASSERT_TRUE(e1.good()); 1238 EntryKernel aguilera = e1.GetKernelCopy(); 1239 Entry kids(&trans, GET_BY_HANDLE, handle2); 1240 ASSERT_TRUE(kids.good()); 1241 EXPECT_TRUE(kids.GetKernelCopy().is_dirty()); 1242 EXPECT_TRUE(IsInDirtyMetahandles(handle2)); 1243 EXPECT_TRUE(aguilera.is_dirty()); 1244 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1245 } 1246 } 1247 1248 TEST_F(SyncableDirectoryTest, TestSaveChangesFailureWithPurge) { 1249 int64 handle1 = 0; 1250 // Set up an item using a regular, saveable directory. 1251 { 1252 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1253 1254 MutableEntry e1(&trans, CREATE, trans.root_id(), "aguilera"); 1255 ASSERT_TRUE(e1.good()); 1256 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1257 handle1 = e1.Get(META_HANDLE); 1258 e1.Put(BASE_VERSION, 1); 1259 e1.Put(IS_DIR, true); 1260 e1.Put(ID, TestIdFactory::FromNumber(101)); 1261 sync_pb::EntitySpecifics bookmark_specs; 1262 AddDefaultExtensionValue(BOOKMARKS, &bookmark_specs); 1263 e1.Put(SPECIFICS, bookmark_specs); 1264 e1.Put(SERVER_SPECIFICS, bookmark_specs); 1265 e1.Put(ID, TestIdFactory::FromNumber(101)); 1266 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1267 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1268 } 1269 ASSERT_TRUE(dir_->SaveChanges()); 1270 1271 // Now do some operations using a directory for which SaveChanges will 1272 // always fail. 1273 dir_.reset(new TestUnsaveableDirectory()); 1274 ASSERT_TRUE(dir_.get()); 1275 ASSERT_TRUE(OPENED == dir_->Open(FilePath(kFilePath), kName)); 1276 ASSERT_TRUE(dir_->good()); 1277 1278 ModelTypeSet set; 1279 set.insert(BOOKMARKS); 1280 dir_->PurgeEntriesWithTypeIn(set); 1281 EXPECT_TRUE(IsInMetahandlesToPurge(handle1)); 1282 ASSERT_FALSE(dir_->SaveChanges()); 1283 EXPECT_TRUE(IsInMetahandlesToPurge(handle1)); 1284 } 1285 1286 // Create items of each model type, and check that GetModelType and 1287 // GetServerModelType return the right value. 1288 TEST_F(SyncableDirectoryTest, GetModelType) { 1289 TestIdFactory id_factory; 1290 for (int i = 0; i < MODEL_TYPE_COUNT; ++i) { 1291 ModelType datatype = ModelTypeFromInt(i); 1292 SCOPED_TRACE(testing::Message("Testing model type ") << datatype); 1293 switch (datatype) { 1294 case UNSPECIFIED: 1295 case TOP_LEVEL_FOLDER: 1296 continue; // Datatype isn't a function of Specifics. 1297 default: 1298 break; 1299 } 1300 sync_pb::EntitySpecifics specifics; 1301 AddDefaultExtensionValue(datatype, &specifics); 1302 1303 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1304 1305 MutableEntry folder(&trans, CREATE, trans.root_id(), "Folder"); 1306 ASSERT_TRUE(folder.good()); 1307 folder.Put(ID, id_factory.NewServerId()); 1308 folder.Put(SPECIFICS, specifics); 1309 folder.Put(BASE_VERSION, 1); 1310 folder.Put(IS_DIR, true); 1311 folder.Put(IS_DEL, false); 1312 ASSERT_EQ(datatype, folder.GetModelType()); 1313 1314 MutableEntry item(&trans, CREATE, trans.root_id(), "Item"); 1315 ASSERT_TRUE(item.good()); 1316 item.Put(ID, id_factory.NewServerId()); 1317 item.Put(SPECIFICS, specifics); 1318 item.Put(BASE_VERSION, 1); 1319 item.Put(IS_DIR, false); 1320 item.Put(IS_DEL, false); 1321 ASSERT_EQ(datatype, item.GetModelType()); 1322 1323 // It's critical that deletion records retain their datatype, so that 1324 // they can be dispatched to the appropriate change processor. 1325 MutableEntry deleted_item(&trans, CREATE, trans.root_id(), "Deleted Item"); 1326 ASSERT_TRUE(item.good()); 1327 deleted_item.Put(ID, id_factory.NewServerId()); 1328 deleted_item.Put(SPECIFICS, specifics); 1329 deleted_item.Put(BASE_VERSION, 1); 1330 deleted_item.Put(IS_DIR, false); 1331 deleted_item.Put(IS_DEL, true); 1332 ASSERT_EQ(datatype, deleted_item.GetModelType()); 1333 1334 MutableEntry server_folder(&trans, CREATE_NEW_UPDATE_ITEM, 1335 id_factory.NewServerId()); 1336 ASSERT_TRUE(server_folder.good()); 1337 server_folder.Put(SERVER_SPECIFICS, specifics); 1338 server_folder.Put(BASE_VERSION, 1); 1339 server_folder.Put(SERVER_IS_DIR, true); 1340 server_folder.Put(SERVER_IS_DEL, false); 1341 ASSERT_EQ(datatype, server_folder.GetServerModelType()); 1342 1343 MutableEntry server_item(&trans, CREATE_NEW_UPDATE_ITEM, 1344 id_factory.NewServerId()); 1345 ASSERT_TRUE(server_item.good()); 1346 server_item.Put(SERVER_SPECIFICS, specifics); 1347 server_item.Put(BASE_VERSION, 1); 1348 server_item.Put(SERVER_IS_DIR, false); 1349 server_item.Put(SERVER_IS_DEL, false); 1350 ASSERT_EQ(datatype, server_item.GetServerModelType()); 1351 1352 browser_sync::SyncEntity folder_entity; 1353 folder_entity.set_id(id_factory.NewServerId()); 1354 folder_entity.set_deleted(false); 1355 folder_entity.set_folder(true); 1356 folder_entity.mutable_specifics()->CopyFrom(specifics); 1357 ASSERT_EQ(datatype, folder_entity.GetModelType()); 1358 1359 browser_sync::SyncEntity item_entity; 1360 item_entity.set_id(id_factory.NewServerId()); 1361 item_entity.set_deleted(false); 1362 item_entity.set_folder(false); 1363 item_entity.mutable_specifics()->CopyFrom(specifics); 1364 ASSERT_EQ(datatype, item_entity.GetModelType()); 1365 } 1366 } 1367 1368 } // namespace 1369 1370 void SyncableDirectoryTest::ValidateEntry(BaseTransaction* trans, 1371 int64 id, 1372 bool check_name, 1373 const std::string& name, 1374 int64 base_version, 1375 int64 server_version, 1376 bool is_del) { 1377 Entry e(trans, GET_BY_ID, TestIdFactory::FromNumber(id)); 1378 ASSERT_TRUE(e.good()); 1379 if (check_name) 1380 ASSERT_TRUE(name == e.Get(NON_UNIQUE_NAME)); 1381 ASSERT_TRUE(base_version == e.Get(BASE_VERSION)); 1382 ASSERT_TRUE(server_version == e.Get(SERVER_VERSION)); 1383 ASSERT_TRUE(is_del == e.Get(IS_DEL)); 1384 } 1385 1386 namespace { 1387 1388 TEST(SyncableDirectoryManager, TestFileRelease) { 1389 DirectoryManager dm(FilePath(FILE_PATH_LITERAL("."))); 1390 ASSERT_TRUE(dm.Open("ScopeTest")); 1391 { 1392 ScopedDirLookup(&dm, "ScopeTest"); 1393 } 1394 dm.Close("ScopeTest"); 1395 ASSERT_TRUE(file_util::Delete(dm.GetSyncDataDatabasePath(), true)); 1396 } 1397 1398 class ThreadOpenTestDelegate : public base::PlatformThread::Delegate { 1399 public: 1400 explicit ThreadOpenTestDelegate(DirectoryManager* dm) 1401 : directory_manager_(dm) {} 1402 DirectoryManager* const directory_manager_; 1403 1404 private: 1405 // PlatformThread::Delegate methods: 1406 virtual void ThreadMain() { 1407 CHECK(directory_manager_->Open("Open")); 1408 } 1409 1410 DISALLOW_COPY_AND_ASSIGN(ThreadOpenTestDelegate); 1411 }; 1412 1413 TEST(SyncableDirectoryManager, ThreadOpenTest) { 1414 DirectoryManager dm(FilePath(FILE_PATH_LITERAL("."))); 1415 base::PlatformThreadHandle thread_handle; 1416 ThreadOpenTestDelegate test_delegate(&dm); 1417 ASSERT_TRUE(base::PlatformThread::Create(0, &test_delegate, &thread_handle)); 1418 base::PlatformThread::Join(thread_handle); 1419 { 1420 ScopedDirLookup dir(&dm, "Open"); 1421 ASSERT_TRUE(dir.good()); 1422 } 1423 dm.Close("Open"); 1424 ScopedDirLookup dir(&dm, "Open"); 1425 ASSERT_FALSE(dir.good()); 1426 } 1427 1428 struct Step { 1429 Step() : condvar(&mutex), number(0) {} 1430 1431 base::Lock mutex; 1432 base::ConditionVariable condvar; 1433 int number; 1434 int64 metahandle; 1435 }; 1436 1437 class ThreadBugDelegate : public base::PlatformThread::Delegate { 1438 public: 1439 // a role is 0 or 1, meaning this thread does the odd or event steps. 1440 ThreadBugDelegate(int role, Step* step, DirectoryManager* dirman) 1441 : role_(role), step_(step), directory_manager_(dirman) {} 1442 1443 protected: 1444 const int role_; 1445 Step* const step_; 1446 DirectoryManager* const directory_manager_; 1447 1448 // PlatformThread::Delegate methods: 1449 virtual void ThreadMain() { 1450 const std::string dirname = "ThreadBug1"; 1451 base::AutoLock scoped_lock(step_->mutex); 1452 1453 while (step_->number < 3) { 1454 while (step_->number % 2 != role_) { 1455 step_->condvar.Wait(); 1456 } 1457 switch (step_->number) { 1458 case 0: 1459 directory_manager_->Open(dirname); 1460 break; 1461 case 1: 1462 { 1463 directory_manager_->Close(dirname); 1464 directory_manager_->Open(dirname); 1465 ScopedDirLookup dir(directory_manager_, dirname); 1466 CHECK(dir.good()); 1467 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); 1468 MutableEntry me(&trans, CREATE, trans.root_id(), "Jeff"); 1469 step_->metahandle = me.Get(META_HANDLE); 1470 me.Put(IS_UNSYNCED, true); 1471 } 1472 break; 1473 case 2: 1474 { 1475 ScopedDirLookup dir(directory_manager_, dirname); 1476 CHECK(dir.good()); 1477 ReadTransaction trans(dir, __FILE__, __LINE__); 1478 Entry e(&trans, GET_BY_HANDLE, step_->metahandle); 1479 CHECK(e.good()); // Failed due to ThreadBug1 1480 } 1481 directory_manager_->Close(dirname); 1482 break; 1483 } 1484 step_->number += 1; 1485 step_->condvar.Signal(); 1486 } 1487 } 1488 1489 DISALLOW_COPY_AND_ASSIGN(ThreadBugDelegate); 1490 }; 1491 1492 TEST(SyncableDirectoryManager, ThreadBug1) { 1493 Step step; 1494 step.number = 0; 1495 DirectoryManager dirman(FilePath(FILE_PATH_LITERAL("."))); 1496 ThreadBugDelegate thread_delegate_1(0, &step, &dirman); 1497 ThreadBugDelegate thread_delegate_2(1, &step, &dirman); 1498 1499 base::PlatformThreadHandle thread_handle_1; 1500 base::PlatformThreadHandle thread_handle_2; 1501 1502 ASSERT_TRUE( 1503 base::PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1)); 1504 ASSERT_TRUE( 1505 base::PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2)); 1506 1507 base::PlatformThread::Join(thread_handle_1); 1508 base::PlatformThread::Join(thread_handle_2); 1509 } 1510 1511 1512 // The in-memory information would get out of sync because a 1513 // directory would be closed and re-opened, and then an old 1514 // Directory::Kernel with stale information would get saved to the db. 1515 class DirectoryKernelStalenessBugDelegate : public ThreadBugDelegate { 1516 public: 1517 DirectoryKernelStalenessBugDelegate(int role, Step* step, 1518 DirectoryManager* dirman) 1519 : ThreadBugDelegate(role, step, dirman) {} 1520 1521 virtual void ThreadMain() { 1522 const char test_bytes[] = "test data"; 1523 const std::string dirname = "DirectoryKernelStalenessBug"; 1524 base::AutoLock scoped_lock(step_->mutex); 1525 const Id jeff_id = TestIdFactory::FromNumber(100); 1526 1527 while (step_->number < 4) { 1528 while (step_->number % 2 != role_) { 1529 step_->condvar.Wait(); 1530 } 1531 switch (step_->number) { 1532 case 0: 1533 { 1534 // Clean up remnants of earlier test runs. 1535 file_util::Delete(directory_manager_->GetSyncDataDatabasePath(), 1536 true); 1537 // Test. 1538 directory_manager_->Open(dirname); 1539 ScopedDirLookup dir(directory_manager_, dirname); 1540 CHECK(dir.good()); 1541 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); 1542 MutableEntry me(&trans, CREATE, trans.root_id(), "Jeff"); 1543 me.Put(BASE_VERSION, 1); 1544 me.Put(ID, jeff_id); 1545 PutDataAsBookmarkFavicon(&trans, &me, test_bytes, 1546 sizeof(test_bytes)); 1547 } 1548 { 1549 ScopedDirLookup dir(directory_manager_, dirname); 1550 CHECK(dir.good()); 1551 dir->SaveChanges(); 1552 } 1553 directory_manager_->Close(dirname); 1554 break; 1555 case 1: 1556 { 1557 directory_manager_->Open(dirname); 1558 ScopedDirLookup dir(directory_manager_, dirname); 1559 CHECK(dir.good()); 1560 } 1561 break; 1562 case 2: 1563 { 1564 ScopedDirLookup dir(directory_manager_, dirname); 1565 CHECK(dir.good()); 1566 } 1567 break; 1568 case 3: 1569 { 1570 ScopedDirLookup dir(directory_manager_, dirname); 1571 CHECK(dir.good()); 1572 ReadTransaction trans(dir, __FILE__, __LINE__); 1573 Entry e(&trans, GET_BY_ID, jeff_id); 1574 ExpectDataFromBookmarkFaviconEquals(&trans, &e, test_bytes, 1575 sizeof(test_bytes)); 1576 } 1577 // Same result as CloseAllDirectories, but more code coverage. 1578 directory_manager_->Close(dirname); 1579 break; 1580 } 1581 step_->number += 1; 1582 step_->condvar.Signal(); 1583 } 1584 } 1585 1586 DISALLOW_COPY_AND_ASSIGN(DirectoryKernelStalenessBugDelegate); 1587 }; 1588 1589 TEST(SyncableDirectoryManager, DirectoryKernelStalenessBug) { 1590 Step step; 1591 1592 DirectoryManager dirman(FilePath(FILE_PATH_LITERAL("."))); 1593 DirectoryKernelStalenessBugDelegate thread_delegate_1(0, &step, &dirman); 1594 DirectoryKernelStalenessBugDelegate thread_delegate_2(1, &step, &dirman); 1595 1596 base::PlatformThreadHandle thread_handle_1; 1597 base::PlatformThreadHandle thread_handle_2; 1598 1599 ASSERT_TRUE( 1600 base::PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1)); 1601 ASSERT_TRUE( 1602 base::PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2)); 1603 1604 base::PlatformThread::Join(thread_handle_1); 1605 base::PlatformThread::Join(thread_handle_2); 1606 } 1607 1608 class StressTransactionsDelegate : public base::PlatformThread::Delegate { 1609 public: 1610 StressTransactionsDelegate(DirectoryManager* dm, 1611 const std::string& dirname, 1612 int thread_number) 1613 : directory_manager_(dm), 1614 dirname_(dirname), 1615 thread_number_(thread_number) {} 1616 1617 private: 1618 DirectoryManager* const directory_manager_; 1619 std::string dirname_; 1620 const int thread_number_; 1621 1622 // PlatformThread::Delegate methods: 1623 virtual void ThreadMain() { 1624 ScopedDirLookup dir(directory_manager_, dirname_); 1625 CHECK(dir.good()); 1626 int entry_count = 0; 1627 std::string path_name; 1628 1629 for (int i = 0; i < 20; ++i) { 1630 const int rand_action = rand() % 10; 1631 if (rand_action < 4 && !path_name.empty()) { 1632 ReadTransaction trans(dir, __FILE__, __LINE__); 1633 CHECK(1 == CountEntriesWithName(&trans, trans.root_id(), path_name)); 1634 base::PlatformThread::Sleep(rand() % 10); 1635 } else { 1636 std::string unique_name = StringPrintf("%d.%d", thread_number_, 1637 entry_count++); 1638 path_name.assign(unique_name.begin(), unique_name.end()); 1639 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); 1640 MutableEntry e(&trans, CREATE, trans.root_id(), path_name); 1641 CHECK(e.good()); 1642 base::PlatformThread::Sleep(rand() % 20); 1643 e.Put(IS_UNSYNCED, true); 1644 if (e.Put(ID, TestIdFactory::FromNumber(rand())) && 1645 e.Get(ID).ServerKnows() && !e.Get(ID).IsRoot()) { 1646 e.Put(BASE_VERSION, 1); 1647 } 1648 } 1649 } 1650 } 1651 1652 DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate); 1653 }; 1654 1655 TEST(SyncableDirectory, StressTransactions) { 1656 DirectoryManager dirman(FilePath(FILE_PATH_LITERAL("."))); 1657 std::string dirname = "stress"; 1658 file_util::Delete(dirman.GetSyncDataDatabasePath(), true); 1659 dirman.Open(dirname); 1660 1661 const int kThreadCount = 7; 1662 base::PlatformThreadHandle threads[kThreadCount]; 1663 scoped_ptr<StressTransactionsDelegate> thread_delegates[kThreadCount]; 1664 1665 for (int i = 0; i < kThreadCount; ++i) { 1666 thread_delegates[i].reset( 1667 new StressTransactionsDelegate(&dirman, dirname, i)); 1668 ASSERT_TRUE(base::PlatformThread::Create( 1669 0, thread_delegates[i].get(), &threads[i])); 1670 } 1671 1672 for (int i = 0; i < kThreadCount; ++i) { 1673 base::PlatformThread::Join(threads[i]); 1674 } 1675 1676 dirman.Close(dirname); 1677 file_util::Delete(dirman.GetSyncDataDatabasePath(), true); 1678 } 1679 1680 class SyncableClientTagTest : public SyncableDirectoryTest { 1681 public: 1682 static const int kBaseVersion = 1; 1683 const char* test_name_; 1684 const char* test_tag_; 1685 1686 SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {} 1687 1688 bool CreateWithDefaultTag(Id id, bool deleted) { 1689 return CreateWithTag(test_tag_, id, deleted); 1690 } 1691 1692 // Attempt to create an entry with a default tag. 1693 bool CreateWithTag(const char* tag, Id id, bool deleted) { 1694 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1695 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), test_name_); 1696 CHECK(me.good()); 1697 me.Put(ID, id); 1698 if (id.ServerKnows()) { 1699 me.Put(BASE_VERSION, kBaseVersion); 1700 } 1701 me.Put(IS_DEL, deleted); 1702 me.Put(IS_UNSYNCED, true); 1703 me.Put(IS_DIR, false); 1704 return me.Put(UNIQUE_CLIENT_TAG, tag); 1705 } 1706 1707 // Verify an entry exists with the default tag. 1708 void VerifyTag(Id id, bool deleted) { 1709 // Should still be present and valid in the client tag index. 1710 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1711 Entry me(&trans, GET_BY_CLIENT_TAG, test_tag_); 1712 CHECK(me.good()); 1713 EXPECT_EQ(me.Get(ID), id); 1714 EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), test_tag_); 1715 EXPECT_EQ(me.Get(IS_DEL), deleted); 1716 EXPECT_EQ(me.Get(IS_UNSYNCED), true); 1717 } 1718 1719 protected: 1720 TestIdFactory factory_; 1721 }; 1722 1723 TEST_F(SyncableClientTagTest, TestClientTagClear) { 1724 Id server_id = factory_.NewServerId(); 1725 EXPECT_TRUE(CreateWithDefaultTag(server_id, false)); 1726 { 1727 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1728 MutableEntry me(&trans, GET_BY_CLIENT_TAG, test_tag_); 1729 EXPECT_TRUE(me.good()); 1730 me.Put(UNIQUE_CLIENT_TAG, ""); 1731 } 1732 { 1733 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1734 Entry by_tag(&trans, GET_BY_CLIENT_TAG, test_tag_); 1735 EXPECT_FALSE(by_tag.good()); 1736 1737 Entry by_id(&trans, GET_BY_ID, server_id); 1738 EXPECT_TRUE(by_id.good()); 1739 EXPECT_TRUE(by_id.Get(UNIQUE_CLIENT_TAG).empty()); 1740 } 1741 } 1742 1743 TEST_F(SyncableClientTagTest, TestClientTagIndexServerId) { 1744 Id server_id = factory_.NewServerId(); 1745 EXPECT_TRUE(CreateWithDefaultTag(server_id, false)); 1746 VerifyTag(server_id, false); 1747 } 1748 1749 TEST_F(SyncableClientTagTest, TestClientTagIndexClientId) { 1750 Id client_id = factory_.NewLocalId(); 1751 EXPECT_TRUE(CreateWithDefaultTag(client_id, false)); 1752 VerifyTag(client_id, false); 1753 } 1754 1755 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexClientId) { 1756 Id client_id = factory_.NewLocalId(); 1757 EXPECT_TRUE(CreateWithDefaultTag(client_id, true)); 1758 VerifyTag(client_id, true); 1759 } 1760 1761 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexServerId) { 1762 Id server_id = factory_.NewServerId(); 1763 EXPECT_TRUE(CreateWithDefaultTag(server_id, true)); 1764 VerifyTag(server_id, true); 1765 } 1766 1767 TEST_F(SyncableClientTagTest, TestClientTagIndexDuplicateServer) { 1768 EXPECT_TRUE(CreateWithDefaultTag(factory_.NewServerId(), true)); 1769 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), true)); 1770 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), false)); 1771 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), false)); 1772 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), true)); 1773 } 1774 1775 } // namespace 1776 } // namespace syncable 1777