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/synchronization/condition_variable.h" 18 #include "base/test/values_test_util.h" 19 #include "base/threading/platform_thread.h" 20 #include "base/values.h" 21 #include "sync/protocol/bookmark_specifics.pb.h" 22 #include "sync/syncable/directory_backing_store.h" 23 #include "sync/syncable/directory_change_delegate.h" 24 #include "sync/syncable/directory_unittest.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 41 namespace syncer { 42 namespace syncable { 43 44 using base::ExpectDictBooleanValue; 45 using base::ExpectDictStringValue; 46 47 // An OnDiskDirectoryBackingStore that can be set to always fail SaveChanges. 48 class TestBackingStore : public OnDiskDirectoryBackingStore { 49 public: 50 TestBackingStore(const std::string& dir_name, 51 const base::FilePath& backing_filepath); 52 53 virtual ~TestBackingStore(); 54 55 virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot) 56 OVERRIDE; 57 58 void StartFailingSaveChanges() { 59 fail_save_changes_ = true; 60 } 61 62 private: 63 bool fail_save_changes_; 64 }; 65 66 TestBackingStore::TestBackingStore(const std::string& dir_name, 67 const base::FilePath& backing_filepath) 68 : OnDiskDirectoryBackingStore(dir_name, backing_filepath), 69 fail_save_changes_(false) { 70 } 71 72 TestBackingStore::~TestBackingStore() { } 73 74 bool TestBackingStore::SaveChanges( 75 const Directory::SaveChangesSnapshot& snapshot){ 76 if (fail_save_changes_) { 77 return false; 78 } else { 79 return OnDiskDirectoryBackingStore::SaveChanges(snapshot); 80 } 81 } 82 83 // A directory whose Save() function can be set to always fail. 84 class TestDirectory : public Directory { 85 public: 86 // A factory function used to work around some initialization order issues. 87 static TestDirectory* Create( 88 Encryptor *encryptor, 89 UnrecoverableErrorHandler *handler, 90 const std::string& dir_name, 91 const base::FilePath& backing_filepath); 92 93 virtual ~TestDirectory(); 94 95 void StartFailingSaveChanges() { 96 backing_store_->StartFailingSaveChanges(); 97 } 98 99 private: 100 TestDirectory(Encryptor* encryptor, 101 UnrecoverableErrorHandler* handler, 102 TestBackingStore* backing_store); 103 104 TestBackingStore* backing_store_; 105 }; 106 107 TestDirectory* TestDirectory::Create( 108 Encryptor *encryptor, 109 UnrecoverableErrorHandler *handler, 110 const std::string& dir_name, 111 const base::FilePath& backing_filepath) { 112 TestBackingStore* backing_store = 113 new TestBackingStore(dir_name, backing_filepath); 114 return new TestDirectory(encryptor, handler, backing_store); 115 } 116 117 TestDirectory::TestDirectory(Encryptor* encryptor, 118 UnrecoverableErrorHandler* handler, 119 TestBackingStore* backing_store) 120 : Directory(backing_store, handler, NULL, NULL, NULL), 121 backing_store_(backing_store) { 122 } 123 124 TestDirectory::~TestDirectory() { } 125 126 TEST(OnDiskSyncableDirectory, FailInitialWrite) { 127 FakeEncryptor encryptor; 128 TestUnrecoverableErrorHandler handler; 129 base::ScopedTempDir temp_dir; 130 ASSERT_TRUE(temp_dir.CreateUniqueTempDir()); 131 base::FilePath file_path = temp_dir.path().Append( 132 FILE_PATH_LITERAL("Test.sqlite3")); 133 std::string name = "user (at) x.com"; 134 NullDirectoryChangeDelegate delegate; 135 136 scoped_ptr<TestDirectory> test_dir( 137 TestDirectory::Create(&encryptor, &handler, name, file_path)); 138 139 test_dir->StartFailingSaveChanges(); 140 ASSERT_EQ(FAILED_INITIAL_WRITE, test_dir->Open(name, &delegate, 141 NullTransactionObserver())); 142 } 143 144 // A variant of SyncableDirectoryTest that uses a real sqlite database. 145 class OnDiskSyncableDirectoryTest : public SyncableDirectoryTest { 146 protected: 147 // SetUp() is called before each test case is run. 148 // The sqlite3 DB is deleted before each test is run. 149 virtual void SetUp() { 150 SyncableDirectoryTest::SetUp(); 151 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 152 file_path_ = temp_dir_.path().Append( 153 FILE_PATH_LITERAL("Test.sqlite3")); 154 base::DeleteFile(file_path_, true); 155 CreateDirectory(); 156 } 157 158 virtual void TearDown() { 159 // This also closes file handles. 160 dir()->SaveChanges(); 161 dir().reset(); 162 base::DeleteFile(file_path_, true); 163 SyncableDirectoryTest::TearDown(); 164 } 165 166 // Creates a new directory. Deletes the old directory, if it exists. 167 void CreateDirectory() { 168 test_directory_ = TestDirectory::Create( 169 encryptor(), unrecoverable_error_handler(), kDirectoryName, file_path_); 170 dir().reset(test_directory_); 171 ASSERT_TRUE(dir().get()); 172 ASSERT_EQ(OPENED, 173 dir()->Open(kDirectoryName, 174 directory_change_delegate(), 175 NullTransactionObserver())); 176 ASSERT_TRUE(dir()->good()); 177 } 178 179 void SaveAndReloadDir() { 180 dir()->SaveChanges(); 181 CreateDirectory(); 182 } 183 184 void StartFailingSaveChanges() { 185 test_directory_->StartFailingSaveChanges(); 186 } 187 188 TestDirectory *test_directory_; // mirrors scoped_ptr<Directory> dir_ 189 base::ScopedTempDir temp_dir_; 190 base::FilePath file_path_; 191 }; 192 193 sync_pb::DataTypeProgressMarker BuildProgress(ModelType type) { 194 sync_pb::DataTypeProgressMarker progress; 195 progress.set_token("token"); 196 progress.set_data_type_id(GetSpecificsFieldNumberFromModelType(type)); 197 return progress; 198 } 199 200 sync_pb::DataTypeContext BuildContext(ModelType type) { 201 sync_pb::DataTypeContext context; 202 context.set_context("context"); 203 context.set_data_type_id(GetSpecificsFieldNumberFromModelType(type)); 204 return context; 205 } 206 207 TEST_F(OnDiskSyncableDirectoryTest, TestPurgeEntriesWithTypeIn) { 208 sync_pb::EntitySpecifics bookmark_specs; 209 sync_pb::EntitySpecifics autofill_specs; 210 sync_pb::EntitySpecifics preference_specs; 211 AddDefaultFieldValue(BOOKMARKS, &bookmark_specs); 212 AddDefaultFieldValue(PREFERENCES, &preference_specs); 213 AddDefaultFieldValue(AUTOFILL, &autofill_specs); 214 215 ModelTypeSet types_to_purge(PREFERENCES, AUTOFILL); 216 217 dir()->SetDownloadProgress(BOOKMARKS, BuildProgress(BOOKMARKS)); 218 dir()->SetDownloadProgress(PREFERENCES, BuildProgress(PREFERENCES)); 219 dir()->SetDownloadProgress(AUTOFILL, BuildProgress(AUTOFILL)); 220 221 TestIdFactory id_factory; 222 // Create some items for each type. 223 { 224 WriteTransaction trans(FROM_HERE, UNITTEST, dir().get()); 225 226 dir()->SetDataTypeContext(&trans, BOOKMARKS, BuildContext(BOOKMARKS)); 227 dir()->SetDataTypeContext(&trans, PREFERENCES, BuildContext(PREFERENCES)); 228 dir()->SetDataTypeContext(&trans, AUTOFILL, BuildContext(AUTOFILL)); 229 230 // Make it look like these types have completed initial sync. 231 CreateTypeRoot(&trans, dir().get(), BOOKMARKS); 232 CreateTypeRoot(&trans, dir().get(), PREFERENCES); 233 CreateTypeRoot(&trans, dir().get(), AUTOFILL); 234 235 // Add more nodes for this type. Technically, they should be placed under 236 // the proper type root nodes but the assertions in this test won't notice 237 // if their parent isn't quite right. 238 MutableEntry item1(&trans, CREATE, BOOKMARKS, trans.root_id(), "Item"); 239 ASSERT_TRUE(item1.good()); 240 item1.PutServerSpecifics(bookmark_specs); 241 item1.PutIsUnsynced(true); 242 243 MutableEntry item2(&trans, CREATE_NEW_UPDATE_ITEM, 244 id_factory.NewServerId()); 245 ASSERT_TRUE(item2.good()); 246 item2.PutServerSpecifics(bookmark_specs); 247 item2.PutIsUnappliedUpdate(true); 248 249 MutableEntry item3(&trans, CREATE, PREFERENCES, 250 trans.root_id(), "Item"); 251 ASSERT_TRUE(item3.good()); 252 item3.PutSpecifics(preference_specs); 253 item3.PutServerSpecifics(preference_specs); 254 item3.PutIsUnsynced(true); 255 256 MutableEntry item4(&trans, CREATE_NEW_UPDATE_ITEM, 257 id_factory.NewServerId()); 258 ASSERT_TRUE(item4.good()); 259 item4.PutServerSpecifics(preference_specs); 260 item4.PutIsUnappliedUpdate(true); 261 262 MutableEntry item5(&trans, CREATE, AUTOFILL, 263 trans.root_id(), "Item"); 264 ASSERT_TRUE(item5.good()); 265 item5.PutSpecifics(autofill_specs); 266 item5.PutServerSpecifics(autofill_specs); 267 item5.PutIsUnsynced(true); 268 269 MutableEntry item6(&trans, CREATE_NEW_UPDATE_ITEM, 270 id_factory.NewServerId()); 271 ASSERT_TRUE(item6.good()); 272 item6.PutServerSpecifics(autofill_specs); 273 item6.PutIsUnappliedUpdate(true); 274 } 275 276 dir()->SaveChanges(); 277 { 278 ReadTransaction trans(FROM_HERE, dir().get()); 279 MetahandleSet all_set; 280 GetAllMetaHandles(&trans, &all_set); 281 ASSERT_EQ(10U, all_set.size()); 282 } 283 284 dir()->PurgeEntriesWithTypeIn(types_to_purge, ModelTypeSet(), ModelTypeSet()); 285 286 // We first query the in-memory data, and then reload the directory (without 287 // saving) to verify that disk does not still have the data. 288 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, true); 289 SaveAndReloadDir(); 290 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, false); 291 } 292 293 TEST_F(OnDiskSyncableDirectoryTest, TestShareInfo) { 294 dir()->set_store_birthday("Jan 31st"); 295 const char* const bag_of_chips_array = "\0bag of chips"; 296 const std::string bag_of_chips_string = 297 std::string(bag_of_chips_array, sizeof(bag_of_chips_array)); 298 dir()->set_bag_of_chips(bag_of_chips_string); 299 { 300 ReadTransaction trans(FROM_HERE, dir().get()); 301 EXPECT_EQ("Jan 31st", dir()->store_birthday()); 302 EXPECT_EQ(bag_of_chips_string, dir()->bag_of_chips()); 303 } 304 dir()->set_store_birthday("April 10th"); 305 const char* const bag_of_chips2_array = "\0bag of chips2"; 306 const std::string bag_of_chips2_string = 307 std::string(bag_of_chips2_array, sizeof(bag_of_chips2_array)); 308 dir()->set_bag_of_chips(bag_of_chips2_string); 309 dir()->SaveChanges(); 310 { 311 ReadTransaction trans(FROM_HERE, dir().get()); 312 EXPECT_EQ("April 10th", dir()->store_birthday()); 313 EXPECT_EQ(bag_of_chips2_string, dir()->bag_of_chips()); 314 } 315 const char* const bag_of_chips3_array = "\0bag of chips3"; 316 const std::string bag_of_chips3_string = 317 std::string(bag_of_chips3_array, sizeof(bag_of_chips3_array)); 318 dir()->set_bag_of_chips(bag_of_chips3_string); 319 // Restore the directory from disk. Make sure that nothing's changed. 320 SaveAndReloadDir(); 321 { 322 ReadTransaction trans(FROM_HERE, dir().get()); 323 EXPECT_EQ("April 10th", dir()->store_birthday()); 324 EXPECT_EQ(bag_of_chips3_string, dir()->bag_of_chips()); 325 } 326 } 327 328 TEST_F(OnDiskSyncableDirectoryTest, 329 TestSimpleFieldsPreservedDuringSaveChanges) { 330 Id update_id = TestIdFactory::FromNumber(1); 331 Id create_id; 332 EntryKernel create_pre_save, update_pre_save; 333 EntryKernel create_post_save, update_post_save; 334 std::string create_name = "Create"; 335 336 { 337 WriteTransaction trans(FROM_HERE, UNITTEST, dir().get()); 338 MutableEntry create( 339 &trans, CREATE, BOOKMARKS, trans.root_id(), create_name); 340 MutableEntry update(&trans, CREATE_NEW_UPDATE_ITEM, update_id); 341 create.PutIsUnsynced(true); 342 update.PutIsUnappliedUpdate(true); 343 sync_pb::EntitySpecifics specifics; 344 specifics.mutable_bookmark()->set_favicon("PNG"); 345 specifics.mutable_bookmark()->set_url("http://nowhere"); 346 create.PutSpecifics(specifics); 347 update.PutServerSpecifics(specifics); 348 create_pre_save = create.GetKernelCopy(); 349 update_pre_save = update.GetKernelCopy(); 350 create_id = create.GetId(); 351 } 352 353 dir()->SaveChanges(); 354 dir().reset( 355 new Directory(new OnDiskDirectoryBackingStore(kDirectoryName, file_path_), 356 unrecoverable_error_handler(), 357 NULL, 358 NULL, 359 NULL)); 360 361 ASSERT_TRUE(dir().get()); 362 ASSERT_EQ(OPENED, 363 dir()->Open(kDirectoryName, 364 directory_change_delegate(), 365 NullTransactionObserver())); 366 ASSERT_TRUE(dir()->good()); 367 368 { 369 ReadTransaction trans(FROM_HERE, dir().get()); 370 Entry create(&trans, GET_BY_ID, create_id); 371 EXPECT_EQ(1, CountEntriesWithName(&trans, trans.root_id(), create_name)); 372 Entry update(&trans, GET_BY_ID, update_id); 373 create_post_save = create.GetKernelCopy(); 374 update_post_save = update.GetKernelCopy(); 375 } 376 int i = BEGIN_FIELDS; 377 for ( ; i < INT64_FIELDS_END ; ++i) { 378 EXPECT_EQ(create_pre_save.ref((Int64Field)i) + 379 (i == TRANSACTION_VERSION ? 1 : 0), 380 create_post_save.ref((Int64Field)i)) 381 << "int64 field #" << i << " changed during save/load"; 382 EXPECT_EQ(update_pre_save.ref((Int64Field)i), 383 update_post_save.ref((Int64Field)i)) 384 << "int64 field #" << i << " changed during save/load"; 385 } 386 for ( ; i < TIME_FIELDS_END ; ++i) { 387 EXPECT_EQ(create_pre_save.ref((TimeField)i), 388 create_post_save.ref((TimeField)i)) 389 << "time field #" << i << " changed during save/load"; 390 EXPECT_EQ(update_pre_save.ref((TimeField)i), 391 update_post_save.ref((TimeField)i)) 392 << "time field #" << i << " changed during save/load"; 393 } 394 for ( ; i < ID_FIELDS_END ; ++i) { 395 EXPECT_EQ(create_pre_save.ref((IdField)i), 396 create_post_save.ref((IdField)i)) 397 << "id field #" << i << " changed during save/load"; 398 EXPECT_EQ(update_pre_save.ref((IdField)i), 399 update_pre_save.ref((IdField)i)) 400 << "id field #" << i << " changed during save/load"; 401 } 402 for ( ; i < BIT_FIELDS_END ; ++i) { 403 EXPECT_EQ(create_pre_save.ref((BitField)i), 404 create_post_save.ref((BitField)i)) 405 << "Bit field #" << i << " changed during save/load"; 406 EXPECT_EQ(update_pre_save.ref((BitField)i), 407 update_post_save.ref((BitField)i)) 408 << "Bit field #" << i << " changed during save/load"; 409 } 410 for ( ; i < STRING_FIELDS_END ; ++i) { 411 EXPECT_EQ(create_pre_save.ref((StringField)i), 412 create_post_save.ref((StringField)i)) 413 << "String field #" << i << " changed during save/load"; 414 EXPECT_EQ(update_pre_save.ref((StringField)i), 415 update_post_save.ref((StringField)i)) 416 << "String field #" << i << " changed during save/load"; 417 } 418 for ( ; i < PROTO_FIELDS_END; ++i) { 419 EXPECT_EQ(create_pre_save.ref((ProtoField)i).SerializeAsString(), 420 create_post_save.ref((ProtoField)i).SerializeAsString()) 421 << "Blob field #" << i << " changed during save/load"; 422 EXPECT_EQ(update_pre_save.ref((ProtoField)i).SerializeAsString(), 423 update_post_save.ref((ProtoField)i).SerializeAsString()) 424 << "Blob field #" << i << " changed during save/load"; 425 } 426 for ( ; i < UNIQUE_POSITION_FIELDS_END; ++i) { 427 EXPECT_TRUE(create_pre_save.ref((UniquePositionField)i).Equals( 428 create_post_save.ref((UniquePositionField)i))) 429 << "Position field #" << i << " changed during save/load"; 430 EXPECT_TRUE(update_pre_save.ref((UniquePositionField)i).Equals( 431 update_post_save.ref((UniquePositionField)i))) 432 << "Position field #" << i << " changed during save/load"; 433 } 434 } 435 436 TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailure) { 437 int64 handle1 = 0; 438 // Set up an item using a regular, saveable directory. 439 { 440 WriteTransaction trans(FROM_HERE, UNITTEST, dir().get()); 441 442 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "aguilera"); 443 ASSERT_TRUE(e1.good()); 444 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 445 handle1 = e1.GetMetahandle(); 446 e1.PutBaseVersion(1); 447 e1.PutIsDir(true); 448 e1.PutId(TestIdFactory::FromNumber(101)); 449 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 450 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 451 } 452 ASSERT_TRUE(dir()->SaveChanges()); 453 454 // Make sure the item is no longer dirty after saving, 455 // and make a modification. 456 { 457 WriteTransaction trans(FROM_HERE, UNITTEST, dir().get()); 458 459 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1); 460 ASSERT_TRUE(aguilera.good()); 461 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 462 EXPECT_EQ(aguilera.GetNonUniqueName(), "aguilera"); 463 aguilera.PutNonUniqueName("overwritten"); 464 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty()); 465 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 466 } 467 ASSERT_TRUE(dir()->SaveChanges()); 468 469 // Now do some operations when SaveChanges() will fail. 470 StartFailingSaveChanges(); 471 ASSERT_TRUE(dir()->good()); 472 473 int64 handle2 = 0; 474 { 475 WriteTransaction trans(FROM_HERE, UNITTEST, dir().get()); 476 477 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1); 478 ASSERT_TRUE(aguilera.good()); 479 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 480 EXPECT_EQ(aguilera.GetNonUniqueName(), "overwritten"); 481 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 482 EXPECT_FALSE(IsInDirtyMetahandles(handle1)); 483 aguilera.PutNonUniqueName("christina"); 484 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty()); 485 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 486 487 // New item. 488 MutableEntry kids_on_block( 489 &trans, CREATE, BOOKMARKS, trans.root_id(), "kids"); 490 ASSERT_TRUE(kids_on_block.good()); 491 handle2 = kids_on_block.GetMetahandle(); 492 kids_on_block.PutBaseVersion(1); 493 kids_on_block.PutIsDir(true); 494 kids_on_block.PutId(TestIdFactory::FromNumber(102)); 495 EXPECT_TRUE(kids_on_block.GetKernelCopy().is_dirty()); 496 EXPECT_TRUE(IsInDirtyMetahandles(handle2)); 497 } 498 499 // We are using an unsaveable directory, so this can't succeed. However, 500 // the HandleSaveChangesFailure code path should have been triggered. 501 ASSERT_FALSE(dir()->SaveChanges()); 502 503 // Make sure things were rolled back and the world is as it was before call. 504 { 505 ReadTransaction trans(FROM_HERE, dir().get()); 506 Entry e1(&trans, GET_BY_HANDLE, handle1); 507 ASSERT_TRUE(e1.good()); 508 EntryKernel aguilera = e1.GetKernelCopy(); 509 Entry kids(&trans, GET_BY_HANDLE, handle2); 510 ASSERT_TRUE(kids.good()); 511 EXPECT_TRUE(kids.GetKernelCopy().is_dirty()); 512 EXPECT_TRUE(IsInDirtyMetahandles(handle2)); 513 EXPECT_TRUE(aguilera.is_dirty()); 514 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 515 } 516 } 517 518 TEST_F(OnDiskSyncableDirectoryTest, TestSaveChangesFailureWithPurge) { 519 int64 handle1 = 0; 520 // Set up an item using a regular, saveable directory. 521 { 522 WriteTransaction trans(FROM_HERE, UNITTEST, dir().get()); 523 524 MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "aguilera"); 525 ASSERT_TRUE(e1.good()); 526 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 527 handle1 = e1.GetMetahandle(); 528 e1.PutBaseVersion(1); 529 e1.PutIsDir(true); 530 e1.PutId(TestIdFactory::FromNumber(101)); 531 sync_pb::EntitySpecifics bookmark_specs; 532 AddDefaultFieldValue(BOOKMARKS, &bookmark_specs); 533 e1.PutSpecifics(bookmark_specs); 534 e1.PutServerSpecifics(bookmark_specs); 535 e1.PutId(TestIdFactory::FromNumber(101)); 536 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 537 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 538 } 539 ASSERT_TRUE(dir()->SaveChanges()); 540 541 // Now do some operations while SaveChanges() is set to fail. 542 StartFailingSaveChanges(); 543 ASSERT_TRUE(dir()->good()); 544 545 ModelTypeSet set(BOOKMARKS); 546 dir()->PurgeEntriesWithTypeIn(set, ModelTypeSet(), ModelTypeSet()); 547 EXPECT_TRUE(IsInMetahandlesToPurge(handle1)); 548 ASSERT_FALSE(dir()->SaveChanges()); 549 EXPECT_TRUE(IsInMetahandlesToPurge(handle1)); 550 } 551 552 class SyncableDirectoryManagement : public testing::Test { 553 public: 554 virtual void SetUp() { 555 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 556 } 557 558 virtual void TearDown() { 559 } 560 protected: 561 base::MessageLoop message_loop_; 562 base::ScopedTempDir temp_dir_; 563 FakeEncryptor encryptor_; 564 TestUnrecoverableErrorHandler handler_; 565 NullDirectoryChangeDelegate delegate_; 566 }; 567 568 TEST_F(SyncableDirectoryManagement, TestFileRelease) { 569 base::FilePath path = 570 temp_dir_.path().Append(Directory::kSyncDatabaseFilename); 571 572 Directory dir(new OnDiskDirectoryBackingStore("ScopeTest", path), 573 &handler_, 574 NULL, 575 NULL, 576 NULL); 577 DirOpenResult result = 578 dir.Open("ScopeTest", &delegate_, NullTransactionObserver()); 579 ASSERT_EQ(result, OPENED); 580 dir.Close(); 581 582 // Closing the directory should have released the backing database file. 583 ASSERT_TRUE(base::DeleteFile(path, true)); 584 } 585 586 class SyncableClientTagTest : public SyncableDirectoryTest { 587 public: 588 static const int kBaseVersion = 1; 589 const char* test_name_; 590 const char* test_tag_; 591 592 SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {} 593 594 bool CreateWithDefaultTag(Id id, bool deleted) { 595 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get()); 596 MutableEntry me(&wtrans, CREATE, PREFERENCES, 597 wtrans.root_id(), test_name_); 598 CHECK(me.good()); 599 me.PutId(id); 600 if (id.ServerKnows()) { 601 me.PutBaseVersion(kBaseVersion); 602 } 603 me.PutIsUnsynced(true); 604 me.PutIsDel(deleted); 605 me.PutIsDir(false); 606 return me.PutUniqueClientTag(test_tag_); 607 } 608 609 // Verify an entry exists with the default tag. 610 void VerifyTag(Id id, bool deleted) { 611 // Should still be present and valid in the client tag index. 612 ReadTransaction trans(FROM_HERE, dir().get()); 613 Entry me(&trans, GET_BY_CLIENT_TAG, test_tag_); 614 CHECK(me.good()); 615 EXPECT_EQ(me.GetId(), id); 616 EXPECT_EQ(me.GetUniqueClientTag(), test_tag_); 617 EXPECT_EQ(me.GetIsDel(), deleted); 618 619 // We only sync deleted items that the server knew about. 620 if (me.GetId().ServerKnows() || !me.GetIsDel()) { 621 EXPECT_EQ(me.GetIsUnsynced(), true); 622 } 623 } 624 625 protected: 626 TestIdFactory factory_; 627 }; 628 629 TEST_F(SyncableClientTagTest, TestClientTagClear) { 630 Id server_id = factory_.NewServerId(); 631 EXPECT_TRUE(CreateWithDefaultTag(server_id, false)); 632 { 633 WriteTransaction trans(FROM_HERE, UNITTEST, dir().get()); 634 MutableEntry me(&trans, GET_BY_CLIENT_TAG, test_tag_); 635 EXPECT_TRUE(me.good()); 636 me.PutUniqueClientTag(std::string()); 637 } 638 { 639 ReadTransaction trans(FROM_HERE, dir().get()); 640 Entry by_tag(&trans, GET_BY_CLIENT_TAG, test_tag_); 641 EXPECT_FALSE(by_tag.good()); 642 643 Entry by_id(&trans, GET_BY_ID, server_id); 644 EXPECT_TRUE(by_id.good()); 645 EXPECT_TRUE(by_id.GetUniqueClientTag().empty()); 646 } 647 } 648 649 TEST_F(SyncableClientTagTest, TestClientTagIndexServerId) { 650 Id server_id = factory_.NewServerId(); 651 EXPECT_TRUE(CreateWithDefaultTag(server_id, false)); 652 VerifyTag(server_id, false); 653 } 654 655 TEST_F(SyncableClientTagTest, TestClientTagIndexClientId) { 656 Id client_id = factory_.NewLocalId(); 657 EXPECT_TRUE(CreateWithDefaultTag(client_id, false)); 658 VerifyTag(client_id, false); 659 } 660 661 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexClientId) { 662 Id client_id = factory_.NewLocalId(); 663 EXPECT_TRUE(CreateWithDefaultTag(client_id, true)); 664 VerifyTag(client_id, true); 665 } 666 667 TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexServerId) { 668 Id server_id = factory_.NewServerId(); 669 EXPECT_TRUE(CreateWithDefaultTag(server_id, true)); 670 VerifyTag(server_id, true); 671 } 672 673 TEST_F(SyncableClientTagTest, TestClientTagIndexDuplicateServer) { 674 EXPECT_TRUE(CreateWithDefaultTag(factory_.NewServerId(), true)); 675 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), true)); 676 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), false)); 677 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), false)); 678 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), true)); 679 } 680 681 } // namespace syncable 682 } // namespace syncer 683