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