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