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