syncable_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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#include <string> 6 7#include "base/basictypes.h" 8#include "base/compiler_specific.h" 9#include "base/file_util.h" 10#include "base/files/file_path.h" 11#include "base/files/scoped_temp_dir.h" 12#include "base/location.h" 13#include "base/logging.h" 14#include "base/memory/scoped_ptr.h" 15#include "base/message_loop/message_loop.h" 16#include "base/stl_util.h" 17#include "base/strings/stringprintf.h" 18#include "base/synchronization/condition_variable.h" 19#include "base/test/values_test_util.h" 20#include "base/threading/platform_thread.h" 21#include "base/values.h" 22#include "sync/protocol/bookmark_specifics.pb.h" 23#include "sync/syncable/directory_backing_store.h" 24#include "sync/syncable/directory_change_delegate.h" 25#include "sync/syncable/in_memory_directory_backing_store.h" 26#include "sync/syncable/metahandle_set.h" 27#include "sync/syncable/mutable_entry.h" 28#include "sync/syncable/on_disk_directory_backing_store.h" 29#include "sync/syncable/syncable_proto_util.h" 30#include "sync/syncable/syncable_read_transaction.h" 31#include "sync/syncable/syncable_util.h" 32#include "sync/syncable/syncable_write_transaction.h" 33#include "sync/test/engine/test_id_factory.h" 34#include "sync/test/engine/test_syncable_utils.h" 35#include "sync/test/fake_encryptor.h" 36#include "sync/test/null_directory_change_delegate.h" 37#include "sync/test/null_transaction_observer.h" 38#include "sync/util/test_unrecoverable_error_handler.h" 39#include "testing/gtest/include/gtest/gtest.h" 40 41namespace syncer { 42namespace syncable { 43 44using base::ExpectDictBooleanValue; 45using base::ExpectDictStringValue; 46 47class SyncableKernelTest : public testing::Test {}; 48 49// TODO(akalin): Add unit tests for EntryKernel::ContainsString(). 50 51TEST_F(SyncableKernelTest, ToValue) { 52 EntryKernel kernel; 53 scoped_ptr<base::DictionaryValue> value(kernel.ToValue(NULL)); 54 if (value) { 55 // Not much to check without repeating the ToValue() code. 56 EXPECT_TRUE(value->HasKey("isDirty")); 57 // The extra +2 is for "isDirty" and "serverModelType". 58 EXPECT_EQ(BIT_TEMPS_END - BEGIN_FIELDS + 2, 59 static_cast<int>(value->size())); 60 } else { 61 ADD_FAILURE(); 62 } 63} 64 65namespace { 66void PutDataAsBookmarkFavicon(WriteTransaction* wtrans, 67 MutableEntry* e, 68 const char* bytes, 69 size_t bytes_length) { 70 sync_pb::EntitySpecifics specifics; 71 specifics.mutable_bookmark()->set_url("http://demo/"); 72 specifics.mutable_bookmark()->set_favicon(bytes, bytes_length); 73 e->PutSpecifics(specifics); 74} 75 76void ExpectDataFromBookmarkFaviconEquals(BaseTransaction* trans, 77 Entry* e, 78 const char* bytes, 79 size_t bytes_length) { 80 ASSERT_TRUE(e->good()); 81 ASSERT_TRUE(e->GetSpecifics().has_bookmark()); 82 ASSERT_EQ("http://demo/", e->GetSpecifics().bookmark().url()); 83 ASSERT_EQ(std::string(bytes, bytes_length), 84 e->GetSpecifics().bookmark().favicon()); 85} 86} // namespace 87 88class SyncableGeneralTest : public testing::Test { 89 public: 90 static const char kIndexTestName[]; 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 base::MessageLoop message_loop_; 101 base::ScopedTempDir temp_dir_; 102 NullDirectoryChangeDelegate delegate_; 103 FakeEncryptor encryptor_; 104 TestUnrecoverableErrorHandler handler_; 105 base::FilePath db_path_; 106}; 107 108const char SyncableGeneralTest::kIndexTestName[] = "IndexTest"; 109 110TEST_F(SyncableGeneralTest, General) { 111 Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"), 112 &handler_, 113 NULL, 114 NULL, 115 NULL); 116 117 ASSERT_EQ(OPENED, dir.Open( 118 "SimpleTest", &delegate_, NullTransactionObserver())); 119 120 int64 written_metahandle; 121 const Id id = TestIdFactory::FromNumber(99); 122 std::string name = "Jeff"; 123 // Test simple read operations on an empty DB. 124 { 125 ReadTransaction rtrans(FROM_HERE, &dir); 126 Entry e(&rtrans, GET_BY_ID, id); 127 ASSERT_FALSE(e.good()); // Hasn't been written yet. 128 129 Directory::Metahandles child_handles; 130 dir.GetChildHandlesById(&rtrans, rtrans.root_id(), &child_handles); 131 EXPECT_TRUE(child_handles.empty()); 132 } 133 134 // Test creating a new meta entry. 135 { 136 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir); 137 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name); 138 ASSERT_TRUE(me.good()); 139 me.PutId(id); 140 me.PutBaseVersion(1); 141 written_metahandle = me.GetMetahandle(); 142 } 143 144 // Test GetChildHandles* after something is now in the DB. 145 // Also check that GET_BY_ID works. 146 { 147 ReadTransaction rtrans(FROM_HERE, &dir); 148 Entry e(&rtrans, GET_BY_ID, id); 149 ASSERT_TRUE(e.good()); 150 151 Directory::Metahandles child_handles; 152 dir.GetChildHandlesById(&rtrans, rtrans.root_id(), &child_handles); 153 EXPECT_EQ(1u, child_handles.size()); 154 155 for (Directory::Metahandles::iterator i = child_handles.begin(); 156 i != child_handles.end(); ++i) { 157 EXPECT_EQ(*i, written_metahandle); 158 } 159 } 160 161 // Test writing data to an entity. Also check that GET_BY_HANDLE works. 162 static const char s[] = "Hello World."; 163 { 164 WriteTransaction trans(FROM_HERE, UNITTEST, &dir); 165 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle); 166 ASSERT_TRUE(e.good()); 167 PutDataAsBookmarkFavicon(&trans, &e, s, sizeof(s)); 168 } 169 170 // Test reading back the contents that we just wrote. 171 { 172 WriteTransaction trans(FROM_HERE, UNITTEST, &dir); 173 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle); 174 ASSERT_TRUE(e.good()); 175 ExpectDataFromBookmarkFaviconEquals(&trans, &e, s, sizeof(s)); 176 } 177 178 // Verify it exists in the folder. 179 { 180 ReadTransaction rtrans(FROM_HERE, &dir); 181 EXPECT_EQ(1, CountEntriesWithName(&rtrans, rtrans.root_id(), name)); 182 } 183 184 // Now delete it. 185 { 186 WriteTransaction trans(FROM_HERE, UNITTEST, &dir); 187 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle); 188 e.PutIsDel(true); 189 190 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name)); 191 } 192 193 dir.SaveChanges(); 194} 195 196TEST_F(SyncableGeneralTest, ChildrenOps) { 197 Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"), 198 &handler_, 199 NULL, 200 NULL, 201 NULL); 202 ASSERT_EQ(OPENED, dir.Open( 203 "SimpleTest", &delegate_, NullTransactionObserver())); 204 205 int64 written_metahandle; 206 const Id id = TestIdFactory::FromNumber(99); 207 std::string name = "Jeff"; 208 { 209 ReadTransaction rtrans(FROM_HERE, &dir); 210 Entry e(&rtrans, GET_BY_ID, id); 211 ASSERT_FALSE(e.good()); // Hasn't been written yet. 212 213 Entry root(&rtrans, GET_BY_ID, rtrans.root_id()); 214 ASSERT_TRUE(root.good()); 215 EXPECT_FALSE(dir.HasChildren(&rtrans, rtrans.root_id())); 216 EXPECT_TRUE(root.GetFirstChildId().IsRoot()); 217 } 218 219 { 220 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir); 221 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name); 222 ASSERT_TRUE(me.good()); 223 me.PutId(id); 224 me.PutBaseVersion(1); 225 written_metahandle = me.GetMetahandle(); 226 } 227 228 // Test children ops after something is now in the DB. 229 { 230 ReadTransaction rtrans(FROM_HERE, &dir); 231 Entry e(&rtrans, GET_BY_ID, id); 232 ASSERT_TRUE(e.good()); 233 234 Entry child(&rtrans, GET_BY_HANDLE, written_metahandle); 235 ASSERT_TRUE(child.good()); 236 237 Entry root(&rtrans, GET_BY_ID, rtrans.root_id()); 238 ASSERT_TRUE(root.good()); 239 EXPECT_TRUE(dir.HasChildren(&rtrans, rtrans.root_id())); 240 EXPECT_EQ(e.GetId(), root.GetFirstChildId()); 241 } 242 243 { 244 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir); 245 MutableEntry me(&wtrans, GET_BY_HANDLE, written_metahandle); 246 ASSERT_TRUE(me.good()); 247 me.PutIsDel(true); 248 } 249 250 // Test children ops after the children have been deleted. 251 { 252 ReadTransaction rtrans(FROM_HERE, &dir); 253 Entry e(&rtrans, GET_BY_ID, id); 254 ASSERT_TRUE(e.good()); 255 256 Entry root(&rtrans, GET_BY_ID, rtrans.root_id()); 257 ASSERT_TRUE(root.good()); 258 EXPECT_FALSE(dir.HasChildren(&rtrans, rtrans.root_id())); 259 EXPECT_TRUE(root.GetFirstChildId().IsRoot()); 260 } 261 262 dir.SaveChanges(); 263} 264 265TEST_F(SyncableGeneralTest, ClientIndexRebuildsProperly) { 266 int64 written_metahandle; 267 TestIdFactory factory; 268 const Id id = factory.NewServerId(); 269 std::string name = "cheesepuffs"; 270 std::string tag = "dietcoke"; 271 272 // Test creating a new meta entry. 273 { 274 Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_), 275 &handler_, 276 NULL, 277 NULL, 278 NULL); 279 ASSERT_EQ(OPENED, dir.Open(kIndexTestName, &delegate_, 280 NullTransactionObserver())); 281 { 282 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir); 283 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name); 284 ASSERT_TRUE(me.good()); 285 me.PutId(id); 286 me.PutBaseVersion(1); 287 me.PutUniqueClientTag(tag); 288 written_metahandle = me.GetMetahandle(); 289 } 290 dir.SaveChanges(); 291 } 292 293 // The DB was closed. Now reopen it. This will cause index regeneration. 294 { 295 Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_), 296 &handler_, 297 NULL, 298 NULL, 299 NULL); 300 ASSERT_EQ(OPENED, dir.Open(kIndexTestName, 301 &delegate_, NullTransactionObserver())); 302 303 ReadTransaction trans(FROM_HERE, &dir); 304 Entry me(&trans, GET_BY_CLIENT_TAG, tag); 305 ASSERT_TRUE(me.good()); 306 EXPECT_EQ(me.GetId(), id); 307 EXPECT_EQ(me.GetBaseVersion(), 1); 308 EXPECT_EQ(me.GetUniqueClientTag(), tag); 309 EXPECT_EQ(me.GetMetahandle(), written_metahandle); 310 } 311} 312 313TEST_F(SyncableGeneralTest, ClientIndexRebuildsDeletedProperly) { 314 TestIdFactory factory; 315 const Id id = factory.NewServerId(); 316 std::string tag = "dietcoke"; 317 318 // Test creating a deleted, unsynced, server meta entry. 319 { 320 Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_), 321 &handler_, 322 NULL, 323 NULL, 324 NULL); 325 ASSERT_EQ(OPENED, dir.Open(kIndexTestName, &delegate_, 326 NullTransactionObserver())); 327 { 328 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir); 329 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "deleted"); 330 ASSERT_TRUE(me.good()); 331 me.PutId(id); 332 me.PutBaseVersion(1); 333 me.PutUniqueClientTag(tag); 334 me.PutIsDel(true); 335 me.PutIsUnsynced(true); // Or it might be purged. 336 } 337 dir.SaveChanges(); 338 } 339 340 // The DB was closed. Now reopen it. This will cause index regeneration. 341 // Should still be present and valid in the client tag index. 342 { 343 Directory dir(new OnDiskDirectoryBackingStore(kIndexTestName, db_path_), 344 &handler_, 345 NULL, 346 NULL, 347 NULL); 348 ASSERT_EQ(OPENED, dir.Open(kIndexTestName, &delegate_, 349 NullTransactionObserver())); 350 351 ReadTransaction trans(FROM_HERE, &dir); 352 Entry me(&trans, GET_BY_CLIENT_TAG, tag); 353 ASSERT_TRUE(me.good()); 354 EXPECT_EQ(me.GetId(), id); 355 EXPECT_EQ(me.GetUniqueClientTag(), tag); 356 EXPECT_TRUE(me.GetIsDel()); 357 EXPECT_TRUE(me.GetIsUnsynced()); 358 } 359} 360 361TEST_F(SyncableGeneralTest, ToValue) { 362 Directory dir(new InMemoryDirectoryBackingStore("SimpleTest"), 363 &handler_, 364 NULL, 365 NULL, 366 NULL); 367 ASSERT_EQ(OPENED, dir.Open( 368 "SimpleTest", &delegate_, NullTransactionObserver())); 369 370 const Id id = TestIdFactory::FromNumber(99); 371 { 372 ReadTransaction rtrans(FROM_HERE, &dir); 373 Entry e(&rtrans, GET_BY_ID, id); 374 EXPECT_FALSE(e.good()); // Hasn't been written yet. 375 376 scoped_ptr<base::DictionaryValue> value(e.ToValue(NULL)); 377 ExpectDictBooleanValue(false, *value, "good"); 378 EXPECT_EQ(1u, value->size()); 379 } 380 381 // Test creating a new meta entry. 382 { 383 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir); 384 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "new"); 385 ASSERT_TRUE(me.good()); 386 me.PutId(id); 387 me.PutBaseVersion(1); 388 389 scoped_ptr<base::DictionaryValue> value(me.ToValue(NULL)); 390 ExpectDictBooleanValue(true, *value, "good"); 391 EXPECT_TRUE(value->HasKey("kernel")); 392 ExpectDictStringValue("Bookmarks", *value, "modelType"); 393 ExpectDictBooleanValue(true, *value, "existsOnClientBecauseNameIsNonEmpty"); 394 ExpectDictBooleanValue(false, *value, "isRoot"); 395 } 396 397 dir.SaveChanges(); 398} 399 400// Test that the bookmark tag generation algorithm remains unchanged. 401TEST_F(SyncableGeneralTest, BookmarkTagTest) { 402 InMemoryDirectoryBackingStore* store = new InMemoryDirectoryBackingStore("x"); 403 404 // The two inputs that form the bookmark tag are the directory's cache_guid 405 // and its next_id value. We don't need to take any action to ensure 406 // consistent next_id values, but we do need to explicitly request that our 407 // InMemoryDirectoryBackingStore always return the same cache_guid. 408 store->request_consistent_cache_guid(); 409 410 Directory dir(store, &handler_, NULL, NULL, NULL); 411 ASSERT_EQ(OPENED, dir.Open("x", &delegate_, NullTransactionObserver())); 412 413 { 414 WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir); 415 MutableEntry bm(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bm"); 416 bm.PutIsUnsynced(true); 417 418 // If this assertion fails, that might indicate that the algorithm used to 419 // generate bookmark tags has been modified. This could have implications 420 // for bookmark ordering. Please make sure you know what you're doing if 421 // you intend to make such a change. 422 ASSERT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", bm.GetUniqueBookmarkTag()); 423 } 424} 425 426// A test fixture for syncable::Directory. Uses an in-memory database to keep 427// the unit tests fast. 428class SyncableDirectoryTest : public testing::Test { 429 protected: 430 base::MessageLoop message_loop_; 431 static const char kName[]; 432 433 virtual void SetUp() { 434 dir_.reset(new Directory(new InMemoryDirectoryBackingStore(kName), 435 &handler_, 436 NULL, 437 NULL, 438 NULL)); 439 ASSERT_TRUE(dir_.get()); 440 ASSERT_EQ(OPENED, dir_->Open(kName, &delegate_, 441 NullTransactionObserver())); 442 ASSERT_TRUE(dir_->good()); 443 } 444 445 virtual void TearDown() { 446 if (dir_) 447 dir_->SaveChanges(); 448 dir_.reset(); 449 } 450 451 void GetAllMetaHandles(BaseTransaction* trans, MetahandleSet* result) { 452 dir_->GetAllMetaHandles(trans, result); 453 } 454 455 bool IsInDirtyMetahandles(int64 metahandle) { 456 return 1 == dir_->kernel_->dirty_metahandles.count(metahandle); 457 } 458 459 bool IsInMetahandlesToPurge(int64 metahandle) { 460 return 1 == dir_->kernel_->metahandles_to_purge.count(metahandle); 461 } 462 463 void CheckPurgeEntriesWithTypeInSucceeded(ModelTypeSet types_to_purge, 464 bool before_reload) { 465 SCOPED_TRACE(testing::Message("Before reload: ") << before_reload); 466 { 467 ReadTransaction trans(FROM_HERE, dir_.get()); 468 MetahandleSet all_set; 469 dir_->GetAllMetaHandles(&trans, &all_set); 470 EXPECT_EQ(4U, all_set.size()); 471 if (before_reload) 472 EXPECT_EQ(6U, dir_->kernel_->metahandles_to_purge.size()); 473 for (MetahandleSet::iterator iter = all_set.begin(); 474 iter != all_set.end(); ++iter) { 475 Entry e(&trans, GET_BY_HANDLE, *iter); 476 const ModelType local_type = e.GetModelType(); 477 const ModelType server_type = e.GetServerModelType(); 478 479 // Note the dance around incrementing |it|, since we sometimes erase(). 480 if ((IsRealDataType(local_type) && 481 types_to_purge.Has(local_type)) || 482 (IsRealDataType(server_type) && 483 types_to_purge.Has(server_type))) { 484 FAIL() << "Illegal type should have been deleted."; 485 } 486 } 487 } 488 489 for (ModelTypeSet::Iterator it = types_to_purge.First(); 490 it.Good(); it.Inc()) { 491 EXPECT_FALSE(dir_->InitialSyncEndedForType(it.Get())); 492 } 493 EXPECT_FALSE(types_to_purge.Has(BOOKMARKS)); 494 EXPECT_TRUE(dir_->InitialSyncEndedForType(BOOKMARKS)); 495 } 496 497 FakeEncryptor encryptor_; 498 TestUnrecoverableErrorHandler handler_; 499 scoped_ptr<Directory> dir_; 500 NullDirectoryChangeDelegate delegate_; 501 502 // Creates an empty entry and sets the ID field to a default one. 503 void CreateEntry(const std::string& entryname) { 504 CreateEntry(entryname, TestIdFactory::FromNumber(-99)); 505 } 506 507 // Creates an empty entry and sets the ID field to id. 508 void CreateEntry(const std::string& entryname, const int id) { 509 CreateEntry(entryname, TestIdFactory::FromNumber(id)); 510 } 511 void CreateEntry(const std::string& entryname, Id id) { 512 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get()); 513 MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), entryname); 514 ASSERT_TRUE(me.good()); 515 me.PutId(id); 516 me.PutIsUnsynced(true); 517 } 518 519 void ValidateEntry(BaseTransaction* trans, 520 int64 id, 521 bool check_name, 522 const std::string& name, 523 int64 base_version, 524 int64 server_version, 525 bool is_del); 526 527 // When a directory is saved then loaded from disk, it will pass through 528 // DropDeletedEntries(). This will remove some entries from the directory. 529 // This function is intended to simulate that process. 530 // 531 // WARNING: The directory will be deleted by this operation. You should 532 // not have any pointers to the directory (open transactions included) 533 // when you call this. 534 DirOpenResult SimulateSaveAndReloadDir(); 535 536 // This function will close and re-open the directory without saving any 537 // pending changes. This is intended to simulate the recovery from a crash 538 // scenario. The same warnings for SimulateSaveAndReloadDir apply here. 539 DirOpenResult SimulateCrashAndReloadDir(); 540 541 private: 542 // A helper function for Simulate{Save,Crash}AndReloadDir. 543 DirOpenResult ReloadDirImpl(); 544}; 545 546TEST_F(SyncableDirectoryTest, TakeSnapshotGetsMetahandlesToPurge) { 547 const int metas_to_create = 50; 548 MetahandleSet expected_purges; 549 MetahandleSet all_handles; 550 { 551 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 552 for (int i = 0; i < metas_to_create; i++) { 553 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo"); 554 e.PutIsUnsynced(true); 555 sync_pb::EntitySpecifics specs; 556 if (i % 2 == 0) { 557 AddDefaultFieldValue(BOOKMARKS, &specs); 558 expected_purges.insert(e.GetMetahandle()); 559 all_handles.insert(e.GetMetahandle()); 560 } else { 561 AddDefaultFieldValue(PREFERENCES, &specs); 562 all_handles.insert(e.GetMetahandle()); 563 } 564 e.PutSpecifics(specs); 565 e.PutServerSpecifics(specs); 566 } 567 } 568 569 ModelTypeSet to_purge(BOOKMARKS); 570 dir_->PurgeEntriesWithTypeIn(to_purge, ModelTypeSet(), ModelTypeSet()); 571 572 Directory::SaveChangesSnapshot snapshot1; 573 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 574 dir_->TakeSnapshotForSaveChanges(&snapshot1); 575 EXPECT_TRUE(expected_purges == snapshot1.metahandles_to_purge); 576 577 to_purge.Clear(); 578 to_purge.Put(PREFERENCES); 579 dir_->PurgeEntriesWithTypeIn(to_purge, ModelTypeSet(), ModelTypeSet()); 580 581 dir_->HandleSaveChangesFailure(snapshot1); 582 583 Directory::SaveChangesSnapshot snapshot2; 584 dir_->TakeSnapshotForSaveChanges(&snapshot2); 585 EXPECT_TRUE(all_handles == snapshot2.metahandles_to_purge); 586} 587 588TEST_F(SyncableDirectoryTest, TakeSnapshotGetsAllDirtyHandlesTest) { 589 const int metahandles_to_create = 100; 590 std::vector<int64> expected_dirty_metahandles; 591 { 592 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 593 for (int i = 0; i < metahandles_to_create; i++) { 594 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo"); 595 expected_dirty_metahandles.push_back(e.GetMetahandle()); 596 e.PutIsUnsynced(true); 597 } 598 } 599 // Fake SaveChanges() and make sure we got what we expected. 600 { 601 Directory::SaveChangesSnapshot snapshot; 602 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 603 dir_->TakeSnapshotForSaveChanges(&snapshot); 604 // Make sure there's an entry for each new metahandle. Make sure all 605 // entries are marked dirty. 606 ASSERT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size()); 607 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin(); 608 i != snapshot.dirty_metas.end(); ++i) { 609 ASSERT_TRUE((*i)->is_dirty()); 610 } 611 dir_->VacuumAfterSaveChanges(snapshot); 612 } 613 // Put a new value with existing transactions as well as adding new ones. 614 { 615 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 616 std::vector<int64> new_dirty_metahandles; 617 for (std::vector<int64>::const_iterator i = 618 expected_dirty_metahandles.begin(); 619 i != expected_dirty_metahandles.end(); ++i) { 620 // Change existing entries to directories to dirty them. 621 MutableEntry e1(&trans, GET_BY_HANDLE, *i); 622 e1.PutIsDir(true); 623 e1.PutIsUnsynced(true); 624 // Add new entries 625 MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), "bar"); 626 e2.PutIsUnsynced(true); 627 new_dirty_metahandles.push_back(e2.GetMetahandle()); 628 } 629 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(), 630 new_dirty_metahandles.begin(), new_dirty_metahandles.end()); 631 } 632 // Fake SaveChanges() and make sure we got what we expected. 633 { 634 Directory::SaveChangesSnapshot snapshot; 635 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 636 dir_->TakeSnapshotForSaveChanges(&snapshot); 637 // Make sure there's an entry for each new metahandle. Make sure all 638 // entries are marked dirty. 639 EXPECT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size()); 640 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin(); 641 i != snapshot.dirty_metas.end(); ++i) { 642 EXPECT_TRUE((*i)->is_dirty()); 643 } 644 dir_->VacuumAfterSaveChanges(snapshot); 645 } 646} 647 648TEST_F(SyncableDirectoryTest, TakeSnapshotGetsOnlyDirtyHandlesTest) { 649 const int metahandles_to_create = 100; 650 651 // half of 2 * metahandles_to_create 652 const unsigned int number_changed = 100u; 653 std::vector<int64> expected_dirty_metahandles; 654 { 655 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 656 for (int i = 0; i < metahandles_to_create; i++) { 657 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo"); 658 expected_dirty_metahandles.push_back(e.GetMetahandle()); 659 e.PutIsUnsynced(true); 660 } 661 } 662 dir_->SaveChanges(); 663 // Put a new value with existing transactions as well as adding new ones. 664 { 665 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 666 std::vector<int64> new_dirty_metahandles; 667 for (std::vector<int64>::const_iterator i = 668 expected_dirty_metahandles.begin(); 669 i != expected_dirty_metahandles.end(); ++i) { 670 // Change existing entries to directories to dirty them. 671 MutableEntry e1(&trans, GET_BY_HANDLE, *i); 672 ASSERT_TRUE(e1.good()); 673 e1.PutIsDir(true); 674 e1.PutIsUnsynced(true); 675 // Add new entries 676 MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), "bar"); 677 e2.PutIsUnsynced(true); 678 new_dirty_metahandles.push_back(e2.GetMetahandle()); 679 } 680 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(), 681 new_dirty_metahandles.begin(), new_dirty_metahandles.end()); 682 } 683 dir_->SaveChanges(); 684 // Don't make any changes whatsoever and ensure nothing comes back. 685 { 686 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 687 for (std::vector<int64>::const_iterator i = 688 expected_dirty_metahandles.begin(); 689 i != expected_dirty_metahandles.end(); ++i) { 690 MutableEntry e(&trans, GET_BY_HANDLE, *i); 691 ASSERT_TRUE(e.good()); 692 // We aren't doing anything to dirty these entries. 693 } 694 } 695 // Fake SaveChanges() and make sure we got what we expected. 696 { 697 Directory::SaveChangesSnapshot snapshot; 698 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 699 dir_->TakeSnapshotForSaveChanges(&snapshot); 700 // Make sure there are no dirty_metahandles. 701 EXPECT_EQ(0u, snapshot.dirty_metas.size()); 702 dir_->VacuumAfterSaveChanges(snapshot); 703 } 704 { 705 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 706 bool should_change = false; 707 for (std::vector<int64>::const_iterator i = 708 expected_dirty_metahandles.begin(); 709 i != expected_dirty_metahandles.end(); ++i) { 710 // Maybe change entries by flipping IS_DIR. 711 MutableEntry e(&trans, GET_BY_HANDLE, *i); 712 ASSERT_TRUE(e.good()); 713 should_change = !should_change; 714 if (should_change) { 715 bool not_dir = !e.GetIsDir(); 716 e.PutIsDir(not_dir); 717 e.PutIsUnsynced(true); 718 } 719 } 720 } 721 // Fake SaveChanges() and make sure we got what we expected. 722 { 723 Directory::SaveChangesSnapshot snapshot; 724 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 725 dir_->TakeSnapshotForSaveChanges(&snapshot); 726 // Make sure there's an entry for each changed metahandle. Make sure all 727 // entries are marked dirty. 728 EXPECT_EQ(number_changed, snapshot.dirty_metas.size()); 729 for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin(); 730 i != snapshot.dirty_metas.end(); ++i) { 731 EXPECT_TRUE((*i)->is_dirty()); 732 } 733 dir_->VacuumAfterSaveChanges(snapshot); 734 } 735} 736 737// Test delete journals management. 738TEST_F(SyncableDirectoryTest, ManageDeleteJournals) { 739 sync_pb::EntitySpecifics bookmark_specifics; 740 AddDefaultFieldValue(BOOKMARKS, &bookmark_specifics); 741 bookmark_specifics.mutable_bookmark()->set_url("url"); 742 743 Id id1 = TestIdFactory::FromNumber(-1); 744 Id id2 = TestIdFactory::FromNumber(-2); 745 int64 handle1 = 0; 746 int64 handle2 = 0; 747 { 748 // Create two bookmark entries and save in database. 749 CreateEntry("item1", id1); 750 CreateEntry("item2", id2); 751 { 752 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 753 MutableEntry item1(&trans, GET_BY_ID, id1); 754 ASSERT_TRUE(item1.good()); 755 handle1 = item1.GetMetahandle(); 756 item1.PutSpecifics(bookmark_specifics); 757 item1.PutServerSpecifics(bookmark_specifics); 758 MutableEntry item2(&trans, GET_BY_ID, id2); 759 ASSERT_TRUE(item2.good()); 760 handle2 = item2.GetMetahandle(); 761 item2.PutSpecifics(bookmark_specifics); 762 item2.PutServerSpecifics(bookmark_specifics); 763 } 764 ASSERT_EQ(OPENED, SimulateSaveAndReloadDir()); 765 } 766 767 { // Test adding and saving delete journals. 768 DeleteJournal* delete_journal = dir_->delete_journal(); 769 { 770 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 771 EntryKernelSet journal_entries; 772 delete_journal->GetDeleteJournals(&trans, BOOKMARKS, &journal_entries); 773 ASSERT_EQ(0u, journal_entries.size()); 774 775 // Set SERVER_IS_DEL of the entries to true and they should be added to 776 // delete journals. 777 MutableEntry item1(&trans, GET_BY_ID, id1); 778 ASSERT_TRUE(item1.good()); 779 item1.PutServerIsDel(true); 780 MutableEntry item2(&trans, GET_BY_ID, id2); 781 ASSERT_TRUE(item2.good()); 782 item2.PutServerIsDel(true); 783 EntryKernel tmp; 784 tmp.put(ID, id1); 785 EXPECT_TRUE(delete_journal->delete_journals_.count(&tmp)); 786 tmp.put(ID, id2); 787 EXPECT_TRUE(delete_journal->delete_journals_.count(&tmp)); 788 } 789 790 // Save delete journals in database and verify memory clearing. 791 ASSERT_TRUE(dir_->SaveChanges()); 792 { 793 ReadTransaction trans(FROM_HERE, dir_.get()); 794 EXPECT_EQ(0u, delete_journal->GetDeleteJournalSize(&trans)); 795 } 796 ASSERT_EQ(OPENED, SimulateSaveAndReloadDir()); 797 } 798 799 { 800 { 801 // Test reading delete journals from database. 802 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 803 DeleteJournal* delete_journal = dir_->delete_journal(); 804 EntryKernelSet journal_entries; 805 delete_journal->GetDeleteJournals(&trans, BOOKMARKS, &journal_entries); 806 ASSERT_EQ(2u, journal_entries.size()); 807 EntryKernel tmp; 808 tmp.put(META_HANDLE, handle1); 809 EXPECT_TRUE(journal_entries.count(&tmp)); 810 tmp.put(META_HANDLE, handle2); 811 EXPECT_TRUE(journal_entries.count(&tmp)); 812 813 // Purge item2. 814 MetahandleSet to_purge; 815 to_purge.insert(handle2); 816 delete_journal->PurgeDeleteJournals(&trans, to_purge); 817 818 // Verify that item2 is purged from journals in memory and will be 819 // purged from database. 820 tmp.put(ID, id2); 821 EXPECT_FALSE(delete_journal->delete_journals_.count(&tmp)); 822 EXPECT_EQ(1u, delete_journal->delete_journals_to_purge_.size()); 823 EXPECT_TRUE(delete_journal->delete_journals_to_purge_.count(handle2)); 824 } 825 ASSERT_EQ(OPENED, SimulateSaveAndReloadDir()); 826 } 827 828 { 829 { 830 // Verify purged entry is gone in database. 831 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 832 DeleteJournal* delete_journal = dir_->delete_journal(); 833 EntryKernelSet journal_entries; 834 delete_journal->GetDeleteJournals(&trans, BOOKMARKS, &journal_entries); 835 ASSERT_EQ(1u, journal_entries.size()); 836 EntryKernel tmp; 837 tmp.put(ID, id1); 838 tmp.put(META_HANDLE, handle1); 839 EXPECT_TRUE(journal_entries.count(&tmp)); 840 841 // Undelete item1. 842 MutableEntry item1(&trans, GET_BY_ID, id1); 843 ASSERT_TRUE(item1.good()); 844 item1.PutServerIsDel(false); 845 EXPECT_TRUE(delete_journal->delete_journals_.empty()); 846 EXPECT_EQ(1u, delete_journal->delete_journals_to_purge_.size()); 847 EXPECT_TRUE(delete_journal->delete_journals_to_purge_.count(handle1)); 848 } 849 ASSERT_EQ(OPENED, SimulateSaveAndReloadDir()); 850 } 851 852 { 853 // Verify undeleted entry is gone from database. 854 ReadTransaction trans(FROM_HERE, dir_.get()); 855 DeleteJournal* delete_journal = dir_->delete_journal(); 856 ASSERT_EQ(0u, delete_journal->GetDeleteJournalSize(&trans)); 857 } 858} 859 860const char SyncableDirectoryTest::kName[] = "Foo"; 861 862namespace { 863 864TEST_F(SyncableDirectoryTest, TestBasicLookupNonExistantID) { 865 ReadTransaction rtrans(FROM_HERE, dir_.get()); 866 Entry e(&rtrans, GET_BY_ID, TestIdFactory::FromNumber(-99)); 867 ASSERT_FALSE(e.good()); 868} 869 870TEST_F(SyncableDirectoryTest, TestBasicLookupValidID) { 871 CreateEntry("rtc"); 872 ReadTransaction rtrans(FROM_HERE, dir_.get()); 873 Entry e(&rtrans, GET_BY_ID, TestIdFactory::FromNumber(-99)); 874 ASSERT_TRUE(e.good()); 875} 876 877TEST_F(SyncableDirectoryTest, TestDelete) { 878 std::string name = "peanut butter jelly time"; 879 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 880 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), name); 881 ASSERT_TRUE(e1.good()); 882 e1.PutIsDel(true); 883 MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), name); 884 ASSERT_TRUE(e2.good()); 885 e2.PutIsDel(true); 886 MutableEntry e3(&trans, CREATE, BOOKMARKS, trans.root_id(), name); 887 ASSERT_TRUE(e3.good()); 888 e3.PutIsDel(true); 889 890 e1.PutIsDel(false); 891 e2.PutIsDel(false); 892 e3.PutIsDel(false); 893 894 e1.PutIsDel(true); 895 e2.PutIsDel(true); 896 e3.PutIsDel(true); 897} 898 899TEST_F(SyncableDirectoryTest, TestGetUnsynced) { 900 Directory::Metahandles handles; 901 int64 handle1, handle2; 902 { 903 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 904 905 dir_->GetUnsyncedMetaHandles(&trans, &handles); 906 ASSERT_TRUE(0 == handles.size()); 907 908 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "abba"); 909 ASSERT_TRUE(e1.good()); 910 handle1 = e1.GetMetahandle(); 911 e1.PutBaseVersion(1); 912 e1.PutIsDir(true); 913 e1.PutId(TestIdFactory::FromNumber(101)); 914 915 MutableEntry e2(&trans, CREATE, BOOKMARKS, e1.GetId(), "bread"); 916 ASSERT_TRUE(e2.good()); 917 handle2 = e2.GetMetahandle(); 918 e2.PutBaseVersion(1); 919 e2.PutId(TestIdFactory::FromNumber(102)); 920 } 921 dir_->SaveChanges(); 922 { 923 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 924 925 dir_->GetUnsyncedMetaHandles(&trans, &handles); 926 ASSERT_TRUE(0 == handles.size()); 927 928 MutableEntry e3(&trans, GET_BY_HANDLE, handle1); 929 ASSERT_TRUE(e3.good()); 930 e3.PutIsUnsynced(true); 931 } 932 dir_->SaveChanges(); 933 { 934 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 935 dir_->GetUnsyncedMetaHandles(&trans, &handles); 936 ASSERT_TRUE(1 == handles.size()); 937 ASSERT_TRUE(handle1 == handles[0]); 938 939 MutableEntry e4(&trans, GET_BY_HANDLE, handle2); 940 ASSERT_TRUE(e4.good()); 941 e4.PutIsUnsynced(true); 942 } 943 dir_->SaveChanges(); 944 { 945 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 946 dir_->GetUnsyncedMetaHandles(&trans, &handles); 947 ASSERT_TRUE(2 == handles.size()); 948 if (handle1 == handles[0]) { 949 ASSERT_TRUE(handle2 == handles[1]); 950 } else { 951 ASSERT_TRUE(handle2 == handles[0]); 952 ASSERT_TRUE(handle1 == handles[1]); 953 } 954 955 MutableEntry e5(&trans, GET_BY_HANDLE, handle1); 956 ASSERT_TRUE(e5.good()); 957 ASSERT_TRUE(e5.GetIsUnsynced()); 958 ASSERT_TRUE(e5.PutIsUnsynced(false)); 959 ASSERT_FALSE(e5.GetIsUnsynced()); 960 } 961 dir_->SaveChanges(); 962 { 963 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 964 dir_->GetUnsyncedMetaHandles(&trans, &handles); 965 ASSERT_TRUE(1 == handles.size()); 966 ASSERT_TRUE(handle2 == handles[0]); 967 } 968} 969 970TEST_F(SyncableDirectoryTest, TestGetUnappliedUpdates) { 971 std::vector<int64> handles; 972 int64 handle1, handle2; 973 const FullModelTypeSet all_types = FullModelTypeSet::All(); 974 { 975 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 976 977 dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles); 978 ASSERT_TRUE(0 == handles.size()); 979 980 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "abba"); 981 ASSERT_TRUE(e1.good()); 982 handle1 = e1.GetMetahandle(); 983 e1.PutIsUnappliedUpdate(false); 984 e1.PutBaseVersion(1); 985 e1.PutId(TestIdFactory::FromNumber(101)); 986 e1.PutIsDir(true); 987 988 MutableEntry e2(&trans, CREATE, BOOKMARKS, e1.GetId(), "bread"); 989 ASSERT_TRUE(e2.good()); 990 handle2 = e2.GetMetahandle(); 991 e2.PutIsUnappliedUpdate(false); 992 e2.PutBaseVersion(1); 993 e2.PutId(TestIdFactory::FromNumber(102)); 994 } 995 dir_->SaveChanges(); 996 { 997 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 998 999 dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles); 1000 ASSERT_TRUE(0 == handles.size()); 1001 1002 MutableEntry e3(&trans, GET_BY_HANDLE, handle1); 1003 ASSERT_TRUE(e3.good()); 1004 e3.PutIsUnappliedUpdate(true); 1005 } 1006 dir_->SaveChanges(); 1007 { 1008 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1009 dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles); 1010 ASSERT_TRUE(1 == handles.size()); 1011 ASSERT_TRUE(handle1 == handles[0]); 1012 1013 MutableEntry e4(&trans, GET_BY_HANDLE, handle2); 1014 ASSERT_TRUE(e4.good()); 1015 e4.PutIsUnappliedUpdate(true); 1016 } 1017 dir_->SaveChanges(); 1018 { 1019 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1020 dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles); 1021 ASSERT_TRUE(2 == handles.size()); 1022 if (handle1 == handles[0]) { 1023 ASSERT_TRUE(handle2 == handles[1]); 1024 } else { 1025 ASSERT_TRUE(handle2 == handles[0]); 1026 ASSERT_TRUE(handle1 == handles[1]); 1027 } 1028 1029 MutableEntry e5(&trans, GET_BY_HANDLE, handle1); 1030 ASSERT_TRUE(e5.good()); 1031 e5.PutIsUnappliedUpdate(false); 1032 } 1033 dir_->SaveChanges(); 1034 { 1035 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1036 dir_->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles); 1037 ASSERT_TRUE(1 == handles.size()); 1038 ASSERT_TRUE(handle2 == handles[0]); 1039 } 1040} 1041 1042 1043TEST_F(SyncableDirectoryTest, DeleteBug_531383) { 1044 // Try to evoke a check failure... 1045 TestIdFactory id_factory; 1046 int64 grandchild_handle; 1047 { 1048 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get()); 1049 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, id_factory.root(), "Bob"); 1050 ASSERT_TRUE(parent.good()); 1051 parent.PutIsDir(true); 1052 parent.PutId(id_factory.NewServerId()); 1053 parent.PutBaseVersion(1); 1054 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "Bob"); 1055 ASSERT_TRUE(child.good()); 1056 child.PutIsDir(true); 1057 child.PutId(id_factory.NewServerId()); 1058 child.PutBaseVersion(1); 1059 MutableEntry grandchild(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob"); 1060 ASSERT_TRUE(grandchild.good()); 1061 grandchild.PutId(id_factory.NewServerId()); 1062 grandchild.PutBaseVersion(1); 1063 grandchild.PutIsDel(true); 1064 MutableEntry twin(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob"); 1065 ASSERT_TRUE(twin.good()); 1066 twin.PutIsDel(true); 1067 grandchild.PutIsDel(false); 1068 1069 grandchild_handle = grandchild.GetMetahandle(); 1070 } 1071 dir_->SaveChanges(); 1072 { 1073 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get()); 1074 MutableEntry grandchild(&wtrans, GET_BY_HANDLE, grandchild_handle); 1075 grandchild.PutIsDel(true); // Used to CHECK fail here. 1076 } 1077} 1078 1079static inline bool IsLegalNewParent(const Entry& a, const Entry& b) { 1080 return IsLegalNewParent(a.trans(), a.GetId(), b.GetId()); 1081} 1082 1083TEST_F(SyncableDirectoryTest, TestIsLegalNewParent) { 1084 TestIdFactory id_factory; 1085 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get()); 1086 Entry root(&wtrans, GET_BY_ID, id_factory.root()); 1087 ASSERT_TRUE(root.good()); 1088 MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root.GetId(), "Bob"); 1089 ASSERT_TRUE(parent.good()); 1090 parent.PutIsDir(true); 1091 parent.PutId(id_factory.NewServerId()); 1092 parent.PutBaseVersion(1); 1093 MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "Bob"); 1094 ASSERT_TRUE(child.good()); 1095 child.PutIsDir(true); 1096 child.PutId(id_factory.NewServerId()); 1097 child.PutBaseVersion(1); 1098 MutableEntry grandchild(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob"); 1099 ASSERT_TRUE(grandchild.good()); 1100 grandchild.PutId(id_factory.NewServerId()); 1101 grandchild.PutBaseVersion(1); 1102 1103 MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, root.GetId(), "Pete"); 1104 ASSERT_TRUE(parent2.good()); 1105 parent2.PutIsDir(true); 1106 parent2.PutId(id_factory.NewServerId()); 1107 parent2.PutBaseVersion(1); 1108 MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent2.GetId(), "Pete"); 1109 ASSERT_TRUE(child2.good()); 1110 child2.PutIsDir(true); 1111 child2.PutId(id_factory.NewServerId()); 1112 child2.PutBaseVersion(1); 1113 MutableEntry grandchild2(&wtrans, CREATE, BOOKMARKS, child2.GetId(), "Pete"); 1114 ASSERT_TRUE(grandchild2.good()); 1115 grandchild2.PutId(id_factory.NewServerId()); 1116 grandchild2.PutBaseVersion(1); 1117 // resulting tree 1118 // root 1119 // / | 1120 // parent parent2 1121 // | | 1122 // child child2 1123 // | | 1124 // grandchild grandchild2 1125 ASSERT_TRUE(IsLegalNewParent(child, root)); 1126 ASSERT_TRUE(IsLegalNewParent(child, parent)); 1127 ASSERT_FALSE(IsLegalNewParent(child, child)); 1128 ASSERT_FALSE(IsLegalNewParent(child, grandchild)); 1129 ASSERT_TRUE(IsLegalNewParent(child, parent2)); 1130 ASSERT_TRUE(IsLegalNewParent(child, grandchild2)); 1131 ASSERT_FALSE(IsLegalNewParent(parent, grandchild)); 1132 ASSERT_FALSE(IsLegalNewParent(root, grandchild)); 1133 ASSERT_FALSE(IsLegalNewParent(parent, grandchild)); 1134} 1135 1136TEST_F(SyncableDirectoryTest, TestEntryIsInFolder) { 1137 // Create a subdir and an entry. 1138 int64 entry_handle; 1139 syncable::Id folder_id; 1140 syncable::Id entry_id; 1141 std::string entry_name = "entry"; 1142 1143 { 1144 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1145 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "folder"); 1146 ASSERT_TRUE(folder.good()); 1147 folder.PutIsDir(true); 1148 EXPECT_TRUE(folder.PutIsUnsynced(true)); 1149 folder_id = folder.GetId(); 1150 1151 MutableEntry entry(&trans, CREATE, BOOKMARKS, folder.GetId(), entry_name); 1152 ASSERT_TRUE(entry.good()); 1153 entry_handle = entry.GetMetahandle(); 1154 entry.PutIsUnsynced(true); 1155 entry_id = entry.GetId(); 1156 } 1157 1158 // Make sure we can find the entry in the folder. 1159 { 1160 ReadTransaction trans(FROM_HERE, dir_.get()); 1161 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), entry_name)); 1162 EXPECT_EQ(1, CountEntriesWithName(&trans, folder_id, entry_name)); 1163 1164 Entry entry(&trans, GET_BY_ID, entry_id); 1165 ASSERT_TRUE(entry.good()); 1166 EXPECT_EQ(entry_handle, entry.GetMetahandle()); 1167 EXPECT_TRUE(entry.GetNonUniqueName()== entry_name); 1168 EXPECT_TRUE(entry.GetParentId()== folder_id); 1169 } 1170} 1171 1172TEST_F(SyncableDirectoryTest, TestParentIdIndexUpdate) { 1173 std::string child_name = "child"; 1174 1175 WriteTransaction wt(FROM_HERE, UNITTEST, dir_.get()); 1176 MutableEntry parent_folder(&wt, CREATE, BOOKMARKS, wt.root_id(), "folder1"); 1177 parent_folder.PutIsUnsynced(true); 1178 parent_folder.PutIsDir(true); 1179 1180 MutableEntry parent_folder2(&wt, CREATE, BOOKMARKS, wt.root_id(), "folder2"); 1181 parent_folder2.PutIsUnsynced(true); 1182 parent_folder2.PutIsDir(true); 1183 1184 MutableEntry child(&wt, CREATE, BOOKMARKS, parent_folder.GetId(), child_name); 1185 child.PutIsDir(true); 1186 child.PutIsUnsynced(true); 1187 1188 ASSERT_TRUE(child.good()); 1189 1190 EXPECT_EQ(0, CountEntriesWithName(&wt, wt.root_id(), child_name)); 1191 EXPECT_EQ(parent_folder.GetId(), child.GetParentId()); 1192 EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder.GetId(), child_name)); 1193 EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder2.GetId(), child_name)); 1194 child.PutParentId(parent_folder2.GetId()); 1195 EXPECT_EQ(parent_folder2.GetId(), child.GetParentId()); 1196 EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder.GetId(), child_name)); 1197 EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder2.GetId(), child_name)); 1198} 1199 1200TEST_F(SyncableDirectoryTest, TestNoReindexDeletedItems) { 1201 std::string folder_name = "folder"; 1202 std::string new_name = "new_name"; 1203 1204 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1205 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), folder_name); 1206 ASSERT_TRUE(folder.good()); 1207 folder.PutIsDir(true); 1208 folder.PutIsDel(true); 1209 1210 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name)); 1211 1212 MutableEntry deleted(&trans, GET_BY_ID, folder.GetId()); 1213 ASSERT_TRUE(deleted.good()); 1214 deleted.PutParentId(trans.root_id()); 1215 deleted.PutNonUniqueName(new_name); 1216 1217 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name)); 1218 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), new_name)); 1219} 1220 1221TEST_F(SyncableDirectoryTest, TestCaseChangeRename) { 1222 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1223 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "CaseChange"); 1224 ASSERT_TRUE(folder.good()); 1225 folder.PutParentId(trans.root_id()); 1226 folder.PutNonUniqueName("CASECHANGE"); 1227 folder.PutIsDel(true); 1228} 1229 1230// Create items of each model type, and check that GetModelType and 1231// GetServerModelType return the right value. 1232TEST_F(SyncableDirectoryTest, GetModelType) { 1233 TestIdFactory id_factory; 1234 ModelTypeSet protocol_types = ProtocolTypes(); 1235 for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good(); 1236 iter.Inc()) { 1237 ModelType datatype = iter.Get(); 1238 SCOPED_TRACE(testing::Message("Testing model type ") << datatype); 1239 switch (datatype) { 1240 case UNSPECIFIED: 1241 case TOP_LEVEL_FOLDER: 1242 continue; // Datatype isn't a function of Specifics. 1243 default: 1244 break; 1245 } 1246 sync_pb::EntitySpecifics specifics; 1247 AddDefaultFieldValue(datatype, &specifics); 1248 1249 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1250 1251 MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "Folder"); 1252 ASSERT_TRUE(folder.good()); 1253 folder.PutId(id_factory.NewServerId()); 1254 folder.PutSpecifics(specifics); 1255 folder.PutBaseVersion(1); 1256 folder.PutIsDir(true); 1257 folder.PutIsDel(false); 1258 ASSERT_EQ(datatype, folder.GetModelType()); 1259 1260 MutableEntry item(&trans, CREATE, BOOKMARKS, trans.root_id(), "Item"); 1261 ASSERT_TRUE(item.good()); 1262 item.PutId(id_factory.NewServerId()); 1263 item.PutSpecifics(specifics); 1264 item.PutBaseVersion(1); 1265 item.PutIsDir(false); 1266 item.PutIsDel(false); 1267 ASSERT_EQ(datatype, item.GetModelType()); 1268 1269 // It's critical that deletion records retain their datatype, so that 1270 // they can be dispatched to the appropriate change processor. 1271 MutableEntry deleted_item( 1272 &trans, CREATE, BOOKMARKS, trans.root_id(), "Deleted Item"); 1273 ASSERT_TRUE(item.good()); 1274 deleted_item.PutId(id_factory.NewServerId()); 1275 deleted_item.PutSpecifics(specifics); 1276 deleted_item.PutBaseVersion(1); 1277 deleted_item.PutIsDir(false); 1278 deleted_item.PutIsDel(true); 1279 ASSERT_EQ(datatype, deleted_item.GetModelType()); 1280 1281 MutableEntry server_folder(&trans, CREATE_NEW_UPDATE_ITEM, 1282 id_factory.NewServerId()); 1283 ASSERT_TRUE(server_folder.good()); 1284 server_folder.PutServerSpecifics(specifics); 1285 server_folder.PutBaseVersion(1); 1286 server_folder.PutServerIsDir(true); 1287 server_folder.PutServerIsDel(false); 1288 ASSERT_EQ(datatype, server_folder.GetServerModelType()); 1289 1290 MutableEntry server_item(&trans, CREATE_NEW_UPDATE_ITEM, 1291 id_factory.NewServerId()); 1292 ASSERT_TRUE(server_item.good()); 1293 server_item.PutServerSpecifics(specifics); 1294 server_item.PutBaseVersion(1); 1295 server_item.PutServerIsDir(false); 1296 server_item.PutServerIsDel(false); 1297 ASSERT_EQ(datatype, server_item.GetServerModelType()); 1298 1299 sync_pb::SyncEntity folder_entity; 1300 folder_entity.set_id_string(SyncableIdToProto(id_factory.NewServerId())); 1301 folder_entity.set_deleted(false); 1302 folder_entity.set_folder(true); 1303 folder_entity.mutable_specifics()->CopyFrom(specifics); 1304 ASSERT_EQ(datatype, GetModelType(folder_entity)); 1305 1306 sync_pb::SyncEntity item_entity; 1307 item_entity.set_id_string(SyncableIdToProto(id_factory.NewServerId())); 1308 item_entity.set_deleted(false); 1309 item_entity.set_folder(false); 1310 item_entity.mutable_specifics()->CopyFrom(specifics); 1311 ASSERT_EQ(datatype, GetModelType(item_entity)); 1312 } 1313} 1314 1315// A test that roughly mimics the directory interaction that occurs when a 1316// bookmark folder and entry are created then synced for the first time. It is 1317// a more common variant of the 'DeletedAndUnsyncedChild' scenario tested below. 1318TEST_F(SyncableDirectoryTest, ChangeEntryIDAndUpdateChildren_ParentAndChild) { 1319 TestIdFactory id_factory; 1320 Id orig_parent_id; 1321 Id orig_child_id; 1322 1323 { 1324 // Create two client-side items, a parent and child. 1325 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1326 1327 MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent"); 1328 parent.PutIsDir(true); 1329 parent.PutIsUnsynced(true); 1330 1331 MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child"); 1332 child.PutIsUnsynced(true); 1333 1334 orig_parent_id = parent.GetId(); 1335 orig_child_id = child.GetId(); 1336 } 1337 1338 { 1339 // Simulate what happens after committing two items. Their IDs will be 1340 // replaced with server IDs. The child is renamed first, then the parent. 1341 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1342 1343 MutableEntry parent(&trans, GET_BY_ID, orig_parent_id); 1344 MutableEntry child(&trans, GET_BY_ID, orig_child_id); 1345 1346 ChangeEntryIDAndUpdateChildren(&trans, &child, id_factory.NewServerId()); 1347 child.PutIsUnsynced(false); 1348 child.PutBaseVersion(1); 1349 child.PutServerVersion(1); 1350 1351 ChangeEntryIDAndUpdateChildren(&trans, &parent, id_factory.NewServerId()); 1352 parent.PutIsUnsynced(false); 1353 parent.PutBaseVersion(1); 1354 parent.PutServerVersion(1); 1355 } 1356 1357 // Final check for validity. 1358 EXPECT_EQ(OPENED, SimulateSaveAndReloadDir()); 1359} 1360 1361// A test based on the scenario where we create a bookmark folder and entry 1362// locally, but with a twist. In this case, the bookmark is deleted before we 1363// are able to sync either it or its parent folder. This scenario used to cause 1364// directory corruption, see crbug.com/125381. 1365TEST_F(SyncableDirectoryTest, 1366 ChangeEntryIDAndUpdateChildren_DeletedAndUnsyncedChild) { 1367 TestIdFactory id_factory; 1368 Id orig_parent_id; 1369 Id orig_child_id; 1370 1371 { 1372 // Create two client-side items, a parent and child. 1373 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1374 1375 MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent"); 1376 parent.PutIsDir(true); 1377 parent.PutIsUnsynced(true); 1378 1379 MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child"); 1380 child.PutIsUnsynced(true); 1381 1382 orig_parent_id = parent.GetId(); 1383 orig_child_id = child.GetId(); 1384 } 1385 1386 { 1387 // Delete the child. 1388 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1389 1390 MutableEntry child(&trans, GET_BY_ID, orig_child_id); 1391 child.PutIsDel(true); 1392 } 1393 1394 { 1395 // Simulate what happens after committing the parent. Its ID will be 1396 // replaced with server a ID. 1397 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1398 1399 MutableEntry parent(&trans, GET_BY_ID, orig_parent_id); 1400 1401 ChangeEntryIDAndUpdateChildren(&trans, &parent, id_factory.NewServerId()); 1402 parent.PutIsUnsynced(false); 1403 parent.PutBaseVersion(1); 1404 parent.PutServerVersion(1); 1405 } 1406 1407 // Final check for validity. 1408 EXPECT_EQ(OPENED, SimulateSaveAndReloadDir()); 1409} 1410 1411// Ask the directory to generate a unique ID. Close and re-open the database 1412// without saving, then ask for another unique ID. Verify IDs are not reused. 1413// This scenario simulates a crash within the first few seconds of operation. 1414TEST_F(SyncableDirectoryTest, LocalIdReuseTest) { 1415 Id pre_crash_id = dir_->NextId(); 1416 SimulateCrashAndReloadDir(); 1417 Id post_crash_id = dir_->NextId(); 1418 EXPECT_NE(pre_crash_id, post_crash_id); 1419} 1420 1421// Ask the directory to generate a unique ID. Save the directory. Close and 1422// re-open the database without saving, then ask for another unique ID. Verify 1423// IDs are not reused. This scenario simulates a steady-state crash. 1424TEST_F(SyncableDirectoryTest, LocalIdReuseTestWithSave) { 1425 Id pre_crash_id = dir_->NextId(); 1426 dir_->SaveChanges(); 1427 SimulateCrashAndReloadDir(); 1428 Id post_crash_id = dir_->NextId(); 1429 EXPECT_NE(pre_crash_id, post_crash_id); 1430} 1431 1432// Ensure that the unsynced, is_del and server unkown entries that may have been 1433// left in the database by old clients will be deleted when we open the old 1434// database. 1435TEST_F(SyncableDirectoryTest, OldClientLeftUnsyncedDeletedLocalItem) { 1436 // We must create an entry with the offending properties. This is done with 1437 // some abuse of the MutableEntry's API; it doesn't expect us to modify an 1438 // item after it is deleted. If this hack becomes impractical we will need to 1439 // find a new way to simulate this scenario. 1440 1441 TestIdFactory id_factory; 1442 1443 // Happy-path: These valid entries should not get deleted. 1444 Id server_knows_id = id_factory.NewServerId(); 1445 Id not_is_del_id = id_factory.NewLocalId(); 1446 1447 // The ID of the entry which will be unsynced, is_del and !ServerKnows(). 1448 Id zombie_id = id_factory.NewLocalId(); 1449 1450 // We're about to do some bad things. Tell the directory verification 1451 // routines to look the other way. 1452 dir_->SetInvariantCheckLevel(OFF); 1453 1454 { 1455 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1456 1457 // Create an uncommitted tombstone entry. 1458 MutableEntry server_knows(&trans, CREATE, BOOKMARKS, id_factory.root(), 1459 "server_knows"); 1460 server_knows.PutId(server_knows_id); 1461 server_knows.PutIsUnsynced(true); 1462 server_knows.PutIsDel(true); 1463 server_knows.PutBaseVersion(5); 1464 server_knows.PutServerVersion(4); 1465 1466 // Create a valid update entry. 1467 MutableEntry not_is_del( 1468 &trans, CREATE, BOOKMARKS, id_factory.root(), "not_is_del"); 1469 not_is_del.PutId(not_is_del_id); 1470 not_is_del.PutIsDel(false); 1471 not_is_del.PutIsUnsynced(true); 1472 1473 // Create a tombstone which should never be sent to the server because the 1474 // server never knew about the item's existence. 1475 // 1476 // New clients should never put entries into this state. We work around 1477 // this by setting IS_DEL before setting IS_UNSYNCED, something which the 1478 // client should never do in practice. 1479 MutableEntry zombie(&trans, CREATE, BOOKMARKS, id_factory.root(), "zombie"); 1480 zombie.PutId(zombie_id); 1481 zombie.PutIsDel(true); 1482 zombie.PutIsUnsynced(true); 1483 } 1484 1485 ASSERT_EQ(OPENED, SimulateSaveAndReloadDir()); 1486 1487 { 1488 ReadTransaction trans(FROM_HERE, dir_.get()); 1489 1490 // The directory loading routines should have cleaned things up, making it 1491 // safe to check invariants once again. 1492 dir_->FullyCheckTreeInvariants(&trans); 1493 1494 Entry server_knows(&trans, GET_BY_ID, server_knows_id); 1495 EXPECT_TRUE(server_knows.good()); 1496 1497 Entry not_is_del(&trans, GET_BY_ID, not_is_del_id); 1498 EXPECT_TRUE(not_is_del.good()); 1499 1500 Entry zombie(&trans, GET_BY_ID, zombie_id); 1501 EXPECT_FALSE(zombie.good()); 1502 } 1503} 1504 1505TEST_F(SyncableDirectoryTest, PositionWithNullSurvivesSaveAndReload) { 1506 TestIdFactory id_factory; 1507 Id null_child_id; 1508 const char null_cstr[] = "\0null\0test"; 1509 std::string null_str(null_cstr, arraysize(null_cstr) - 1); 1510 // Pad up to the minimum length with 0x7f characters, then add a string that 1511 // contains a few NULLs to the end. This is slightly wrong, since the suffix 1512 // part of a UniquePosition shouldn't contain NULLs, but it's good enough for 1513 // this test. 1514 std::string suffix = 1515 std::string(UniquePosition::kSuffixLength - null_str.length(), '\x7f') 1516 + null_str; 1517 UniquePosition null_pos = UniquePosition::FromInt64(10, suffix); 1518 1519 { 1520 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1521 1522 MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent"); 1523 parent.PutIsDir(true); 1524 parent.PutIsUnsynced(true); 1525 1526 MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child"); 1527 child.PutIsUnsynced(true); 1528 child.PutUniquePosition(null_pos); 1529 child.PutServerUniquePosition(null_pos); 1530 1531 null_child_id = child.GetId(); 1532 } 1533 1534 EXPECT_EQ(OPENED, SimulateSaveAndReloadDir()); 1535 1536 { 1537 ReadTransaction trans(FROM_HERE, dir_.get()); 1538 1539 Entry null_ordinal_child(&trans, GET_BY_ID, null_child_id); 1540 EXPECT_TRUE( 1541 null_pos.Equals(null_ordinal_child.GetUniquePosition())); 1542 EXPECT_TRUE( 1543 null_pos.Equals(null_ordinal_child.GetServerUniquePosition())); 1544 } 1545} 1546 1547// An OnDirectoryBackingStore that can be set to always fail SaveChanges. 1548class TestBackingStore : public OnDiskDirectoryBackingStore { 1549 public: 1550 TestBackingStore(const std::string& dir_name, 1551 const base::FilePath& backing_filepath); 1552 1553 virtual ~TestBackingStore(); 1554 1555 virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot) 1556 OVERRIDE; 1557 1558 void StartFailingSaveChanges() { 1559 fail_save_changes_ = true; 1560 } 1561 1562 private: 1563 bool fail_save_changes_; 1564}; 1565 1566TestBackingStore::TestBackingStore(const std::string& dir_name, 1567 const base::FilePath& backing_filepath) 1568 : OnDiskDirectoryBackingStore(dir_name, backing_filepath), 1569 fail_save_changes_(false) { 1570} 1571 1572TestBackingStore::~TestBackingStore() { } 1573 1574bool TestBackingStore::SaveChanges( 1575 const Directory::SaveChangesSnapshot& snapshot){ 1576 if (fail_save_changes_) { 1577 return false; 1578 } else { 1579 return OnDiskDirectoryBackingStore::SaveChanges(snapshot); 1580 } 1581} 1582 1583// A directory whose Save() function can be set to always fail. 1584class TestDirectory : public Directory { 1585 public: 1586 // A factory function used to work around some initialization order issues. 1587 static TestDirectory* Create( 1588 Encryptor *encryptor, 1589 UnrecoverableErrorHandler *handler, 1590 const std::string& dir_name, 1591 const base::FilePath& backing_filepath); 1592 1593 virtual ~TestDirectory(); 1594 1595 void StartFailingSaveChanges() { 1596 backing_store_->StartFailingSaveChanges(); 1597 } 1598 1599 private: 1600 TestDirectory(Encryptor* encryptor, 1601 UnrecoverableErrorHandler* handler, 1602 TestBackingStore* backing_store); 1603 1604 TestBackingStore* backing_store_; 1605}; 1606 1607TestDirectory* TestDirectory::Create( 1608 Encryptor *encryptor, 1609 UnrecoverableErrorHandler *handler, 1610 const std::string& dir_name, 1611 const base::FilePath& backing_filepath) { 1612 TestBackingStore* backing_store = 1613 new TestBackingStore(dir_name, backing_filepath); 1614 return new TestDirectory(encryptor, handler, backing_store); 1615} 1616 1617TestDirectory::TestDirectory(Encryptor* encryptor, 1618 UnrecoverableErrorHandler* handler, 1619 TestBackingStore* backing_store) 1620 : Directory(backing_store, handler, NULL, NULL, NULL), 1621 backing_store_(backing_store) { 1622} 1623 1624TestDirectory::~TestDirectory() { } 1625 1626TEST(OnDiskSyncableDirectory, FailInitialWrite) { 1627 FakeEncryptor encryptor; 1628 TestUnrecoverableErrorHandler handler; 1629 base::ScopedTempDir temp_dir; 1630 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 1631 base::FilePath file_path = temp_dir.path().Append( 1632 FILE_PATH_LITERAL("Test.sqlite3")); 1633 std::string name = "user@x.com"; 1634 NullDirectoryChangeDelegate delegate; 1635 1636 scoped_ptr<TestDirectory> test_dir( 1637 TestDirectory::Create(&encryptor, &handler, name, file_path)); 1638 1639 test_dir->StartFailingSaveChanges(); 1640 ASSERT_EQ(FAILED_INITIAL_WRITE, test_dir->Open(name, &delegate, 1641 NullTransactionObserver())); 1642} 1643 1644// A variant of SyncableDirectoryTest that uses a real sqlite database. 1645class OnDiskSyncableDirectoryTest : public SyncableDirectoryTest { 1646 protected: 1647 // SetUp() is called before each test case is run. 1648 // The sqlite3 DB is deleted before each test is run. 1649 virtual void SetUp() { 1650 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 1651 file_path_ = temp_dir_.path().Append( 1652 FILE_PATH_LITERAL("Test.sqlite3")); 1653 base::DeleteFile(file_path_, true); 1654 CreateDirectory(); 1655 } 1656 1657 virtual void TearDown() { 1658 // This also closes file handles. 1659 dir_->SaveChanges(); 1660 dir_.reset(); 1661 base::DeleteFile(file_path_, true); 1662 } 1663 1664 // Creates a new directory. Deletes the old directory, if it exists. 1665 void CreateDirectory() { 1666 test_directory_ = 1667 TestDirectory::Create(&encryptor_, &handler_, kName, file_path_); 1668 dir_.reset(test_directory_); 1669 ASSERT_TRUE(dir_.get()); 1670 ASSERT_EQ(OPENED, dir_->Open(kName, &delegate_, 1671 NullTransactionObserver())); 1672 ASSERT_TRUE(dir_->good()); 1673 } 1674 1675 void SaveAndReloadDir() { 1676 dir_->SaveChanges(); 1677 CreateDirectory(); 1678 } 1679 1680 void StartFailingSaveChanges() { 1681 test_directory_->StartFailingSaveChanges(); 1682 } 1683 1684 TestDirectory *test_directory_; // mirrors scoped_ptr<Directory> dir_ 1685 base::ScopedTempDir temp_dir_; 1686 base::FilePath file_path_; 1687}; 1688 1689TEST_F(OnDiskSyncableDirectoryTest, TestPurgeEntriesWithTypeIn) { 1690 sync_pb::EntitySpecifics bookmark_specs; 1691 sync_pb::EntitySpecifics autofill_specs; 1692 sync_pb::EntitySpecifics preference_specs; 1693 AddDefaultFieldValue(BOOKMARKS, &bookmark_specs); 1694 AddDefaultFieldValue(PREFERENCES, &preference_specs); 1695 AddDefaultFieldValue(AUTOFILL, &autofill_specs); 1696 1697 ModelTypeSet types_to_purge(PREFERENCES, AUTOFILL); 1698 1699 TestIdFactory id_factory; 1700 // Create some items for each type. 1701 { 1702 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1703 1704 // Make it look like these types have completed initial sync. 1705 CreateTypeRoot(&trans, dir_.get(), BOOKMARKS); 1706 CreateTypeRoot(&trans, dir_.get(), PREFERENCES); 1707 CreateTypeRoot(&trans, dir_.get(), AUTOFILL); 1708 1709 // Add more nodes for this type. Technically, they should be placed under 1710 // the proper type root nodes but the assertions in this test won't notice 1711 // if their parent isn't quite right. 1712 MutableEntry item1(&trans, CREATE, BOOKMARKS, trans.root_id(), "Item"); 1713 ASSERT_TRUE(item1.good()); 1714 item1.PutServerSpecifics(bookmark_specs); 1715 item1.PutIsUnsynced(true); 1716 1717 MutableEntry item2(&trans, CREATE_NEW_UPDATE_ITEM, 1718 id_factory.NewServerId()); 1719 ASSERT_TRUE(item2.good()); 1720 item2.PutServerSpecifics(bookmark_specs); 1721 item2.PutIsUnappliedUpdate(true); 1722 1723 MutableEntry item3(&trans, CREATE, PREFERENCES, 1724 trans.root_id(), "Item"); 1725 ASSERT_TRUE(item3.good()); 1726 item3.PutSpecifics(preference_specs); 1727 item3.PutServerSpecifics(preference_specs); 1728 item3.PutIsUnsynced(true); 1729 1730 MutableEntry item4(&trans, CREATE_NEW_UPDATE_ITEM, 1731 id_factory.NewServerId()); 1732 ASSERT_TRUE(item4.good()); 1733 item4.PutServerSpecifics(preference_specs); 1734 item4.PutIsUnappliedUpdate(true); 1735 1736 MutableEntry item5(&trans, CREATE, AUTOFILL, 1737 trans.root_id(), "Item"); 1738 ASSERT_TRUE(item5.good()); 1739 item5.PutSpecifics(autofill_specs); 1740 item5.PutServerSpecifics(autofill_specs); 1741 item5.PutIsUnsynced(true); 1742 1743 MutableEntry item6(&trans, CREATE_NEW_UPDATE_ITEM, 1744 id_factory.NewServerId()); 1745 ASSERT_TRUE(item6.good()); 1746 item6.PutServerSpecifics(autofill_specs); 1747 item6.PutIsUnappliedUpdate(true); 1748 } 1749 1750 dir_->SaveChanges(); 1751 { 1752 ReadTransaction trans(FROM_HERE, dir_.get()); 1753 MetahandleSet all_set; 1754 GetAllMetaHandles(&trans, &all_set); 1755 ASSERT_EQ(10U, all_set.size()); 1756 } 1757 1758 dir_->PurgeEntriesWithTypeIn(types_to_purge, ModelTypeSet(), ModelTypeSet()); 1759 1760 // We first query the in-memory data, and then reload the directory (without 1761 // saving) to verify that disk does not still have the data. 1762 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, true); 1763 SaveAndReloadDir(); 1764 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, false); 1765} 1766 1767TEST_F(OnDiskSyncableDirectoryTest, TestShareInfo) { 1768 dir_->set_store_birthday("Jan 31st"); 1769 const char* const bag_of_chips_array = "\0bag of chips"; 1770 const std::string bag_of_chips_string = 1771 std::string(bag_of_chips_array, sizeof(bag_of_chips_array)); 1772 dir_->set_bag_of_chips(bag_of_chips_string); 1773 { 1774 ReadTransaction trans(FROM_HERE, dir_.get()); 1775 EXPECT_EQ("Jan 31st", dir_->store_birthday()); 1776 EXPECT_EQ(bag_of_chips_string, dir_->bag_of_chips()); 1777 } 1778 dir_->set_store_birthday("April 10th"); 1779 const char* const bag_of_chips2_array = "\0bag of chips2"; 1780 const std::string bag_of_chips2_string = 1781 std::string(bag_of_chips2_array, sizeof(bag_of_chips2_array)); 1782 dir_->set_bag_of_chips(bag_of_chips2_string); 1783 dir_->SaveChanges(); 1784 { 1785 ReadTransaction trans(FROM_HERE, dir_.get()); 1786 EXPECT_EQ("April 10th", dir_->store_birthday()); 1787 EXPECT_EQ(bag_of_chips2_string, dir_->bag_of_chips()); 1788 } 1789 const char* const bag_of_chips3_array = "\0bag of chips3"; 1790 const std::string bag_of_chips3_string = 1791 std::string(bag_of_chips3_array, sizeof(bag_of_chips3_array)); 1792 dir_->set_bag_of_chips(bag_of_chips3_string); 1793 // Restore the directory from disk. Make sure that nothing's changed. 1794 SaveAndReloadDir(); 1795 { 1796 ReadTransaction trans(FROM_HERE, dir_.get()); 1797 EXPECT_EQ("April 10th", dir_->store_birthday()); 1798 EXPECT_EQ(bag_of_chips3_string, dir_->bag_of_chips()); 1799 } 1800} 1801 1802TEST_F(OnDiskSyncableDirectoryTest, 1803 TestSimpleFieldsPreservedDuringSaveChanges) { 1804 Id update_id = TestIdFactory::FromNumber(1); 1805 Id create_id; 1806 EntryKernel create_pre_save, update_pre_save; 1807 EntryKernel create_post_save, update_post_save; 1808 std::string create_name = "Create"; 1809 1810 { 1811 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1812 MutableEntry create( 1813 &trans, CREATE, BOOKMARKS, trans.root_id(), create_name); 1814 MutableEntry update(&trans, CREATE_NEW_UPDATE_ITEM, update_id); 1815 create.PutIsUnsynced(true); 1816 update.PutIsUnappliedUpdate(true); 1817 sync_pb::EntitySpecifics specifics; 1818 specifics.mutable_bookmark()->set_favicon("PNG"); 1819 specifics.mutable_bookmark()->set_url("http://nowhere"); 1820 create.PutSpecifics(specifics); 1821 update.PutSpecifics(specifics); 1822 create_pre_save = create.GetKernelCopy(); 1823 update_pre_save = update.GetKernelCopy(); 1824 create_id = create.GetId(); 1825 } 1826 1827 dir_->SaveChanges(); 1828 dir_.reset(new Directory(new OnDiskDirectoryBackingStore(kName, file_path_), 1829 &handler_, 1830 NULL, 1831 NULL, 1832 NULL)); 1833 1834 ASSERT_TRUE(dir_.get()); 1835 ASSERT_EQ(OPENED, dir_->Open(kName, &delegate_, NullTransactionObserver())); 1836 ASSERT_TRUE(dir_->good()); 1837 1838 { 1839 ReadTransaction trans(FROM_HERE, dir_.get()); 1840 Entry create(&trans, GET_BY_ID, create_id); 1841 EXPECT_EQ(1, CountEntriesWithName(&trans, trans.root_id(), create_name)); 1842 Entry update(&trans, GET_BY_ID, update_id); 1843 create_post_save = create.GetKernelCopy(); 1844 update_post_save = update.GetKernelCopy(); 1845 } 1846 int i = BEGIN_FIELDS; 1847 for ( ; i < INT64_FIELDS_END ; ++i) { 1848 EXPECT_EQ(create_pre_save.ref((Int64Field)i) + 1849 (i == TRANSACTION_VERSION ? 1 : 0), 1850 create_post_save.ref((Int64Field)i)) 1851 << "int64 field #" << i << " changed during save/load"; 1852 EXPECT_EQ(update_pre_save.ref((Int64Field)i) + 1853 (i == TRANSACTION_VERSION ? 1 : 0), 1854 update_post_save.ref((Int64Field)i)) 1855 << "int64 field #" << i << " changed during save/load"; 1856 } 1857 for ( ; i < TIME_FIELDS_END ; ++i) { 1858 EXPECT_EQ(create_pre_save.ref((TimeField)i), 1859 create_post_save.ref((TimeField)i)) 1860 << "time field #" << i << " changed during save/load"; 1861 EXPECT_EQ(update_pre_save.ref((TimeField)i), 1862 update_post_save.ref((TimeField)i)) 1863 << "time field #" << i << " changed during save/load"; 1864 } 1865 for ( ; i < ID_FIELDS_END ; ++i) { 1866 EXPECT_EQ(create_pre_save.ref((IdField)i), 1867 create_post_save.ref((IdField)i)) 1868 << "id field #" << i << " changed during save/load"; 1869 EXPECT_EQ(update_pre_save.ref((IdField)i), 1870 update_pre_save.ref((IdField)i)) 1871 << "id field #" << i << " changed during save/load"; 1872 } 1873 for ( ; i < BIT_FIELDS_END ; ++i) { 1874 EXPECT_EQ(create_pre_save.ref((BitField)i), 1875 create_post_save.ref((BitField)i)) 1876 << "Bit field #" << i << " changed during save/load"; 1877 EXPECT_EQ(update_pre_save.ref((BitField)i), 1878 update_post_save.ref((BitField)i)) 1879 << "Bit field #" << i << " changed during save/load"; 1880 } 1881 for ( ; i < STRING_FIELDS_END ; ++i) { 1882 EXPECT_EQ(create_pre_save.ref((StringField)i), 1883 create_post_save.ref((StringField)i)) 1884 << "String field #" << i << " changed during save/load"; 1885 EXPECT_EQ(update_pre_save.ref((StringField)i), 1886 update_post_save.ref((StringField)i)) 1887 << "String field #" << i << " changed during save/load"; 1888 } 1889 for ( ; i < PROTO_FIELDS_END; ++i) { 1890 EXPECT_EQ(create_pre_save.ref((ProtoField)i).SerializeAsString(), 1891 create_post_save.ref((ProtoField)i).SerializeAsString()) 1892 << "Blob field #" << i << " changed during save/load"; 1893 EXPECT_EQ(update_pre_save.ref((ProtoField)i).SerializeAsString(), 1894 update_post_save.ref((ProtoField)i).SerializeAsString()) 1895 << "Blob field #" << i << " changed during save/load"; 1896 } 1897 for ( ; i < UNIQUE_POSITION_FIELDS_END; ++i) { 1898 EXPECT_TRUE(create_pre_save.ref((UniquePositionField)i).Equals( 1899 create_post_save.ref((UniquePositionField)i))) 1900 << "Position field #" << i << " changed during save/load"; 1901 EXPECT_TRUE(update_pre_save.ref((UniquePositionField)i).Equals( 1902 update_post_save.ref((UniquePositionField)i))) 1903 << "Position field #" << i << " changed during save/load"; 1904 } 1905} 1906 1907TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailure) { 1908 int64 handle1 = 0; 1909 // Set up an item using a regular, saveable directory. 1910 { 1911 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1912 1913 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "aguilera"); 1914 ASSERT_TRUE(e1.good()); 1915 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1916 handle1 = e1.GetMetahandle(); 1917 e1.PutBaseVersion(1); 1918 e1.PutIsDir(true); 1919 e1.PutId(TestIdFactory::FromNumber(101)); 1920 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1921 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1922 } 1923 ASSERT_TRUE(dir_->SaveChanges()); 1924 1925 // Make sure the item is no longer dirty after saving, 1926 // and make a modification. 1927 { 1928 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1929 1930 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1); 1931 ASSERT_TRUE(aguilera.good()); 1932 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 1933 EXPECT_EQ(aguilera.GetNonUniqueName(), "aguilera"); 1934 aguilera.PutNonUniqueName("overwritten"); 1935 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty()); 1936 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1937 } 1938 ASSERT_TRUE(dir_->SaveChanges()); 1939 1940 // Now do some operations when SaveChanges() will fail. 1941 StartFailingSaveChanges(); 1942 ASSERT_TRUE(dir_->good()); 1943 1944 int64 handle2 = 0; 1945 { 1946 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1947 1948 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1); 1949 ASSERT_TRUE(aguilera.good()); 1950 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 1951 EXPECT_EQ(aguilera.GetNonUniqueName(), "overwritten"); 1952 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 1953 EXPECT_FALSE(IsInDirtyMetahandles(handle1)); 1954 aguilera.PutNonUniqueName("christina"); 1955 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty()); 1956 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1957 1958 // New item. 1959 MutableEntry kids_on_block( 1960 &trans, CREATE, BOOKMARKS, trans.root_id(), "kids"); 1961 ASSERT_TRUE(kids_on_block.good()); 1962 handle2 = kids_on_block.GetMetahandle(); 1963 kids_on_block.PutBaseVersion(1); 1964 kids_on_block.PutIsDir(true); 1965 kids_on_block.PutId(TestIdFactory::FromNumber(102)); 1966 EXPECT_TRUE(kids_on_block.GetKernelCopy().is_dirty()); 1967 EXPECT_TRUE(IsInDirtyMetahandles(handle2)); 1968 } 1969 1970 // We are using an unsaveable directory, so this can't succeed. However, 1971 // the HandleSaveChangesFailure code path should have been triggered. 1972 ASSERT_FALSE(dir_->SaveChanges()); 1973 1974 // Make sure things were rolled back and the world is as it was before call. 1975 { 1976 ReadTransaction trans(FROM_HERE, dir_.get()); 1977 Entry e1(&trans, GET_BY_HANDLE, handle1); 1978 ASSERT_TRUE(e1.good()); 1979 EntryKernel aguilera = e1.GetKernelCopy(); 1980 Entry kids(&trans, GET_BY_HANDLE, handle2); 1981 ASSERT_TRUE(kids.good()); 1982 EXPECT_TRUE(kids.GetKernelCopy().is_dirty()); 1983 EXPECT_TRUE(IsInDirtyMetahandles(handle2)); 1984 EXPECT_TRUE(aguilera.is_dirty()); 1985 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1986 } 1987} 1988 1989TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailureWithPurge) { 1990 int64 handle1 = 0; 1991 // Set up an item using a regular, saveable directory. 1992 { 1993 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 1994 1995 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "aguilera"); 1996 ASSERT_TRUE(e1.good()); 1997 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1998 handle1 = e1.GetMetahandle(); 1999 e1.PutBaseVersion(1); 2000 e1.PutIsDir(true); 2001 e1.PutId(TestIdFactory::FromNumber(101)); 2002 sync_pb::EntitySpecifics bookmark_specs; 2003 AddDefaultFieldValue(BOOKMARKS, &bookmark_specs); 2004 e1.PutSpecifics(bookmark_specs); 2005 e1.PutServerSpecifics(bookmark_specs); 2006 e1.PutId(TestIdFactory::FromNumber(101)); 2007 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 2008 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 2009 } 2010 ASSERT_TRUE(dir_->SaveChanges()); 2011 2012 // Now do some operations while SaveChanges() is set to fail. 2013 StartFailingSaveChanges(); 2014 ASSERT_TRUE(dir_->good()); 2015 2016 ModelTypeSet set(BOOKMARKS); 2017 dir_->PurgeEntriesWithTypeIn(set, ModelTypeSet(), ModelTypeSet()); 2018 EXPECT_TRUE(IsInMetahandlesToPurge(handle1)); 2019 ASSERT_FALSE(dir_->SaveChanges()); 2020 EXPECT_TRUE(IsInMetahandlesToPurge(handle1)); 2021} 2022 2023} // namespace 2024 2025void SyncableDirectoryTest::ValidateEntry(BaseTransaction* trans, 2026 int64 id, 2027 bool check_name, 2028 const std::string& name, 2029 int64 base_version, 2030 int64 server_version, 2031 bool is_del) { 2032 Entry e(trans, GET_BY_ID, TestIdFactory::FromNumber(id)); 2033 ASSERT_TRUE(e.good()); 2034 if (check_name) 2035 ASSERT_TRUE(name == e.GetNonUniqueName()); 2036 ASSERT_TRUE(base_version == e.GetBaseVersion()); 2037 ASSERT_TRUE(server_version == e.GetServerVersion()); 2038 ASSERT_TRUE(is_del == e.GetIsDel()); 2039} 2040 2041DirOpenResult SyncableDirectoryTest::SimulateSaveAndReloadDir() { 2042 if (!dir_->SaveChanges()) 2043 return FAILED_IN_UNITTEST; 2044 2045 return ReloadDirImpl(); 2046} 2047 2048DirOpenResult SyncableDirectoryTest::SimulateCrashAndReloadDir() { 2049 return ReloadDirImpl(); 2050} 2051 2052DirOpenResult SyncableDirectoryTest::ReloadDirImpl() { 2053 // Do some tricky things to preserve the backing store. 2054 DirectoryBackingStore* saved_store = dir_->store_.release(); 2055 2056 // Close the current directory. 2057 dir_->Close(); 2058 dir_.reset(); 2059 2060 dir_.reset(new Directory(saved_store, 2061 &handler_, 2062 NULL, 2063 NULL, 2064 NULL)); 2065 DirOpenResult result = dir_->OpenImpl(kName, &delegate_, 2066 NullTransactionObserver()); 2067 2068 // If something went wrong, we need to clear this member. If we don't, 2069 // TearDown() will be guaranteed to crash when it calls SaveChanges(). 2070 if (result != OPENED) 2071 dir_.reset(); 2072 2073 return result; 2074} 2075 2076namespace { 2077 2078class SyncableDirectoryManagement : public testing::Test { 2079 public: 2080 virtual void SetUp() { 2081 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 2082 } 2083 2084 virtual void TearDown() { 2085 } 2086 protected: 2087 base::MessageLoop message_loop_; 2088 base::ScopedTempDir temp_dir_; 2089 FakeEncryptor encryptor_; 2090 TestUnrecoverableErrorHandler handler_; 2091 NullDirectoryChangeDelegate delegate_; 2092}; 2093 2094TEST_F(SyncableDirectoryManagement, TestFileRelease) { 2095 base::FilePath path = temp_dir_.path().Append( 2096 Directory::kSyncDatabaseFilename); 2097 2098 syncable::Directory dir(new OnDiskDirectoryBackingStore("ScopeTest", path), 2099 &handler_, 2100 NULL, 2101 NULL, 2102 NULL); 2103 DirOpenResult result = 2104 dir.Open("ScopeTest", &delegate_, NullTransactionObserver()); 2105 ASSERT_EQ(result, OPENED); 2106 dir.Close(); 2107 2108 // Closing the directory should have released the backing database file. 2109 ASSERT_TRUE(base::DeleteFile(path, true)); 2110} 2111 2112class StressTransactionsDelegate : public base::PlatformThread::Delegate { 2113 public: 2114 StressTransactionsDelegate(Directory* dir, int thread_number) 2115 : dir_(dir), 2116 thread_number_(thread_number) {} 2117 2118 private: 2119 Directory* const dir_; 2120 const int thread_number_; 2121 2122 // PlatformThread::Delegate methods: 2123 virtual void ThreadMain() OVERRIDE { 2124 int entry_count = 0; 2125 std::string path_name; 2126 2127 for (int i = 0; i < 20; ++i) { 2128 const int rand_action = rand() % 10; 2129 if (rand_action < 4 && !path_name.empty()) { 2130 ReadTransaction trans(FROM_HERE, dir_); 2131 CHECK(1 == CountEntriesWithName(&trans, trans.root_id(), path_name)); 2132 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds( 2133 rand() % 10)); 2134 } else { 2135 std::string unique_name = 2136 base::StringPrintf("%d.%d", thread_number_, entry_count++); 2137 path_name.assign(unique_name.begin(), unique_name.end()); 2138 WriteTransaction trans(FROM_HERE, UNITTEST, dir_); 2139 MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), path_name); 2140 CHECK(e.good()); 2141 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds( 2142 rand() % 20)); 2143 e.PutIsUnsynced(true); 2144 if (e.PutId(TestIdFactory::FromNumber(rand())) && 2145 e.GetId().ServerKnows() && !e.GetId().IsRoot()) { 2146 e.PutBaseVersion(1); 2147 } 2148 } 2149 } 2150 } 2151 2152 DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate); 2153}; 2154 2155TEST(SyncableDirectory, StressTransactions) { 2156 base::MessageLoop message_loop; 2157 base::ScopedTempDir temp_dir; 2158 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 2159 FakeEncryptor encryptor; 2160 TestUnrecoverableErrorHandler handler; 2161 NullDirectoryChangeDelegate delegate; 2162 std::string dirname = "stress"; 2163 Directory dir(new InMemoryDirectoryBackingStore(dirname), 2164 &handler, 2165 NULL, 2166 NULL, 2167 NULL); 2168 dir.Open(dirname, &delegate, NullTransactionObserver()); 2169 2170 const int kThreadCount = 7; 2171 base::PlatformThreadHandle threads[kThreadCount]; 2172 scoped_ptr<StressTransactionsDelegate> thread_delegates[kThreadCount]; 2173 2174 for (int i = 0; i < kThreadCount; ++i) { 2175 thread_delegates[i].reset(new StressTransactionsDelegate(&dir, i)); 2176 ASSERT_TRUE(base::PlatformThread::Create( 2177 0, thread_delegates[i].get(), &threads[i])); 2178 } 2179 2180 for (int i = 0; i < kThreadCount; ++i) { 2181 base::PlatformThread::Join(threads[i]); 2182 } 2183 2184 dir.Close(); 2185} 2186 2187class SyncableClientTagTest : public SyncableDirectoryTest { 2188 public: 2189 static const int kBaseVersion = 1; 2190 const char* test_name_; 2191 const char* test_tag_; 2192 2193 SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {} 2194 2195 bool CreateWithDefaultTag(Id id, bool deleted) { 2196 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get()); 2197 MutableEntry me(&wtrans, CREATE, PREFERENCES, 2198 wtrans.root_id(), test_name_); 2199 CHECK(me.good()); 2200 me.PutId(id); 2201 if (id.ServerKnows()) { 2202 me.PutBaseVersion(kBaseVersion); 2203 } 2204 me.PutIsUnsynced(true); 2205 me.PutIsDel(deleted); 2206 me.PutIsDir(false); 2207 return me.PutUniqueClientTag(test_tag_); 2208 } 2209 2210 // Verify an entry exists with the default tag. 2211 void VerifyTag(Id id, bool deleted) { 2212 // Should still be present and valid in the client tag index. 2213 ReadTransaction trans(FROM_HERE, dir_.get()); 2214 Entry me(&trans, GET_BY_CLIENT_TAG, test_tag_); 2215 CHECK(me.good()); 2216 EXPECT_EQ(me.GetId(), id); 2217 EXPECT_EQ(me.GetUniqueClientTag(), test_tag_); 2218 EXPECT_EQ(me.GetIsDel(), deleted); 2219 2220 // We only sync deleted items that the server knew about. 2221 if (me.GetId().ServerKnows() || !me.GetIsDel()) { 2222 EXPECT_EQ(me.GetIsUnsynced(), true); 2223 } 2224 } 2225 2226 protected: 2227 TestIdFactory factory_; 2228}; 2229 2230TEST_F(SyncableClientTagTest, TestClientTagClear) { 2231 Id server_id = factory_.NewServerId(); 2232 EXPECT_TRUE(CreateWithDefaultTag(server_id, false)); 2233 { 2234 WriteTransaction trans(FROM_HERE, UNITTEST, dir_.get()); 2235 MutableEntry me(&trans, GET_BY_CLIENT_TAG, test_tag_); 2236 EXPECT_TRUE(me.good()); 2237 me.PutUniqueClientTag(std::string()); 2238 } 2239 { 2240 ReadTransaction trans(FROM_HERE, dir_.get()); 2241 Entry by_tag(&trans, GET_BY_CLIENT_TAG, test_tag_); 2242 EXPECT_FALSE(by_tag.good()); 2243 2244 Entry by_id(&trans, GET_BY_ID, server_id); 2245 EXPECT_TRUE(by_id.good()); 2246 EXPECT_TRUE(by_id.GetUniqueClientTag().empty()); 2247 } 2248} 2249 2250TEST_F(SyncableClientTagTest, TestClientTagIndexServerId) { 2251 Id server_id = factory_.NewServerId(); 2252 EXPECT_TRUE(CreateWithDefaultTag(server_id, false)); 2253 VerifyTag(server_id, false); 2254} 2255 2256TEST_F(SyncableClientTagTest, TestClientTagIndexClientId) { 2257 Id client_id = factory_.NewLocalId(); 2258 EXPECT_TRUE(CreateWithDefaultTag(client_id, false)); 2259 VerifyTag(client_id, false); 2260} 2261 2262TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexClientId) { 2263 Id client_id = factory_.NewLocalId(); 2264 EXPECT_TRUE(CreateWithDefaultTag(client_id, true)); 2265 VerifyTag(client_id, true); 2266} 2267 2268TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexServerId) { 2269 Id server_id = factory_.NewServerId(); 2270 EXPECT_TRUE(CreateWithDefaultTag(server_id, true)); 2271 VerifyTag(server_id, true); 2272} 2273 2274TEST_F(SyncableClientTagTest, TestClientTagIndexDuplicateServer) { 2275 EXPECT_TRUE(CreateWithDefaultTag(factory_.NewServerId(), true)); 2276 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), true)); 2277 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), false)); 2278 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), false)); 2279 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), true)); 2280} 2281 2282} // namespace 2283} // namespace syncable 2284} // namespace syncer 2285