syncable_unittest.cc revision 3f50c38dc070f4bb515c1b64450dae14f316474e
1// Copyright (c) 2010 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 "chrome/browser/sync/syncable/syncable.h" 6 7#include "build/build_config.h" 8 9#include <sys/types.h> 10 11#include <limits> 12#include <string> 13 14#if !defined(OS_WIN) 15#define MAX_PATH PATH_MAX 16#include <ostream> 17#include <stdio.h> 18#include <sys/ipc.h> 19#include <sys/sem.h> 20#include <sys/times.h> 21#endif // !defined(OS_WIN) 22 23#include "base/file_path.h" 24#include "base/file_util.h" 25#include "base/logging.h" 26#include "base/scoped_ptr.h" 27#include "base/scoped_temp_dir.h" 28#include "base/string_util.h" 29#include "base/threading/platform_thread.h" 30#include "chrome/browser/sync/engine/syncproto.h" 31#include "chrome/browser/sync/protocol/bookmark_specifics.pb.h" 32#include "chrome/browser/sync/syncable/directory_backing_store.h" 33#include "chrome/browser/sync/syncable/directory_manager.h" 34#include "chrome/common/deprecated/event_sys-inl.h" 35#include "chrome/test/sync/engine/test_id_factory.h" 36#include "chrome/test/sync/engine/test_syncable_utils.h" 37#include "testing/gtest/include/gtest/gtest.h" 38#include "third_party/sqlite/sqlite3.h" 39 40using browser_sync::TestIdFactory; 41 42namespace syncable { 43 44namespace { 45void PutDataAsBookmarkFavicon(WriteTransaction* wtrans, 46 MutableEntry* e, 47 const char* bytes, 48 size_t bytes_length) { 49 sync_pb::EntitySpecifics specifics; 50 specifics.MutableExtension(sync_pb::bookmark)->set_url("http://demo/"); 51 specifics.MutableExtension(sync_pb::bookmark)->set_favicon(bytes, 52 bytes_length); 53 e->Put(SPECIFICS, specifics); 54} 55 56void ExpectDataFromBookmarkFaviconEquals(BaseTransaction* trans, 57 Entry* e, 58 const char* bytes, 59 size_t bytes_length) { 60 ASSERT_TRUE(e->good()); 61 ASSERT_TRUE(e->Get(SPECIFICS).HasExtension(sync_pb::bookmark)); 62 ASSERT_EQ("http://demo/", 63 e->Get(SPECIFICS).GetExtension(sync_pb::bookmark).url()); 64 ASSERT_EQ(std::string(bytes, bytes_length), 65 e->Get(SPECIFICS).GetExtension(sync_pb::bookmark).favicon()); 66} 67} // namespace 68 69class SyncableGeneralTest : public testing::Test { 70 public: 71 virtual void SetUp() { 72 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 73 db_path_ = temp_dir_.path().Append( 74 FILE_PATH_LITERAL("SyncableTest.sqlite3")); 75 } 76 77 virtual void TearDown() { 78 } 79 protected: 80 ScopedTempDir temp_dir_; 81 FilePath db_path_; 82}; 83 84TEST_F(SyncableGeneralTest, General) { 85 Directory dir; 86 dir.Open(db_path_, "SimpleTest"); 87 88 int64 written_metahandle; 89 const Id id = TestIdFactory::FromNumber(99); 90 std::string name = "Jeff"; 91 // Test simple read operations on an empty DB. 92 { 93 ReadTransaction rtrans(&dir, __FILE__, __LINE__); 94 Entry e(&rtrans, GET_BY_ID, id); 95 ASSERT_FALSE(e.good()); // Hasn't been written yet. 96 97 Directory::ChildHandles child_handles; 98 dir.GetChildHandles(&rtrans, rtrans.root_id(), &child_handles); 99 EXPECT_TRUE(child_handles.empty()); 100 } 101 102 // Test creating a new meta entry. 103 { 104 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__); 105 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), name); 106 ASSERT_TRUE(me.good()); 107 me.Put(ID, id); 108 me.Put(BASE_VERSION, 1); 109 written_metahandle = me.Get(META_HANDLE); 110 } 111 112 // Test GetChildHandles after something is now in the DB. 113 // Also check that GET_BY_ID works. 114 { 115 ReadTransaction rtrans(&dir, __FILE__, __LINE__); 116 Entry e(&rtrans, GET_BY_ID, id); 117 ASSERT_TRUE(e.good()); 118 119 Directory::ChildHandles child_handles; 120 dir.GetChildHandles(&rtrans, rtrans.root_id(), &child_handles); 121 EXPECT_EQ(1u, child_handles.size()); 122 123 for (Directory::ChildHandles::iterator i = child_handles.begin(); 124 i != child_handles.end(); ++i) { 125 EXPECT_EQ(*i, written_metahandle); 126 } 127 } 128 129 // Test writing data to an entity. Also check that GET_BY_HANDLE works. 130 static const char s[] = "Hello World."; 131 { 132 WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__); 133 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle); 134 ASSERT_TRUE(e.good()); 135 PutDataAsBookmarkFavicon(&trans, &e, s, sizeof(s)); 136 } 137 138 // Test reading back the contents that we just wrote. 139 { 140 WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__); 141 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle); 142 ASSERT_TRUE(e.good()); 143 ExpectDataFromBookmarkFaviconEquals(&trans, &e, s, sizeof(s)); 144 } 145 146 // Verify it exists in the folder. 147 { 148 ReadTransaction rtrans(&dir, __FILE__, __LINE__); 149 EXPECT_EQ(1, CountEntriesWithName(&rtrans, rtrans.root_id(), name)); 150 } 151 152 // Now delete it. 153 { 154 WriteTransaction trans(&dir, UNITTEST, __FILE__, __LINE__); 155 MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle); 156 e.Put(IS_DEL, true); 157 158 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name)); 159 } 160 161 dir.SaveChanges(); 162} 163 164TEST_F(SyncableGeneralTest, ClientIndexRebuildsProperly) { 165 int64 written_metahandle; 166 TestIdFactory factory; 167 const Id id = factory.NewServerId(); 168 std::string name = "cheesepuffs"; 169 std::string tag = "dietcoke"; 170 171 // Test creating a new meta entry. 172 { 173 Directory dir; 174 dir.Open(db_path_, "IndexTest"); 175 { 176 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__); 177 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), name); 178 ASSERT_TRUE(me.good()); 179 me.Put(ID, id); 180 me.Put(BASE_VERSION, 1); 181 me.Put(UNIQUE_CLIENT_TAG, tag); 182 written_metahandle = me.Get(META_HANDLE); 183 } 184 dir.SaveChanges(); 185 } 186 187 // The DB was closed. Now reopen it. This will cause index regeneration. 188 { 189 Directory dir; 190 dir.Open(db_path_, "IndexTest"); 191 192 ReadTransaction trans(&dir, __FILE__, __LINE__); 193 Entry me(&trans, GET_BY_CLIENT_TAG, tag); 194 ASSERT_TRUE(me.good()); 195 EXPECT_EQ(me.Get(ID), id); 196 EXPECT_EQ(me.Get(BASE_VERSION), 1); 197 EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), tag); 198 EXPECT_EQ(me.Get(META_HANDLE), written_metahandle); 199 } 200} 201 202TEST_F(SyncableGeneralTest, ClientIndexRebuildsDeletedProperly) { 203 TestIdFactory factory; 204 const Id id = factory.NewServerId(); 205 std::string tag = "dietcoke"; 206 207 // Test creating a deleted, unsynced, server meta entry. 208 { 209 Directory dir; 210 dir.Open(db_path_, "IndexTest"); 211 { 212 WriteTransaction wtrans(&dir, UNITTEST, __FILE__, __LINE__); 213 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), "deleted"); 214 ASSERT_TRUE(me.good()); 215 me.Put(ID, id); 216 me.Put(BASE_VERSION, 1); 217 me.Put(UNIQUE_CLIENT_TAG, tag); 218 me.Put(IS_DEL, true); 219 me.Put(IS_UNSYNCED, true); // Or it might be purged. 220 } 221 dir.SaveChanges(); 222 } 223 224 // The DB was closed. Now reopen it. This will cause index regeneration. 225 // Should still be present and valid in the client tag index. 226 { 227 Directory dir; 228 dir.Open(db_path_, "IndexTest"); 229 230 ReadTransaction trans(&dir, __FILE__, __LINE__); 231 Entry me(&trans, GET_BY_CLIENT_TAG, tag); 232 ASSERT_TRUE(me.good()); 233 EXPECT_EQ(me.Get(ID), id); 234 EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), tag); 235 EXPECT_TRUE(me.Get(IS_DEL)); 236 EXPECT_TRUE(me.Get(IS_UNSYNCED)); 237 } 238} 239 240// A Directory whose backing store always fails SaveChanges by returning false. 241class TestUnsaveableDirectory : public Directory { 242 public: 243 class UnsaveableBackingStore : public DirectoryBackingStore { 244 public: 245 UnsaveableBackingStore(const std::string& dir_name, 246 const FilePath& backing_filepath) 247 : DirectoryBackingStore(dir_name, backing_filepath) { } 248 virtual bool SaveChanges(const Directory::SaveChangesSnapshot& snapshot) { 249 return false; 250 } 251 }; 252 virtual DirectoryBackingStore* CreateBackingStore( 253 const std::string& dir_name, 254 const FilePath& backing_filepath) { 255 return new UnsaveableBackingStore(dir_name, backing_filepath); 256 } 257}; 258 259// Test suite for syncable::Directory. 260class SyncableDirectoryTest : public testing::Test { 261 protected: 262 static const FilePath::CharType kFilePath[]; 263 static const char kName[]; 264 static const Id kId; 265 266 // SetUp() is called before each test case is run. 267 // The sqlite3 DB is deleted before each test is run. 268 virtual void SetUp() { 269 file_path_ = FilePath(kFilePath); 270 file_util::Delete(file_path_, true); 271 dir_.reset(new Directory()); 272 ASSERT_TRUE(dir_.get()); 273 ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName)); 274 ASSERT_TRUE(dir_->good()); 275 } 276 277 virtual void TearDown() { 278 // This also closes file handles. 279 dir_->SaveChanges(); 280 dir_.reset(); 281 file_util::Delete(file_path_, true); 282 } 283 284 void ReloadDir() { 285 dir_.reset(new Directory()); 286 ASSERT_TRUE(dir_.get()); 287 ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName)); 288 } 289 290 void SaveAndReloadDir() { 291 dir_->SaveChanges(); 292 ReloadDir(); 293 } 294 295 bool IsInDirtyMetahandles(int64 metahandle) { 296 return 1 == dir_->kernel_->dirty_metahandles->count(metahandle); 297 } 298 299 bool IsInMetahandlesToPurge(int64 metahandle) { 300 return 1 == dir_->kernel_->metahandles_to_purge->count(metahandle); 301 } 302 303 void CheckPurgeEntriesWithTypeInSucceeded(const ModelTypeSet& types_to_purge, 304 bool before_reload) { 305 SCOPED_TRACE(testing::Message("Before reload: ") << before_reload); 306 { 307 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 308 MetahandleSet all_set; 309 dir_->GetAllMetaHandles(&trans, &all_set); 310 EXPECT_EQ(3U, all_set.size()); 311 if (before_reload) 312 EXPECT_EQ(4U, dir_->kernel_->metahandles_to_purge->size()); 313 for (MetahandleSet::iterator iter = all_set.begin(); 314 iter != all_set.end(); ++iter) { 315 Entry e(&trans, GET_BY_HANDLE, *iter); 316 if ((types_to_purge.count(e.GetModelType()) || 317 types_to_purge.count(e.GetServerModelType()))) { 318 FAIL() << "Illegal type should have been deleted."; 319 } 320 } 321 } 322 323 EXPECT_FALSE(dir_->initial_sync_ended_for_type(PREFERENCES)); 324 EXPECT_FALSE(dir_->initial_sync_ended_for_type(AUTOFILL)); 325 EXPECT_TRUE(dir_->initial_sync_ended_for_type(BOOKMARKS)); 326 327 EXPECT_EQ(0, dir_->last_download_timestamp(PREFERENCES)); 328 EXPECT_EQ(0, dir_->last_download_timestamp(AUTOFILL)); 329 EXPECT_EQ(1, dir_->last_download_timestamp(BOOKMARKS)); 330 } 331 332 scoped_ptr<Directory> dir_; 333 FilePath file_path_; 334 335 // Creates an empty entry and sets the ID field to the default kId. 336 void CreateEntry(const std::string& entryname) { 337 CreateEntry(entryname, kId); 338 } 339 340 // Creates an empty entry and sets the ID field to id. 341 void CreateEntry(const std::string& entryname, const int id) { 342 CreateEntry(entryname, TestIdFactory::FromNumber(id)); 343 } 344 void CreateEntry(const std::string& entryname, Id id) { 345 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__); 346 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), entryname); 347 ASSERT_TRUE(me.good()); 348 me.Put(ID, id); 349 me.Put(IS_UNSYNCED, true); 350 } 351 352 void ValidateEntry(BaseTransaction* trans, 353 int64 id, 354 bool check_name, 355 const std::string& name, 356 int64 base_version, 357 int64 server_version, 358 bool is_del); 359}; 360 361TEST_F(SyncableDirectoryTest, TakeSnapshotGetsMetahandlesToPurge) { 362 const int metas_to_create = 50; 363 MetahandleSet expected_purges; 364 MetahandleSet all_handles; 365 { 366 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 367 for (int i = 0; i < metas_to_create; i++) { 368 MutableEntry e(&trans, CREATE, trans.root_id(), "foo"); 369 e.Put(IS_UNSYNCED, true); 370 sync_pb::EntitySpecifics specs; 371 if (i % 2 == 0) { 372 AddDefaultExtensionValue(BOOKMARKS, &specs); 373 expected_purges.insert(e.Get(META_HANDLE)); 374 all_handles.insert(e.Get(META_HANDLE)); 375 } else { 376 AddDefaultExtensionValue(PREFERENCES, &specs); 377 all_handles.insert(e.Get(META_HANDLE)); 378 } 379 e.Put(SPECIFICS, specs); 380 e.Put(SERVER_SPECIFICS, specs); 381 } 382 } 383 384 ModelTypeSet to_purge; 385 to_purge.insert(BOOKMARKS); 386 dir_->PurgeEntriesWithTypeIn(to_purge); 387 388 Directory::SaveChangesSnapshot snapshot1; 389 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 390 dir_->TakeSnapshotForSaveChanges(&snapshot1); 391 EXPECT_TRUE(expected_purges == snapshot1.metahandles_to_purge); 392 393 to_purge.clear(); 394 to_purge.insert(PREFERENCES); 395 dir_->PurgeEntriesWithTypeIn(to_purge); 396 397 dir_->HandleSaveChangesFailure(snapshot1); 398 399 Directory::SaveChangesSnapshot snapshot2; 400 dir_->TakeSnapshotForSaveChanges(&snapshot2); 401 EXPECT_TRUE(all_handles == snapshot2.metahandles_to_purge); 402} 403 404TEST_F(SyncableDirectoryTest, TakeSnapshotGetsAllDirtyHandlesTest) { 405 const int metahandles_to_create = 100; 406 std::vector<int64> expected_dirty_metahandles; 407 { 408 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 409 for (int i = 0; i < metahandles_to_create; i++) { 410 MutableEntry e(&trans, CREATE, trans.root_id(), "foo"); 411 expected_dirty_metahandles.push_back(e.Get(META_HANDLE)); 412 e.Put(IS_UNSYNCED, true); 413 } 414 } 415 // Fake SaveChanges() and make sure we got what we expected. 416 { 417 Directory::SaveChangesSnapshot snapshot; 418 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 419 dir_->TakeSnapshotForSaveChanges(&snapshot); 420 // Make sure there's an entry for each new metahandle. Make sure all 421 // entries are marked dirty. 422 ASSERT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size()); 423 for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin(); 424 i != snapshot.dirty_metas.end(); ++i) { 425 ASSERT_TRUE(i->is_dirty()); 426 } 427 dir_->VacuumAfterSaveChanges(snapshot); 428 } 429 // Put a new value with existing transactions as well as adding new ones. 430 { 431 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 432 std::vector<int64> new_dirty_metahandles; 433 for (std::vector<int64>::const_iterator i = 434 expected_dirty_metahandles.begin(); 435 i != expected_dirty_metahandles.end(); ++i) { 436 // Change existing entries to directories to dirty them. 437 MutableEntry e1(&trans, GET_BY_HANDLE, *i); 438 e1.Put(IS_DIR, true); 439 e1.Put(IS_UNSYNCED, true); 440 // Add new entries 441 MutableEntry e2(&trans, CREATE, trans.root_id(), "bar"); 442 e2.Put(IS_UNSYNCED, true); 443 new_dirty_metahandles.push_back(e2.Get(META_HANDLE)); 444 } 445 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(), 446 new_dirty_metahandles.begin(), new_dirty_metahandles.end()); 447 } 448 // Fake SaveChanges() and make sure we got what we expected. 449 { 450 Directory::SaveChangesSnapshot snapshot; 451 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 452 dir_->TakeSnapshotForSaveChanges(&snapshot); 453 // Make sure there's an entry for each new metahandle. Make sure all 454 // entries are marked dirty. 455 EXPECT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size()); 456 for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin(); 457 i != snapshot.dirty_metas.end(); ++i) { 458 EXPECT_TRUE(i->is_dirty()); 459 } 460 dir_->VacuumAfterSaveChanges(snapshot); 461 } 462} 463 464TEST_F(SyncableDirectoryTest, TestPurgeEntriesWithTypeIn) { 465 sync_pb::EntitySpecifics bookmark_specs; 466 sync_pb::EntitySpecifics autofill_specs; 467 sync_pb::EntitySpecifics preference_specs; 468 AddDefaultExtensionValue(BOOKMARKS, &bookmark_specs); 469 AddDefaultExtensionValue(PREFERENCES, &preference_specs); 470 AddDefaultExtensionValue(AUTOFILL, &autofill_specs); 471 dir_->set_initial_sync_ended_for_type(BOOKMARKS, true); 472 dir_->set_last_download_timestamp(BOOKMARKS, 1); 473 dir_->set_initial_sync_ended_for_type(PREFERENCES, true); 474 dir_->set_last_download_timestamp(PREFERENCES, 1); 475 dir_->set_initial_sync_ended_for_type(AUTOFILL, true); 476 dir_->set_last_download_timestamp(AUTOFILL, 1); 477 478 479 std::set<ModelType> types_to_purge; 480 types_to_purge.insert(PREFERENCES); 481 types_to_purge.insert(AUTOFILL); 482 483 TestIdFactory id_factory; 484 // Create some items for each type. 485 { 486 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 487 MutableEntry item1(&trans, CREATE, trans.root_id(), "Item"); 488 ASSERT_TRUE(item1.good()); 489 item1.Put(SPECIFICS, bookmark_specs); 490 item1.Put(SERVER_SPECIFICS, bookmark_specs); 491 item1.Put(IS_UNSYNCED, true); 492 493 MutableEntry item2(&trans, CREATE_NEW_UPDATE_ITEM, 494 id_factory.NewServerId()); 495 ASSERT_TRUE(item2.good()); 496 item2.Put(SERVER_SPECIFICS, bookmark_specs); 497 item2.Put(IS_UNAPPLIED_UPDATE, true); 498 499 MutableEntry item3(&trans, CREATE, trans.root_id(), "Item"); 500 ASSERT_TRUE(item3.good()); 501 item3.Put(SPECIFICS, preference_specs); 502 item3.Put(SERVER_SPECIFICS, preference_specs); 503 item3.Put(IS_UNSYNCED, true); 504 505 MutableEntry item4(&trans, CREATE_NEW_UPDATE_ITEM, 506 id_factory.NewServerId()); 507 ASSERT_TRUE(item4.good()); 508 item4.Put(SERVER_SPECIFICS, preference_specs); 509 item4.Put(IS_UNAPPLIED_UPDATE, true); 510 511 MutableEntry item5(&trans, CREATE, trans.root_id(), "Item"); 512 ASSERT_TRUE(item5.good()); 513 item5.Put(SPECIFICS, autofill_specs); 514 item5.Put(SERVER_SPECIFICS, autofill_specs); 515 item5.Put(IS_UNSYNCED, true); 516 517 MutableEntry item6(&trans, CREATE_NEW_UPDATE_ITEM, 518 id_factory.NewServerId()); 519 ASSERT_TRUE(item6.good()); 520 item6.Put(SERVER_SPECIFICS, autofill_specs); 521 item6.Put(IS_UNAPPLIED_UPDATE, true); 522 } 523 524 dir_->SaveChanges(); 525 { 526 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 527 MetahandleSet all_set; 528 dir_->GetAllMetaHandles(&trans, &all_set); 529 ASSERT_EQ(7U, all_set.size()); 530 } 531 532 dir_->PurgeEntriesWithTypeIn(types_to_purge); 533 534 // We first query the in-memory data, and then reload the directory (without 535 // saving) to verify that disk does not still have the data. 536 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, true); 537 SaveAndReloadDir(); 538 CheckPurgeEntriesWithTypeInSucceeded(types_to_purge, false); 539} 540 541TEST_F(SyncableDirectoryTest, TakeSnapshotGetsOnlyDirtyHandlesTest) { 542 const int metahandles_to_create = 100; 543 544 // half of 2 * metahandles_to_create 545 const unsigned int number_changed = 100u; 546 std::vector<int64> expected_dirty_metahandles; 547 { 548 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 549 for (int i = 0; i < metahandles_to_create; i++) { 550 MutableEntry e(&trans, CREATE, trans.root_id(), "foo"); 551 expected_dirty_metahandles.push_back(e.Get(META_HANDLE)); 552 e.Put(IS_UNSYNCED, true); 553 } 554 } 555 dir_->SaveChanges(); 556 // Put a new value with existing transactions as well as adding new ones. 557 { 558 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 559 std::vector<int64> new_dirty_metahandles; 560 for (std::vector<int64>::const_iterator i = 561 expected_dirty_metahandles.begin(); 562 i != expected_dirty_metahandles.end(); ++i) { 563 // Change existing entries to directories to dirty them. 564 MutableEntry e1(&trans, GET_BY_HANDLE, *i); 565 ASSERT_TRUE(e1.good()); 566 e1.Put(IS_DIR, true); 567 e1.Put(IS_UNSYNCED, true); 568 // Add new entries 569 MutableEntry e2(&trans, CREATE, trans.root_id(), "bar"); 570 e2.Put(IS_UNSYNCED, true); 571 new_dirty_metahandles.push_back(e2.Get(META_HANDLE)); 572 } 573 expected_dirty_metahandles.insert(expected_dirty_metahandles.end(), 574 new_dirty_metahandles.begin(), new_dirty_metahandles.end()); 575 } 576 dir_->SaveChanges(); 577 // Don't make any changes whatsoever and ensure nothing comes back. 578 { 579 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 580 for (std::vector<int64>::const_iterator i = 581 expected_dirty_metahandles.begin(); 582 i != expected_dirty_metahandles.end(); ++i) { 583 MutableEntry e(&trans, GET_BY_HANDLE, *i); 584 ASSERT_TRUE(e.good()); 585 // We aren't doing anything to dirty these entries. 586 } 587 } 588 // Fake SaveChanges() and make sure we got what we expected. 589 { 590 Directory::SaveChangesSnapshot snapshot; 591 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 592 dir_->TakeSnapshotForSaveChanges(&snapshot); 593 // Make sure there are no dirty_metahandles. 594 EXPECT_EQ(0u, snapshot.dirty_metas.size()); 595 dir_->VacuumAfterSaveChanges(snapshot); 596 } 597 { 598 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 599 bool should_change = false; 600 for (std::vector<int64>::const_iterator i = 601 expected_dirty_metahandles.begin(); 602 i != expected_dirty_metahandles.end(); ++i) { 603 // Maybe change entries by flipping IS_DIR. 604 MutableEntry e(&trans, GET_BY_HANDLE, *i); 605 ASSERT_TRUE(e.good()); 606 should_change = !should_change; 607 if (should_change) { 608 bool not_dir = !e.Get(IS_DIR); 609 e.Put(IS_DIR, not_dir); 610 e.Put(IS_UNSYNCED, true); 611 } 612 } 613 } 614 // Fake SaveChanges() and make sure we got what we expected. 615 { 616 Directory::SaveChangesSnapshot snapshot; 617 base::AutoLock scoped_lock(dir_->kernel_->save_changes_mutex); 618 dir_->TakeSnapshotForSaveChanges(&snapshot); 619 // Make sure there's an entry for each changed metahandle. Make sure all 620 // entries are marked dirty. 621 EXPECT_EQ(number_changed, snapshot.dirty_metas.size()); 622 for (OriginalEntries::const_iterator i = snapshot.dirty_metas.begin(); 623 i != snapshot.dirty_metas.end(); ++i) { 624 EXPECT_TRUE(i->is_dirty()); 625 } 626 dir_->VacuumAfterSaveChanges(snapshot); 627 } 628} 629 630const FilePath::CharType SyncableDirectoryTest::kFilePath[] = 631 FILE_PATH_LITERAL("Test.sqlite3"); 632const char SyncableDirectoryTest::kName[] = "Foo"; 633const Id SyncableDirectoryTest::kId(TestIdFactory::FromNumber(-99)); 634 635namespace { 636TEST_F(SyncableDirectoryTest, TestBasicLookupNonExistantID) { 637 ReadTransaction rtrans(dir_.get(), __FILE__, __LINE__); 638 Entry e(&rtrans, GET_BY_ID, kId); 639 ASSERT_FALSE(e.good()); 640} 641 642TEST_F(SyncableDirectoryTest, TestBasicLookupValidID) { 643 CreateEntry("rtc"); 644 ReadTransaction rtrans(dir_.get(), __FILE__, __LINE__); 645 Entry e(&rtrans, GET_BY_ID, kId); 646 ASSERT_TRUE(e.good()); 647} 648 649TEST_F(SyncableDirectoryTest, TestDelete) { 650 std::string name = "peanut butter jelly time"; 651 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 652 MutableEntry e1(&trans, CREATE, trans.root_id(), name); 653 ASSERT_TRUE(e1.good()); 654 ASSERT_TRUE(e1.Put(IS_DEL, true)); 655 MutableEntry e2(&trans, CREATE, trans.root_id(), name); 656 ASSERT_TRUE(e2.good()); 657 ASSERT_TRUE(e2.Put(IS_DEL, true)); 658 MutableEntry e3(&trans, CREATE, trans.root_id(), name); 659 ASSERT_TRUE(e3.good()); 660 ASSERT_TRUE(e3.Put(IS_DEL, true)); 661 662 ASSERT_TRUE(e1.Put(IS_DEL, false)); 663 ASSERT_TRUE(e2.Put(IS_DEL, false)); 664 ASSERT_TRUE(e3.Put(IS_DEL, false)); 665 666 ASSERT_TRUE(e1.Put(IS_DEL, true)); 667 ASSERT_TRUE(e2.Put(IS_DEL, true)); 668 ASSERT_TRUE(e3.Put(IS_DEL, true)); 669} 670 671TEST_F(SyncableDirectoryTest, TestGetUnsynced) { 672 Directory::UnsyncedMetaHandles handles; 673 int64 handle1, handle2; 674 { 675 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 676 677 dir_->GetUnsyncedMetaHandles(&trans, &handles); 678 ASSERT_TRUE(0 == handles.size()); 679 680 MutableEntry e1(&trans, CREATE, trans.root_id(), "abba"); 681 ASSERT_TRUE(e1.good()); 682 handle1 = e1.Get(META_HANDLE); 683 e1.Put(BASE_VERSION, 1); 684 e1.Put(IS_DIR, true); 685 e1.Put(ID, TestIdFactory::FromNumber(101)); 686 687 MutableEntry e2(&trans, CREATE, e1.Get(ID), "bread"); 688 ASSERT_TRUE(e2.good()); 689 handle2 = e2.Get(META_HANDLE); 690 e2.Put(BASE_VERSION, 1); 691 e2.Put(ID, TestIdFactory::FromNumber(102)); 692 } 693 dir_->SaveChanges(); 694 { 695 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 696 697 dir_->GetUnsyncedMetaHandles(&trans, &handles); 698 ASSERT_TRUE(0 == handles.size()); 699 700 MutableEntry e3(&trans, GET_BY_HANDLE, handle1); 701 ASSERT_TRUE(e3.good()); 702 e3.Put(IS_UNSYNCED, true); 703 } 704 dir_->SaveChanges(); 705 { 706 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 707 dir_->GetUnsyncedMetaHandles(&trans, &handles); 708 ASSERT_TRUE(1 == handles.size()); 709 ASSERT_TRUE(handle1 == handles[0]); 710 711 MutableEntry e4(&trans, GET_BY_HANDLE, handle2); 712 ASSERT_TRUE(e4.good()); 713 e4.Put(IS_UNSYNCED, true); 714 } 715 dir_->SaveChanges(); 716 { 717 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 718 dir_->GetUnsyncedMetaHandles(&trans, &handles); 719 ASSERT_TRUE(2 == handles.size()); 720 if (handle1 == handles[0]) { 721 ASSERT_TRUE(handle2 == handles[1]); 722 } else { 723 ASSERT_TRUE(handle2 == handles[0]); 724 ASSERT_TRUE(handle1 == handles[1]); 725 } 726 727 MutableEntry e5(&trans, GET_BY_HANDLE, handle1); 728 ASSERT_TRUE(e5.good()); 729 ASSERT_TRUE(e5.Get(IS_UNSYNCED)); 730 ASSERT_TRUE(e5.Put(IS_UNSYNCED, false)); 731 ASSERT_FALSE(e5.Get(IS_UNSYNCED)); 732 } 733 dir_->SaveChanges(); 734 { 735 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 736 dir_->GetUnsyncedMetaHandles(&trans, &handles); 737 ASSERT_TRUE(1 == handles.size()); 738 ASSERT_TRUE(handle2 == handles[0]); 739 } 740} 741 742TEST_F(SyncableDirectoryTest, TestGetUnappliedUpdates) { 743 Directory::UnappliedUpdateMetaHandles handles; 744 int64 handle1, handle2; 745 { 746 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 747 748 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles); 749 ASSERT_TRUE(0 == handles.size()); 750 751 MutableEntry e1(&trans, CREATE, trans.root_id(), "abba"); 752 ASSERT_TRUE(e1.good()); 753 handle1 = e1.Get(META_HANDLE); 754 e1.Put(IS_UNAPPLIED_UPDATE, false); 755 e1.Put(BASE_VERSION, 1); 756 e1.Put(ID, TestIdFactory::FromNumber(101)); 757 e1.Put(IS_DIR, true); 758 759 MutableEntry e2(&trans, CREATE, e1.Get(ID), "bread"); 760 ASSERT_TRUE(e2.good()); 761 handle2 = e2.Get(META_HANDLE); 762 e2.Put(IS_UNAPPLIED_UPDATE, false); 763 e2.Put(BASE_VERSION, 1); 764 e2.Put(ID, TestIdFactory::FromNumber(102)); 765 } 766 dir_->SaveChanges(); 767 { 768 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 769 770 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles); 771 ASSERT_TRUE(0 == handles.size()); 772 773 MutableEntry e3(&trans, GET_BY_HANDLE, handle1); 774 ASSERT_TRUE(e3.good()); 775 e3.Put(IS_UNAPPLIED_UPDATE, true); 776 } 777 dir_->SaveChanges(); 778 { 779 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 780 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles); 781 ASSERT_TRUE(1 == handles.size()); 782 ASSERT_TRUE(handle1 == handles[0]); 783 784 MutableEntry e4(&trans, GET_BY_HANDLE, handle2); 785 ASSERT_TRUE(e4.good()); 786 e4.Put(IS_UNAPPLIED_UPDATE, true); 787 } 788 dir_->SaveChanges(); 789 { 790 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 791 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles); 792 ASSERT_TRUE(2 == handles.size()); 793 if (handle1 == handles[0]) { 794 ASSERT_TRUE(handle2 == handles[1]); 795 } else { 796 ASSERT_TRUE(handle2 == handles[0]); 797 ASSERT_TRUE(handle1 == handles[1]); 798 } 799 800 MutableEntry e5(&trans, GET_BY_HANDLE, handle1); 801 ASSERT_TRUE(e5.good()); 802 e5.Put(IS_UNAPPLIED_UPDATE, false); 803 } 804 dir_->SaveChanges(); 805 { 806 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 807 dir_->GetUnappliedUpdateMetaHandles(&trans, &handles); 808 ASSERT_TRUE(1 == handles.size()); 809 ASSERT_TRUE(handle2 == handles[0]); 810 } 811} 812 813 814TEST_F(SyncableDirectoryTest, DeleteBug_531383) { 815 // Try to evoke a check failure... 816 TestIdFactory id_factory; 817 int64 grandchild_handle, twin_handle; 818 { 819 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__); 820 MutableEntry parent(&wtrans, CREATE, id_factory.root(), "Bob"); 821 ASSERT_TRUE(parent.good()); 822 parent.Put(IS_DIR, true); 823 parent.Put(ID, id_factory.NewServerId()); 824 parent.Put(BASE_VERSION, 1); 825 MutableEntry child(&wtrans, CREATE, parent.Get(ID), "Bob"); 826 ASSERT_TRUE(child.good()); 827 child.Put(IS_DIR, true); 828 child.Put(ID, id_factory.NewServerId()); 829 child.Put(BASE_VERSION, 1); 830 MutableEntry grandchild(&wtrans, CREATE, child.Get(ID), "Bob"); 831 ASSERT_TRUE(grandchild.good()); 832 grandchild.Put(ID, id_factory.NewServerId()); 833 grandchild.Put(BASE_VERSION, 1); 834 ASSERT_TRUE(grandchild.Put(IS_DEL, true)); 835 MutableEntry twin(&wtrans, CREATE, child.Get(ID), "Bob"); 836 ASSERT_TRUE(twin.good()); 837 ASSERT_TRUE(twin.Put(IS_DEL, true)); 838 ASSERT_TRUE(grandchild.Put(IS_DEL, false)); 839 840 grandchild_handle = grandchild.Get(META_HANDLE); 841 twin_handle = twin.Get(META_HANDLE); 842 } 843 dir_->SaveChanges(); 844 { 845 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__); 846 MutableEntry grandchild(&wtrans, GET_BY_HANDLE, grandchild_handle); 847 grandchild.Put(IS_DEL, true); // Used to CHECK fail here. 848 } 849} 850 851static inline bool IsLegalNewParent(const Entry& a, const Entry& b) { 852 return IsLegalNewParent(a.trans(), a.Get(ID), b.Get(ID)); 853} 854 855TEST_F(SyncableDirectoryTest, TestIsLegalNewParent) { 856 TestIdFactory id_factory; 857 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__); 858 Entry root(&wtrans, GET_BY_ID, id_factory.root()); 859 ASSERT_TRUE(root.good()); 860 MutableEntry parent(&wtrans, CREATE, root.Get(ID), "Bob"); 861 ASSERT_TRUE(parent.good()); 862 parent.Put(IS_DIR, true); 863 parent.Put(ID, id_factory.NewServerId()); 864 parent.Put(BASE_VERSION, 1); 865 MutableEntry child(&wtrans, CREATE, parent.Get(ID), "Bob"); 866 ASSERT_TRUE(child.good()); 867 child.Put(IS_DIR, true); 868 child.Put(ID, id_factory.NewServerId()); 869 child.Put(BASE_VERSION, 1); 870 MutableEntry grandchild(&wtrans, CREATE, child.Get(ID), "Bob"); 871 ASSERT_TRUE(grandchild.good()); 872 grandchild.Put(ID, id_factory.NewServerId()); 873 grandchild.Put(BASE_VERSION, 1); 874 875 MutableEntry parent2(&wtrans, CREATE, root.Get(ID), "Pete"); 876 ASSERT_TRUE(parent2.good()); 877 parent2.Put(IS_DIR, true); 878 parent2.Put(ID, id_factory.NewServerId()); 879 parent2.Put(BASE_VERSION, 1); 880 MutableEntry child2(&wtrans, CREATE, parent2.Get(ID), "Pete"); 881 ASSERT_TRUE(child2.good()); 882 child2.Put(IS_DIR, true); 883 child2.Put(ID, id_factory.NewServerId()); 884 child2.Put(BASE_VERSION, 1); 885 MutableEntry grandchild2(&wtrans, CREATE, child2.Get(ID), "Pete"); 886 ASSERT_TRUE(grandchild2.good()); 887 grandchild2.Put(ID, id_factory.NewServerId()); 888 grandchild2.Put(BASE_VERSION, 1); 889 // resulting tree 890 // root 891 // / | 892 // parent parent2 893 // | | 894 // child child2 895 // | | 896 // grandchild grandchild2 897 ASSERT_TRUE(IsLegalNewParent(child, root)); 898 ASSERT_TRUE(IsLegalNewParent(child, parent)); 899 ASSERT_FALSE(IsLegalNewParent(child, child)); 900 ASSERT_FALSE(IsLegalNewParent(child, grandchild)); 901 ASSERT_TRUE(IsLegalNewParent(child, parent2)); 902 ASSERT_TRUE(IsLegalNewParent(child, grandchild2)); 903 ASSERT_FALSE(IsLegalNewParent(parent, grandchild)); 904 ASSERT_FALSE(IsLegalNewParent(root, grandchild)); 905 ASSERT_FALSE(IsLegalNewParent(parent, grandchild)); 906} 907 908TEST_F(SyncableDirectoryTest, TestEntryIsInFolder) { 909 // Create a subdir and an entry. 910 int64 entry_handle; 911 syncable::Id folder_id; 912 syncable::Id entry_id; 913 std::string entry_name = "entry"; 914 915 { 916 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 917 MutableEntry folder(&trans, CREATE, trans.root_id(), "folder"); 918 ASSERT_TRUE(folder.good()); 919 EXPECT_TRUE(folder.Put(IS_DIR, true)); 920 EXPECT_TRUE(folder.Put(IS_UNSYNCED, true)); 921 folder_id = folder.Get(ID); 922 923 MutableEntry entry(&trans, CREATE, folder.Get(ID), entry_name); 924 ASSERT_TRUE(entry.good()); 925 entry_handle = entry.Get(META_HANDLE); 926 entry.Put(IS_UNSYNCED, true); 927 entry_id = entry.Get(ID); 928 } 929 930 // Make sure we can find the entry in the folder. 931 { 932 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 933 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), entry_name)); 934 EXPECT_EQ(1, CountEntriesWithName(&trans, folder_id, entry_name)); 935 936 Entry entry(&trans, GET_BY_ID, entry_id); 937 ASSERT_TRUE(entry.good()); 938 EXPECT_EQ(entry_handle, entry.Get(META_HANDLE)); 939 EXPECT_TRUE(entry.Get(NON_UNIQUE_NAME) == entry_name); 940 EXPECT_TRUE(entry.Get(PARENT_ID) == folder_id); 941 } 942} 943 944TEST_F(SyncableDirectoryTest, TestParentIdIndexUpdate) { 945 std::string child_name = "child"; 946 947 WriteTransaction wt(dir_.get(), UNITTEST, __FILE__, __LINE__); 948 MutableEntry parent_folder(&wt, CREATE, wt.root_id(), "folder1"); 949 parent_folder.Put(IS_UNSYNCED, true); 950 EXPECT_TRUE(parent_folder.Put(IS_DIR, true)); 951 952 MutableEntry parent_folder2(&wt, CREATE, wt.root_id(), "folder2"); 953 parent_folder2.Put(IS_UNSYNCED, true); 954 EXPECT_TRUE(parent_folder2.Put(IS_DIR, true)); 955 956 MutableEntry child(&wt, CREATE, parent_folder.Get(ID), child_name); 957 EXPECT_TRUE(child.Put(IS_DIR, true)); 958 child.Put(IS_UNSYNCED, true); 959 960 ASSERT_TRUE(child.good()); 961 962 EXPECT_EQ(0, CountEntriesWithName(&wt, wt.root_id(), child_name)); 963 EXPECT_EQ(parent_folder.Get(ID), child.Get(PARENT_ID)); 964 EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder.Get(ID), child_name)); 965 EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder2.Get(ID), child_name)); 966 child.Put(PARENT_ID, parent_folder2.Get(ID)); 967 EXPECT_EQ(parent_folder2.Get(ID), child.Get(PARENT_ID)); 968 EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder.Get(ID), child_name)); 969 EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder2.Get(ID), child_name)); 970} 971 972TEST_F(SyncableDirectoryTest, TestNoReindexDeletedItems) { 973 std::string folder_name = "folder"; 974 std::string new_name = "new_name"; 975 976 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 977 MutableEntry folder(&trans, CREATE, trans.root_id(), folder_name); 978 ASSERT_TRUE(folder.good()); 979 ASSERT_TRUE(folder.Put(IS_DIR, true)); 980 ASSERT_TRUE(folder.Put(IS_DEL, true)); 981 982 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name)); 983 984 MutableEntry deleted(&trans, GET_BY_ID, folder.Get(ID)); 985 ASSERT_TRUE(deleted.good()); 986 ASSERT_TRUE(deleted.Put(PARENT_ID, trans.root_id())); 987 ASSERT_TRUE(deleted.Put(NON_UNIQUE_NAME, new_name)); 988 989 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name)); 990 EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), new_name)); 991} 992 993TEST_F(SyncableDirectoryTest, TestCaseChangeRename) { 994 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 995 MutableEntry folder(&trans, CREATE, trans.root_id(), "CaseChange"); 996 ASSERT_TRUE(folder.good()); 997 EXPECT_TRUE(folder.Put(PARENT_ID, trans.root_id())); 998 EXPECT_TRUE(folder.Put(NON_UNIQUE_NAME, "CASECHANGE")); 999 EXPECT_TRUE(folder.Put(IS_DEL, true)); 1000} 1001 1002TEST_F(SyncableDirectoryTest, TestShareInfo) { 1003 dir_->set_last_download_timestamp(AUTOFILL, 100); 1004 dir_->set_last_download_timestamp(BOOKMARKS, 1000); 1005 dir_->set_initial_sync_ended_for_type(AUTOFILL, true); 1006 dir_->set_store_birthday("Jan 31st"); 1007 dir_->SetNotificationState("notification_state"); 1008 { 1009 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1010 EXPECT_EQ(100, dir_->last_download_timestamp(AUTOFILL)); 1011 EXPECT_EQ(1000, dir_->last_download_timestamp(BOOKMARKS)); 1012 EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL)); 1013 EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS)); 1014 EXPECT_EQ("Jan 31st", dir_->store_birthday()); 1015 EXPECT_EQ("notification_state", dir_->GetAndClearNotificationState()); 1016 EXPECT_EQ("", dir_->GetAndClearNotificationState()); 1017 } 1018 dir_->set_last_download_timestamp(AUTOFILL, 200); 1019 dir_->set_store_birthday("April 10th"); 1020 dir_->SetNotificationState("notification_state2"); 1021 dir_->SaveChanges(); 1022 { 1023 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1024 EXPECT_EQ(200, dir_->last_download_timestamp(AUTOFILL)); 1025 EXPECT_EQ(1000, dir_->last_download_timestamp(BOOKMARKS)); 1026 EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL)); 1027 EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS)); 1028 EXPECT_EQ("April 10th", dir_->store_birthday()); 1029 EXPECT_EQ("notification_state2", dir_->GetAndClearNotificationState()); 1030 EXPECT_EQ("", dir_->GetAndClearNotificationState()); 1031 } 1032 dir_->SetNotificationState("notification_state2"); 1033 // Restore the directory from disk. Make sure that nothing's changed. 1034 SaveAndReloadDir(); 1035 { 1036 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1037 EXPECT_EQ(200, dir_->last_download_timestamp(AUTOFILL)); 1038 EXPECT_EQ(1000, dir_->last_download_timestamp(BOOKMARKS)); 1039 EXPECT_TRUE(dir_->initial_sync_ended_for_type(AUTOFILL)); 1040 EXPECT_FALSE(dir_->initial_sync_ended_for_type(BOOKMARKS)); 1041 EXPECT_EQ("April 10th", dir_->store_birthday()); 1042 EXPECT_EQ("notification_state2", dir_->GetAndClearNotificationState()); 1043 EXPECT_EQ("", dir_->GetAndClearNotificationState()); 1044 } 1045} 1046 1047TEST_F(SyncableDirectoryTest, TestSimpleFieldsPreservedDuringSaveChanges) { 1048 Id update_id = TestIdFactory::FromNumber(1); 1049 Id create_id; 1050 EntryKernel create_pre_save, update_pre_save; 1051 EntryKernel create_post_save, update_post_save; 1052 std::string create_name = "Create"; 1053 1054 { 1055 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1056 MutableEntry create(&trans, CREATE, trans.root_id(), create_name); 1057 MutableEntry update(&trans, CREATE_NEW_UPDATE_ITEM, update_id); 1058 create.Put(IS_UNSYNCED, true); 1059 update.Put(IS_UNAPPLIED_UPDATE, true); 1060 sync_pb::EntitySpecifics specifics; 1061 specifics.MutableExtension(sync_pb::bookmark)->set_favicon("PNG"); 1062 specifics.MutableExtension(sync_pb::bookmark)->set_url("http://nowhere"); 1063 create.Put(SPECIFICS, specifics); 1064 create_pre_save = create.GetKernelCopy(); 1065 update_pre_save = update.GetKernelCopy(); 1066 create_id = create.Get(ID); 1067 } 1068 1069 dir_->SaveChanges(); 1070 dir_.reset(new Directory()); 1071 ASSERT_TRUE(dir_.get()); 1072 ASSERT_TRUE(OPENED == dir_->Open(file_path_, kName)); 1073 ASSERT_TRUE(dir_->good()); 1074 1075 { 1076 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1077 Entry create(&trans, GET_BY_ID, create_id); 1078 EXPECT_EQ(1, CountEntriesWithName(&trans, trans.root_id(), create_name)); 1079 Entry update(&trans, GET_BY_ID, update_id); 1080 create_post_save = create.GetKernelCopy(); 1081 update_post_save = update.GetKernelCopy(); 1082 } 1083 int i = BEGIN_FIELDS; 1084 for ( ; i < INT64_FIELDS_END ; ++i) { 1085 EXPECT_EQ(create_pre_save.ref((Int64Field)i), 1086 create_post_save.ref((Int64Field)i)) 1087 << "int64 field #" << i << " changed during save/load"; 1088 EXPECT_EQ(update_pre_save.ref((Int64Field)i), 1089 update_post_save.ref((Int64Field)i)) 1090 << "int64 field #" << i << " changed during save/load"; 1091 } 1092 for ( ; i < ID_FIELDS_END ; ++i) { 1093 EXPECT_EQ(create_pre_save.ref((IdField)i), 1094 create_post_save.ref((IdField)i)) 1095 << "id field #" << i << " changed during save/load"; 1096 EXPECT_EQ(update_pre_save.ref((IdField)i), 1097 update_pre_save.ref((IdField)i)) 1098 << "id field #" << i << " changed during save/load"; 1099 } 1100 for ( ; i < BIT_FIELDS_END ; ++i) { 1101 EXPECT_EQ(create_pre_save.ref((BitField)i), 1102 create_post_save.ref((BitField)i)) 1103 << "Bit field #" << i << " changed during save/load"; 1104 EXPECT_EQ(update_pre_save.ref((BitField)i), 1105 update_post_save.ref((BitField)i)) 1106 << "Bit field #" << i << " changed during save/load"; 1107 } 1108 for ( ; i < STRING_FIELDS_END ; ++i) { 1109 EXPECT_EQ(create_pre_save.ref((StringField)i), 1110 create_post_save.ref((StringField)i)) 1111 << "String field #" << i << " changed during save/load"; 1112 EXPECT_EQ(update_pre_save.ref((StringField)i), 1113 update_post_save.ref((StringField)i)) 1114 << "String field #" << i << " changed during save/load"; 1115 } 1116 for ( ; i < PROTO_FIELDS_END; ++i) { 1117 EXPECT_EQ(create_pre_save.ref((ProtoField)i).SerializeAsString(), 1118 create_post_save.ref((ProtoField)i).SerializeAsString()) 1119 << "Blob field #" << i << " changed during save/load"; 1120 EXPECT_EQ(update_pre_save.ref((ProtoField)i).SerializeAsString(), 1121 update_post_save.ref((ProtoField)i).SerializeAsString()) 1122 << "Blob field #" << i << " changed during save/load"; 1123 } 1124} 1125 1126TEST_F(SyncableDirectoryTest, TestSaveChangesFailure) { 1127 int64 handle1 = 0; 1128 // Set up an item using a regular, saveable directory. 1129 { 1130 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1131 1132 MutableEntry e1(&trans, CREATE, trans.root_id(), "aguilera"); 1133 ASSERT_TRUE(e1.good()); 1134 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1135 handle1 = e1.Get(META_HANDLE); 1136 e1.Put(BASE_VERSION, 1); 1137 e1.Put(IS_DIR, true); 1138 e1.Put(ID, TestIdFactory::FromNumber(101)); 1139 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1140 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1141 } 1142 ASSERT_TRUE(dir_->SaveChanges()); 1143 1144 // Make sure the item is no longer dirty after saving, 1145 // and make a modification. 1146 { 1147 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1148 1149 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1); 1150 ASSERT_TRUE(aguilera.good()); 1151 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 1152 EXPECT_EQ(aguilera.Get(NON_UNIQUE_NAME), "aguilera"); 1153 aguilera.Put(NON_UNIQUE_NAME, "overwritten"); 1154 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty()); 1155 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1156 } 1157 ASSERT_TRUE(dir_->SaveChanges()); 1158 1159 // Now do some operations using a directory for which SaveChanges will 1160 // always fail. 1161 dir_.reset(new TestUnsaveableDirectory()); 1162 ASSERT_TRUE(dir_.get()); 1163 ASSERT_TRUE(OPENED == dir_->Open(FilePath(kFilePath), kName)); 1164 ASSERT_TRUE(dir_->good()); 1165 int64 handle2 = 0; 1166 { 1167 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1168 1169 MutableEntry aguilera(&trans, GET_BY_HANDLE, handle1); 1170 ASSERT_TRUE(aguilera.good()); 1171 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 1172 EXPECT_EQ(aguilera.Get(NON_UNIQUE_NAME), "overwritten"); 1173 EXPECT_FALSE(aguilera.GetKernelCopy().is_dirty()); 1174 EXPECT_FALSE(IsInDirtyMetahandles(handle1)); 1175 aguilera.Put(NON_UNIQUE_NAME, "christina"); 1176 EXPECT_TRUE(aguilera.GetKernelCopy().is_dirty()); 1177 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1178 1179 // New item. 1180 MutableEntry kids_on_block(&trans, CREATE, trans.root_id(), "kids"); 1181 ASSERT_TRUE(kids_on_block.good()); 1182 handle2 = kids_on_block.Get(META_HANDLE); 1183 kids_on_block.Put(BASE_VERSION, 1); 1184 kids_on_block.Put(IS_DIR, true); 1185 kids_on_block.Put(ID, TestIdFactory::FromNumber(102)); 1186 EXPECT_TRUE(kids_on_block.GetKernelCopy().is_dirty()); 1187 EXPECT_TRUE(IsInDirtyMetahandles(handle2)); 1188 } 1189 1190 // We are using an unsaveable directory, so this can't succeed. However, 1191 // the HandleSaveChangesFailure code path should have been triggered. 1192 ASSERT_FALSE(dir_->SaveChanges()); 1193 1194 // Make sure things were rolled back and the world is as it was before call. 1195 { 1196 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1197 Entry e1(&trans, GET_BY_HANDLE, handle1); 1198 ASSERT_TRUE(e1.good()); 1199 EntryKernel aguilera = e1.GetKernelCopy(); 1200 Entry kids(&trans, GET_BY_HANDLE, handle2); 1201 ASSERT_TRUE(kids.good()); 1202 EXPECT_TRUE(kids.GetKernelCopy().is_dirty()); 1203 EXPECT_TRUE(IsInDirtyMetahandles(handle2)); 1204 EXPECT_TRUE(aguilera.is_dirty()); 1205 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1206 } 1207} 1208 1209TEST_F(SyncableDirectoryTest, TestSaveChangesFailureWithPurge) { 1210 int64 handle1 = 0; 1211 // Set up an item using a regular, saveable directory. 1212 { 1213 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1214 1215 MutableEntry e1(&trans, CREATE, trans.root_id(), "aguilera"); 1216 ASSERT_TRUE(e1.good()); 1217 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1218 handle1 = e1.Get(META_HANDLE); 1219 e1.Put(BASE_VERSION, 1); 1220 e1.Put(IS_DIR, true); 1221 e1.Put(ID, TestIdFactory::FromNumber(101)); 1222 sync_pb::EntitySpecifics bookmark_specs; 1223 AddDefaultExtensionValue(BOOKMARKS, &bookmark_specs); 1224 e1.Put(SPECIFICS, bookmark_specs); 1225 e1.Put(SERVER_SPECIFICS, bookmark_specs); 1226 e1.Put(ID, TestIdFactory::FromNumber(101)); 1227 EXPECT_TRUE(e1.GetKernelCopy().is_dirty()); 1228 EXPECT_TRUE(IsInDirtyMetahandles(handle1)); 1229 } 1230 ASSERT_TRUE(dir_->SaveChanges()); 1231 1232 // Now do some operations using a directory for which SaveChanges will 1233 // always fail. 1234 dir_.reset(new TestUnsaveableDirectory()); 1235 ASSERT_TRUE(dir_.get()); 1236 ASSERT_TRUE(OPENED == dir_->Open(FilePath(kFilePath), kName)); 1237 ASSERT_TRUE(dir_->good()); 1238 1239 ModelTypeSet set; 1240 set.insert(BOOKMARKS); 1241 dir_->PurgeEntriesWithTypeIn(set); 1242 EXPECT_TRUE(IsInMetahandlesToPurge(handle1)); 1243 ASSERT_FALSE(dir_->SaveChanges()); 1244 EXPECT_TRUE(IsInMetahandlesToPurge(handle1)); 1245} 1246 1247// Create items of each model type, and check that GetModelType and 1248// GetServerModelType return the right value. 1249TEST_F(SyncableDirectoryTest, GetModelType) { 1250 TestIdFactory id_factory; 1251 for (int i = 0; i < MODEL_TYPE_COUNT; ++i) { 1252 ModelType datatype = ModelTypeFromInt(i); 1253 SCOPED_TRACE(testing::Message("Testing model type ") << datatype); 1254 switch (datatype) { 1255 case UNSPECIFIED: 1256 case TOP_LEVEL_FOLDER: 1257 continue; // Datatype isn't a function of Specifics. 1258 default: 1259 break; 1260 } 1261 sync_pb::EntitySpecifics specifics; 1262 AddDefaultExtensionValue(datatype, &specifics); 1263 1264 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1265 1266 MutableEntry folder(&trans, CREATE, trans.root_id(), "Folder"); 1267 ASSERT_TRUE(folder.good()); 1268 folder.Put(ID, id_factory.NewServerId()); 1269 folder.Put(SPECIFICS, specifics); 1270 folder.Put(BASE_VERSION, 1); 1271 folder.Put(IS_DIR, true); 1272 folder.Put(IS_DEL, false); 1273 ASSERT_EQ(datatype, folder.GetModelType()); 1274 1275 MutableEntry item(&trans, CREATE, trans.root_id(), "Item"); 1276 ASSERT_TRUE(item.good()); 1277 item.Put(ID, id_factory.NewServerId()); 1278 item.Put(SPECIFICS, specifics); 1279 item.Put(BASE_VERSION, 1); 1280 item.Put(IS_DIR, false); 1281 item.Put(IS_DEL, false); 1282 ASSERT_EQ(datatype, item.GetModelType()); 1283 1284 // It's critical that deletion records retain their datatype, so that 1285 // they can be dispatched to the appropriate change processor. 1286 MutableEntry deleted_item(&trans, CREATE, trans.root_id(), "Deleted Item"); 1287 ASSERT_TRUE(item.good()); 1288 deleted_item.Put(ID, id_factory.NewServerId()); 1289 deleted_item.Put(SPECIFICS, specifics); 1290 deleted_item.Put(BASE_VERSION, 1); 1291 deleted_item.Put(IS_DIR, false); 1292 deleted_item.Put(IS_DEL, true); 1293 ASSERT_EQ(datatype, deleted_item.GetModelType()); 1294 1295 MutableEntry server_folder(&trans, CREATE_NEW_UPDATE_ITEM, 1296 id_factory.NewServerId()); 1297 ASSERT_TRUE(server_folder.good()); 1298 server_folder.Put(SERVER_SPECIFICS, specifics); 1299 server_folder.Put(BASE_VERSION, 1); 1300 server_folder.Put(SERVER_IS_DIR, true); 1301 server_folder.Put(SERVER_IS_DEL, false); 1302 ASSERT_EQ(datatype, server_folder.GetServerModelType()); 1303 1304 MutableEntry server_item(&trans, CREATE_NEW_UPDATE_ITEM, 1305 id_factory.NewServerId()); 1306 ASSERT_TRUE(server_item.good()); 1307 server_item.Put(SERVER_SPECIFICS, specifics); 1308 server_item.Put(BASE_VERSION, 1); 1309 server_item.Put(SERVER_IS_DIR, false); 1310 server_item.Put(SERVER_IS_DEL, false); 1311 ASSERT_EQ(datatype, server_item.GetServerModelType()); 1312 1313 browser_sync::SyncEntity folder_entity; 1314 folder_entity.set_id(id_factory.NewServerId()); 1315 folder_entity.set_deleted(false); 1316 folder_entity.set_folder(true); 1317 folder_entity.mutable_specifics()->CopyFrom(specifics); 1318 ASSERT_EQ(datatype, folder_entity.GetModelType()); 1319 1320 browser_sync::SyncEntity item_entity; 1321 item_entity.set_id(id_factory.NewServerId()); 1322 item_entity.set_deleted(false); 1323 item_entity.set_folder(false); 1324 item_entity.mutable_specifics()->CopyFrom(specifics); 1325 ASSERT_EQ(datatype, item_entity.GetModelType()); 1326 } 1327} 1328 1329} // namespace 1330 1331void SyncableDirectoryTest::ValidateEntry(BaseTransaction* trans, 1332 int64 id, 1333 bool check_name, 1334 const std::string& name, 1335 int64 base_version, 1336 int64 server_version, 1337 bool is_del) { 1338 Entry e(trans, GET_BY_ID, TestIdFactory::FromNumber(id)); 1339 ASSERT_TRUE(e.good()); 1340 if (check_name) 1341 ASSERT_TRUE(name == e.Get(NON_UNIQUE_NAME)); 1342 ASSERT_TRUE(base_version == e.Get(BASE_VERSION)); 1343 ASSERT_TRUE(server_version == e.Get(SERVER_VERSION)); 1344 ASSERT_TRUE(is_del == e.Get(IS_DEL)); 1345} 1346 1347namespace { 1348 1349TEST(SyncableDirectoryManager, TestFileRelease) { 1350 DirectoryManager dm(FilePath(FILE_PATH_LITERAL("."))); 1351 ASSERT_TRUE(dm.Open("ScopeTest")); 1352 { 1353 ScopedDirLookup(&dm, "ScopeTest"); 1354 } 1355 dm.Close("ScopeTest"); 1356 ASSERT_TRUE(file_util::Delete(dm.GetSyncDataDatabasePath(), true)); 1357} 1358 1359class ThreadOpenTestDelegate : public base::PlatformThread::Delegate { 1360 public: 1361 explicit ThreadOpenTestDelegate(DirectoryManager* dm) 1362 : directory_manager_(dm) {} 1363 DirectoryManager* const directory_manager_; 1364 1365 private: 1366 // PlatformThread::Delegate methods: 1367 virtual void ThreadMain() { 1368 CHECK(directory_manager_->Open("Open")); 1369 } 1370 1371 DISALLOW_COPY_AND_ASSIGN(ThreadOpenTestDelegate); 1372}; 1373 1374TEST(SyncableDirectoryManager, ThreadOpenTest) { 1375 DirectoryManager dm(FilePath(FILE_PATH_LITERAL("."))); 1376 base::PlatformThreadHandle thread_handle; 1377 ThreadOpenTestDelegate test_delegate(&dm); 1378 ASSERT_TRUE(base::PlatformThread::Create(0, &test_delegate, &thread_handle)); 1379 base::PlatformThread::Join(thread_handle); 1380 { 1381 ScopedDirLookup dir(&dm, "Open"); 1382 ASSERT_TRUE(dir.good()); 1383 } 1384 dm.Close("Open"); 1385 ScopedDirLookup dir(&dm, "Open"); 1386 ASSERT_FALSE(dir.good()); 1387} 1388 1389struct Step { 1390 Step() : condvar(&mutex), number(0) {} 1391 1392 base::Lock mutex; 1393 base::ConditionVariable condvar; 1394 int number; 1395 int64 metahandle; 1396}; 1397 1398class ThreadBugDelegate : public base::PlatformThread::Delegate { 1399 public: 1400 // a role is 0 or 1, meaning this thread does the odd or event steps. 1401 ThreadBugDelegate(int role, Step* step, DirectoryManager* dirman) 1402 : role_(role), step_(step), directory_manager_(dirman) {} 1403 1404 protected: 1405 const int role_; 1406 Step* const step_; 1407 DirectoryManager* const directory_manager_; 1408 1409 // PlatformThread::Delegate methods: 1410 virtual void ThreadMain() { 1411 const std::string dirname = "ThreadBug1"; 1412 base::AutoLock scoped_lock(step_->mutex); 1413 1414 while (step_->number < 3) { 1415 while (step_->number % 2 != role_) { 1416 step_->condvar.Wait(); 1417 } 1418 switch (step_->number) { 1419 case 0: 1420 directory_manager_->Open(dirname); 1421 break; 1422 case 1: 1423 { 1424 directory_manager_->Close(dirname); 1425 directory_manager_->Open(dirname); 1426 ScopedDirLookup dir(directory_manager_, dirname); 1427 CHECK(dir.good()); 1428 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); 1429 MutableEntry me(&trans, CREATE, trans.root_id(), "Jeff"); 1430 step_->metahandle = me.Get(META_HANDLE); 1431 me.Put(IS_UNSYNCED, true); 1432 } 1433 break; 1434 case 2: 1435 { 1436 ScopedDirLookup dir(directory_manager_, dirname); 1437 CHECK(dir.good()); 1438 ReadTransaction trans(dir, __FILE__, __LINE__); 1439 Entry e(&trans, GET_BY_HANDLE, step_->metahandle); 1440 CHECK(e.good()); // Failed due to ThreadBug1 1441 } 1442 directory_manager_->Close(dirname); 1443 break; 1444 } 1445 step_->number += 1; 1446 step_->condvar.Signal(); 1447 } 1448 } 1449 1450 DISALLOW_COPY_AND_ASSIGN(ThreadBugDelegate); 1451}; 1452 1453TEST(SyncableDirectoryManager, ThreadBug1) { 1454 Step step; 1455 step.number = 0; 1456 DirectoryManager dirman(FilePath(FILE_PATH_LITERAL("."))); 1457 ThreadBugDelegate thread_delegate_1(0, &step, &dirman); 1458 ThreadBugDelegate thread_delegate_2(1, &step, &dirman); 1459 1460 base::PlatformThreadHandle thread_handle_1; 1461 base::PlatformThreadHandle thread_handle_2; 1462 1463 ASSERT_TRUE( 1464 base::PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1)); 1465 ASSERT_TRUE( 1466 base::PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2)); 1467 1468 base::PlatformThread::Join(thread_handle_1); 1469 base::PlatformThread::Join(thread_handle_2); 1470} 1471 1472 1473// The in-memory information would get out of sync because a 1474// directory would be closed and re-opened, and then an old 1475// Directory::Kernel with stale information would get saved to the db. 1476class DirectoryKernelStalenessBugDelegate : public ThreadBugDelegate { 1477 public: 1478 DirectoryKernelStalenessBugDelegate(int role, Step* step, 1479 DirectoryManager* dirman) 1480 : ThreadBugDelegate(role, step, dirman) {} 1481 1482 virtual void ThreadMain() { 1483 const char test_bytes[] = "test data"; 1484 const std::string dirname = "DirectoryKernelStalenessBug"; 1485 base::AutoLock scoped_lock(step_->mutex); 1486 const Id jeff_id = TestIdFactory::FromNumber(100); 1487 1488 while (step_->number < 4) { 1489 while (step_->number % 2 != role_) { 1490 step_->condvar.Wait(); 1491 } 1492 switch (step_->number) { 1493 case 0: 1494 { 1495 // Clean up remnants of earlier test runs. 1496 file_util::Delete(directory_manager_->GetSyncDataDatabasePath(), 1497 true); 1498 // Test. 1499 directory_manager_->Open(dirname); 1500 ScopedDirLookup dir(directory_manager_, dirname); 1501 CHECK(dir.good()); 1502 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); 1503 MutableEntry me(&trans, CREATE, trans.root_id(), "Jeff"); 1504 me.Put(BASE_VERSION, 1); 1505 me.Put(ID, jeff_id); 1506 PutDataAsBookmarkFavicon(&trans, &me, test_bytes, 1507 sizeof(test_bytes)); 1508 } 1509 { 1510 ScopedDirLookup dir(directory_manager_, dirname); 1511 CHECK(dir.good()); 1512 dir->SaveChanges(); 1513 } 1514 directory_manager_->Close(dirname); 1515 break; 1516 case 1: 1517 { 1518 directory_manager_->Open(dirname); 1519 ScopedDirLookup dir(directory_manager_, dirname); 1520 CHECK(dir.good()); 1521 } 1522 break; 1523 case 2: 1524 { 1525 ScopedDirLookup dir(directory_manager_, dirname); 1526 CHECK(dir.good()); 1527 } 1528 break; 1529 case 3: 1530 { 1531 ScopedDirLookup dir(directory_manager_, dirname); 1532 CHECK(dir.good()); 1533 ReadTransaction trans(dir, __FILE__, __LINE__); 1534 Entry e(&trans, GET_BY_ID, jeff_id); 1535 ExpectDataFromBookmarkFaviconEquals(&trans, &e, test_bytes, 1536 sizeof(test_bytes)); 1537 } 1538 // Same result as CloseAllDirectories, but more code coverage. 1539 directory_manager_->Close(dirname); 1540 break; 1541 } 1542 step_->number += 1; 1543 step_->condvar.Signal(); 1544 } 1545 } 1546 1547 DISALLOW_COPY_AND_ASSIGN(DirectoryKernelStalenessBugDelegate); 1548}; 1549 1550TEST(SyncableDirectoryManager, DirectoryKernelStalenessBug) { 1551 Step step; 1552 1553 DirectoryManager dirman(FilePath(FILE_PATH_LITERAL("."))); 1554 DirectoryKernelStalenessBugDelegate thread_delegate_1(0, &step, &dirman); 1555 DirectoryKernelStalenessBugDelegate thread_delegate_2(1, &step, &dirman); 1556 1557 base::PlatformThreadHandle thread_handle_1; 1558 base::PlatformThreadHandle thread_handle_2; 1559 1560 ASSERT_TRUE( 1561 base::PlatformThread::Create(0, &thread_delegate_1, &thread_handle_1)); 1562 ASSERT_TRUE( 1563 base::PlatformThread::Create(0, &thread_delegate_2, &thread_handle_2)); 1564 1565 base::PlatformThread::Join(thread_handle_1); 1566 base::PlatformThread::Join(thread_handle_2); 1567} 1568 1569class StressTransactionsDelegate : public base::PlatformThread::Delegate { 1570 public: 1571 StressTransactionsDelegate(DirectoryManager* dm, 1572 const std::string& dirname, 1573 int thread_number) 1574 : directory_manager_(dm), 1575 dirname_(dirname), 1576 thread_number_(thread_number) {} 1577 1578 private: 1579 DirectoryManager* const directory_manager_; 1580 std::string dirname_; 1581 const int thread_number_; 1582 1583 // PlatformThread::Delegate methods: 1584 virtual void ThreadMain() { 1585 ScopedDirLookup dir(directory_manager_, dirname_); 1586 CHECK(dir.good()); 1587 int entry_count = 0; 1588 std::string path_name; 1589 1590 for (int i = 0; i < 20; ++i) { 1591 const int rand_action = rand() % 10; 1592 if (rand_action < 4 && !path_name.empty()) { 1593 ReadTransaction trans(dir, __FILE__, __LINE__); 1594 CHECK(1 == CountEntriesWithName(&trans, trans.root_id(), path_name)); 1595 base::PlatformThread::Sleep(rand() % 10); 1596 } else { 1597 std::string unique_name = StringPrintf("%d.%d", thread_number_, 1598 entry_count++); 1599 path_name.assign(unique_name.begin(), unique_name.end()); 1600 WriteTransaction trans(dir, UNITTEST, __FILE__, __LINE__); 1601 MutableEntry e(&trans, CREATE, trans.root_id(), path_name); 1602 CHECK(e.good()); 1603 base::PlatformThread::Sleep(rand() % 20); 1604 e.Put(IS_UNSYNCED, true); 1605 if (e.Put(ID, TestIdFactory::FromNumber(rand())) && 1606 e.Get(ID).ServerKnows() && !e.Get(ID).IsRoot()) { 1607 e.Put(BASE_VERSION, 1); 1608 } 1609 } 1610 } 1611 } 1612 1613 DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate); 1614}; 1615 1616TEST(SyncableDirectory, StressTransactions) { 1617 DirectoryManager dirman(FilePath(FILE_PATH_LITERAL("."))); 1618 std::string dirname = "stress"; 1619 file_util::Delete(dirman.GetSyncDataDatabasePath(), true); 1620 dirman.Open(dirname); 1621 1622 const int kThreadCount = 7; 1623 base::PlatformThreadHandle threads[kThreadCount]; 1624 scoped_ptr<StressTransactionsDelegate> thread_delegates[kThreadCount]; 1625 1626 for (int i = 0; i < kThreadCount; ++i) { 1627 thread_delegates[i].reset( 1628 new StressTransactionsDelegate(&dirman, dirname, i)); 1629 ASSERT_TRUE(base::PlatformThread::Create( 1630 0, thread_delegates[i].get(), &threads[i])); 1631 } 1632 1633 for (int i = 0; i < kThreadCount; ++i) { 1634 base::PlatformThread::Join(threads[i]); 1635 } 1636 1637 dirman.Close(dirname); 1638 file_util::Delete(dirman.GetSyncDataDatabasePath(), true); 1639} 1640 1641TEST(Syncable, ComparePathNames) { 1642 struct { 1643 char a; 1644 char b; 1645 int expected_result; 1646 } tests[] = { 1647 { 'A', 'A', 0 }, 1648 { 'A', 'a', 0 }, 1649 { 'a', 'A', 0 }, 1650 { 'a', 'a', 0 }, 1651 { 'A', 'B', -1 }, 1652 { 'A', 'b', -1 }, 1653 { 'a', 'B', -1 }, 1654 { 'a', 'b', -1 }, 1655 { 'B', 'A', 1 }, 1656 { 'B', 'a', 1 }, 1657 { 'b', 'A', 1 }, 1658 { 'b', 'a', 1 } }; 1659 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { 1660 std::string a(1, tests[i].a); 1661 std::string b(1, tests[i].b); 1662 const int result = ComparePathNames(a, b); 1663 if (result != tests[i].expected_result) { 1664 ADD_FAILURE() << "ComparePathNames(" << tests[i].a << ", " << tests[i].b 1665 << ") returned " << result << "; expected " 1666 << tests[i].expected_result; 1667 } 1668 } 1669} 1670 1671class SyncableClientTagTest : public SyncableDirectoryTest { 1672 public: 1673 static const int kBaseVersion = 1; 1674 const char* test_name_; 1675 const char* test_tag_; 1676 1677 SyncableClientTagTest() : test_name_("test_name"), test_tag_("dietcoke") {} 1678 1679 bool CreateWithDefaultTag(Id id, bool deleted) { 1680 return CreateWithTag(test_tag_, id, deleted); 1681 } 1682 1683 // Attempt to create an entry with a default tag. 1684 bool CreateWithTag(const char* tag, Id id, bool deleted) { 1685 WriteTransaction wtrans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1686 MutableEntry me(&wtrans, CREATE, wtrans.root_id(), test_name_); 1687 CHECK(me.good()); 1688 me.Put(ID, id); 1689 if (id.ServerKnows()) { 1690 me.Put(BASE_VERSION, kBaseVersion); 1691 } 1692 me.Put(IS_DEL, deleted); 1693 me.Put(IS_UNSYNCED, true); 1694 me.Put(IS_DIR, false); 1695 return me.Put(UNIQUE_CLIENT_TAG, tag); 1696 } 1697 1698 // Verify an entry exists with the default tag. 1699 void VerifyTag(Id id, bool deleted) { 1700 // Should still be present and valid in the client tag index. 1701 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1702 Entry me(&trans, GET_BY_CLIENT_TAG, test_tag_); 1703 CHECK(me.good()); 1704 EXPECT_EQ(me.Get(ID), id); 1705 EXPECT_EQ(me.Get(UNIQUE_CLIENT_TAG), test_tag_); 1706 EXPECT_EQ(me.Get(IS_DEL), deleted); 1707 EXPECT_EQ(me.Get(IS_UNSYNCED), true); 1708 } 1709 1710 protected: 1711 TestIdFactory factory_; 1712}; 1713 1714TEST_F(SyncableClientTagTest, TestClientTagClear) { 1715 Id server_id = factory_.NewServerId(); 1716 EXPECT_TRUE(CreateWithDefaultTag(server_id, false)); 1717 { 1718 WriteTransaction trans(dir_.get(), UNITTEST, __FILE__, __LINE__); 1719 MutableEntry me(&trans, GET_BY_CLIENT_TAG, test_tag_); 1720 EXPECT_TRUE(me.good()); 1721 me.Put(UNIQUE_CLIENT_TAG, ""); 1722 } 1723 { 1724 ReadTransaction trans(dir_.get(), __FILE__, __LINE__); 1725 Entry by_tag(&trans, GET_BY_CLIENT_TAG, test_tag_); 1726 EXPECT_FALSE(by_tag.good()); 1727 1728 Entry by_id(&trans, GET_BY_ID, server_id); 1729 EXPECT_TRUE(by_id.good()); 1730 EXPECT_TRUE(by_id.Get(UNIQUE_CLIENT_TAG).empty()); 1731 } 1732} 1733 1734TEST_F(SyncableClientTagTest, TestClientTagIndexServerId) { 1735 Id server_id = factory_.NewServerId(); 1736 EXPECT_TRUE(CreateWithDefaultTag(server_id, false)); 1737 VerifyTag(server_id, false); 1738} 1739 1740TEST_F(SyncableClientTagTest, TestClientTagIndexClientId) { 1741 Id client_id = factory_.NewLocalId(); 1742 EXPECT_TRUE(CreateWithDefaultTag(client_id, false)); 1743 VerifyTag(client_id, false); 1744} 1745 1746TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexClientId) { 1747 Id client_id = factory_.NewLocalId(); 1748 EXPECT_TRUE(CreateWithDefaultTag(client_id, true)); 1749 VerifyTag(client_id, true); 1750} 1751 1752TEST_F(SyncableClientTagTest, TestDeletedClientTagIndexServerId) { 1753 Id server_id = factory_.NewServerId(); 1754 EXPECT_TRUE(CreateWithDefaultTag(server_id, true)); 1755 VerifyTag(server_id, true); 1756} 1757 1758TEST_F(SyncableClientTagTest, TestClientTagIndexDuplicateServer) { 1759 EXPECT_TRUE(CreateWithDefaultTag(factory_.NewServerId(), true)); 1760 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), true)); 1761 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewServerId(), false)); 1762 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), false)); 1763 EXPECT_FALSE(CreateWithDefaultTag(factory_.NewLocalId(), true)); 1764} 1765 1766} // namespace 1767} // namespace syncable 1768