Home | History | Annotate | Download | only in syncable
      1 // Copyright 2014 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 "sync/syncable/directory_unittest.h"
      6 
      7 #include "base/strings/stringprintf.h"
      8 #include "base/test/values_test_util.h"
      9 #include "sync/internal_api/public/base/attachment_id_proto.h"
     10 #include "sync/syncable/syncable_proto_util.h"
     11 #include "sync/syncable/syncable_util.h"
     12 #include "sync/syncable/syncable_write_transaction.h"
     13 #include "sync/test/engine/test_syncable_utils.h"
     14 #include "sync/test/test_directory_backing_store.h"
     15 
     16 using base::ExpectDictBooleanValue;
     17 using base::ExpectDictStringValue;
     18 
     19 namespace syncer {
     20 
     21 namespace syncable {
     22 
     23 namespace {
     24 
     25 bool IsLegalNewParent(const Entry& a, const Entry& b) {
     26   return IsLegalNewParent(a.trans(), a.GetId(), b.GetId());
     27 }
     28 
     29 void PutDataAsBookmarkFavicon(WriteTransaction* wtrans,
     30                               MutableEntry* e,
     31                               const char* bytes,
     32                               size_t bytes_length) {
     33   sync_pb::EntitySpecifics specifics;
     34   specifics.mutable_bookmark()->set_url("http://demo/");
     35   specifics.mutable_bookmark()->set_favicon(bytes, bytes_length);
     36   e->PutSpecifics(specifics);
     37 }
     38 
     39 void ExpectDataFromBookmarkFaviconEquals(BaseTransaction* trans,
     40                                          Entry* e,
     41                                          const char* bytes,
     42                                          size_t bytes_length) {
     43   ASSERT_TRUE(e->good());
     44   ASSERT_TRUE(e->GetSpecifics().has_bookmark());
     45   ASSERT_EQ("http://demo/", e->GetSpecifics().bookmark().url());
     46   ASSERT_EQ(std::string(bytes, bytes_length),
     47             e->GetSpecifics().bookmark().favicon());
     48 }
     49 
     50 }  // namespace
     51 
     52 const char SyncableDirectoryTest::kDirectoryName[] = "Foo";
     53 
     54 SyncableDirectoryTest::SyncableDirectoryTest() {
     55 }
     56 
     57 SyncableDirectoryTest::~SyncableDirectoryTest() {
     58 }
     59 
     60 void SyncableDirectoryTest::SetUp() {
     61   ASSERT_TRUE(connection_.OpenInMemory());
     62   ASSERT_EQ(OPENED, ReopenDirectory());
     63 }
     64 
     65 void SyncableDirectoryTest::TearDown() {
     66   if (dir_)
     67     dir_->SaveChanges();
     68   dir_.reset();
     69 }
     70 
     71 DirOpenResult SyncableDirectoryTest::ReopenDirectory() {
     72   // Use a TestDirectoryBackingStore and sql::Connection so we can have test
     73   // data persist across Directory object lifetimes while getting the
     74   // performance benefits of not writing to disk.
     75   dir_.reset(
     76       new Directory(new TestDirectoryBackingStore(kDirectoryName, &connection_),
     77                     &handler_,
     78                     NULL,
     79                     NULL,
     80                     NULL));
     81 
     82   DirOpenResult open_result =
     83       dir_->Open(kDirectoryName, &delegate_, NullTransactionObserver());
     84 
     85   if (open_result != OPENED) {
     86     dir_.reset();
     87   }
     88 
     89   return open_result;
     90 }
     91 
     92 // Creates an empty entry and sets the ID field to a default one.
     93 void SyncableDirectoryTest::CreateEntry(const ModelType& model_type,
     94                                         const std::string& entryname) {
     95   CreateEntry(model_type, entryname, TestIdFactory::FromNumber(-99));
     96 }
     97 
     98 // Creates an empty entry and sets the ID field to id.
     99 void SyncableDirectoryTest::CreateEntry(const ModelType& model_type,
    100                                         const std::string& entryname,
    101                                         const int id) {
    102   CreateEntry(model_type, entryname, TestIdFactory::FromNumber(id));
    103 }
    104 
    105 void SyncableDirectoryTest::CreateEntry(const ModelType& model_type,
    106                                         const std::string& entryname,
    107                                         const Id& id) {
    108   CreateEntryWithAttachmentMetadata(
    109       model_type, entryname, id, sync_pb::AttachmentMetadata());
    110 }
    111 
    112 void SyncableDirectoryTest::CreateEntryWithAttachmentMetadata(
    113     const ModelType& model_type,
    114     const std::string& entryname,
    115     const Id& id,
    116     const sync_pb::AttachmentMetadata& attachment_metadata) {
    117   WriteTransaction wtrans(FROM_HERE, UNITTEST, dir_.get());
    118   MutableEntry me(&wtrans, CREATE, model_type, wtrans.root_id(), entryname);
    119   ASSERT_TRUE(me.good());
    120   me.PutId(id);
    121   me.PutAttachmentMetadata(attachment_metadata);
    122   me.PutIsUnsynced(true);
    123 }
    124 
    125 void SyncableDirectoryTest::DeleteEntry(const Id& id) {
    126   WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    127   MutableEntry entry(&trans, GET_BY_ID, id);
    128   ASSERT_TRUE(entry.good());
    129   entry.PutIsDel(true);
    130 }
    131 
    132 DirOpenResult SyncableDirectoryTest::SimulateSaveAndReloadDir() {
    133   if (!dir_->SaveChanges())
    134     return FAILED_IN_UNITTEST;
    135 
    136   return ReopenDirectory();
    137 }
    138 
    139 DirOpenResult SyncableDirectoryTest::SimulateCrashAndReloadDir() {
    140   return ReopenDirectory();
    141 }
    142 
    143 void SyncableDirectoryTest::GetAllMetaHandles(BaseTransaction* trans,
    144                                               MetahandleSet* result) {
    145   dir_->GetAllMetaHandles(trans, result);
    146 }
    147 
    148 void SyncableDirectoryTest::CheckPurgeEntriesWithTypeInSucceeded(
    149     ModelTypeSet types_to_purge,
    150     bool before_reload) {
    151   SCOPED_TRACE(testing::Message("Before reload: ") << before_reload);
    152   {
    153     ReadTransaction trans(FROM_HERE, dir_.get());
    154     MetahandleSet all_set;
    155     dir_->GetAllMetaHandles(&trans, &all_set);
    156     EXPECT_EQ(4U, all_set.size());
    157     if (before_reload)
    158       EXPECT_EQ(6U, dir_->kernel_->metahandles_to_purge.size());
    159     for (MetahandleSet::iterator iter = all_set.begin(); iter != all_set.end();
    160          ++iter) {
    161       Entry e(&trans, GET_BY_HANDLE, *iter);
    162       const ModelType local_type = e.GetModelType();
    163       const ModelType server_type = e.GetServerModelType();
    164 
    165       // Note the dance around incrementing |it|, since we sometimes erase().
    166       if ((IsRealDataType(local_type) && types_to_purge.Has(local_type)) ||
    167           (IsRealDataType(server_type) && types_to_purge.Has(server_type))) {
    168         FAIL() << "Illegal type should have been deleted.";
    169       }
    170     }
    171   }
    172 
    173   for (ModelTypeSet::Iterator it = types_to_purge.First(); it.Good();
    174        it.Inc()) {
    175     EXPECT_FALSE(dir_->InitialSyncEndedForType(it.Get()));
    176     sync_pb::DataTypeProgressMarker progress;
    177     dir_->GetDownloadProgress(it.Get(), &progress);
    178     EXPECT_EQ("", progress.token());
    179 
    180     ReadTransaction trans(FROM_HERE, dir_.get());
    181     sync_pb::DataTypeContext context;
    182     dir_->GetDataTypeContext(&trans, it.Get(), &context);
    183     EXPECT_TRUE(context.SerializeAsString().empty());
    184   }
    185   EXPECT_FALSE(types_to_purge.Has(BOOKMARKS));
    186   EXPECT_TRUE(dir_->InitialSyncEndedForType(BOOKMARKS));
    187 }
    188 
    189 bool SyncableDirectoryTest::IsInDirtyMetahandles(int64 metahandle) {
    190   return 1 == dir_->kernel_->dirty_metahandles.count(metahandle);
    191 }
    192 
    193 bool SyncableDirectoryTest::IsInMetahandlesToPurge(int64 metahandle) {
    194   return 1 == dir_->kernel_->metahandles_to_purge.count(metahandle);
    195 }
    196 
    197 scoped_ptr<Directory>& SyncableDirectoryTest::dir() {
    198   return dir_;
    199 }
    200 
    201 DirectoryChangeDelegate* SyncableDirectoryTest::directory_change_delegate() {
    202   return &delegate_;
    203 }
    204 
    205 Encryptor* SyncableDirectoryTest::encryptor() {
    206   return &encryptor_;
    207 }
    208 
    209 UnrecoverableErrorHandler*
    210 SyncableDirectoryTest::unrecoverable_error_handler() {
    211   return &handler_;
    212 }
    213 
    214 void SyncableDirectoryTest::ValidateEntry(BaseTransaction* trans,
    215                                           int64 id,
    216                                           bool check_name,
    217                                           const std::string& name,
    218                                           int64 base_version,
    219                                           int64 server_version,
    220                                           bool is_del) {
    221   Entry e(trans, GET_BY_ID, TestIdFactory::FromNumber(id));
    222   ASSERT_TRUE(e.good());
    223   if (check_name)
    224     ASSERT_TRUE(name == e.GetNonUniqueName());
    225   ASSERT_TRUE(base_version == e.GetBaseVersion());
    226   ASSERT_TRUE(server_version == e.GetServerVersion());
    227   ASSERT_TRUE(is_del == e.GetIsDel());
    228 }
    229 
    230 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsMetahandlesToPurge) {
    231   const int metas_to_create = 50;
    232   MetahandleSet expected_purges;
    233   MetahandleSet all_handles;
    234   {
    235     dir()->SetDownloadProgress(BOOKMARKS, BuildProgress(BOOKMARKS));
    236     dir()->SetDownloadProgress(PREFERENCES, BuildProgress(PREFERENCES));
    237     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    238     for (int i = 0; i < metas_to_create; i++) {
    239       MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo");
    240       e.PutIsUnsynced(true);
    241       sync_pb::EntitySpecifics specs;
    242       if (i % 2 == 0) {
    243         AddDefaultFieldValue(BOOKMARKS, &specs);
    244         expected_purges.insert(e.GetMetahandle());
    245         all_handles.insert(e.GetMetahandle());
    246       } else {
    247         AddDefaultFieldValue(PREFERENCES, &specs);
    248         all_handles.insert(e.GetMetahandle());
    249       }
    250       e.PutSpecifics(specs);
    251       e.PutServerSpecifics(specs);
    252     }
    253   }
    254 
    255   ModelTypeSet to_purge(BOOKMARKS);
    256   dir()->PurgeEntriesWithTypeIn(to_purge, ModelTypeSet(), ModelTypeSet());
    257 
    258   Directory::SaveChangesSnapshot snapshot1;
    259   base::AutoLock scoped_lock(dir()->kernel_->save_changes_mutex);
    260   dir()->TakeSnapshotForSaveChanges(&snapshot1);
    261   EXPECT_TRUE(expected_purges == snapshot1.metahandles_to_purge);
    262 
    263   to_purge.Clear();
    264   to_purge.Put(PREFERENCES);
    265   dir()->PurgeEntriesWithTypeIn(to_purge, ModelTypeSet(), ModelTypeSet());
    266 
    267   dir()->HandleSaveChangesFailure(snapshot1);
    268 
    269   Directory::SaveChangesSnapshot snapshot2;
    270   dir()->TakeSnapshotForSaveChanges(&snapshot2);
    271   EXPECT_TRUE(all_handles == snapshot2.metahandles_to_purge);
    272 }
    273 
    274 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsAllDirtyHandlesTest) {
    275   const int metahandles_to_create = 100;
    276   std::vector<int64> expected_dirty_metahandles;
    277   {
    278     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    279     for (int i = 0; i < metahandles_to_create; i++) {
    280       MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo");
    281       expected_dirty_metahandles.push_back(e.GetMetahandle());
    282       e.PutIsUnsynced(true);
    283     }
    284   }
    285   // Fake SaveChanges() and make sure we got what we expected.
    286   {
    287     Directory::SaveChangesSnapshot snapshot;
    288     base::AutoLock scoped_lock(dir()->kernel_->save_changes_mutex);
    289     dir()->TakeSnapshotForSaveChanges(&snapshot);
    290     // Make sure there's an entry for each new metahandle.  Make sure all
    291     // entries are marked dirty.
    292     ASSERT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size());
    293     for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
    294          i != snapshot.dirty_metas.end();
    295          ++i) {
    296       ASSERT_TRUE((*i)->is_dirty());
    297     }
    298     dir()->VacuumAfterSaveChanges(snapshot);
    299   }
    300   // Put a new value with existing transactions as well as adding new ones.
    301   {
    302     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    303     std::vector<int64> new_dirty_metahandles;
    304     for (std::vector<int64>::const_iterator i =
    305              expected_dirty_metahandles.begin();
    306          i != expected_dirty_metahandles.end();
    307          ++i) {
    308       // Change existing entries to directories to dirty them.
    309       MutableEntry e1(&trans, GET_BY_HANDLE, *i);
    310       e1.PutIsDir(true);
    311       e1.PutIsUnsynced(true);
    312       // Add new entries
    313       MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), "bar");
    314       e2.PutIsUnsynced(true);
    315       new_dirty_metahandles.push_back(e2.GetMetahandle());
    316     }
    317     expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
    318                                       new_dirty_metahandles.begin(),
    319                                       new_dirty_metahandles.end());
    320   }
    321   // Fake SaveChanges() and make sure we got what we expected.
    322   {
    323     Directory::SaveChangesSnapshot snapshot;
    324     base::AutoLock scoped_lock(dir()->kernel_->save_changes_mutex);
    325     dir()->TakeSnapshotForSaveChanges(&snapshot);
    326     // Make sure there's an entry for each new metahandle.  Make sure all
    327     // entries are marked dirty.
    328     EXPECT_EQ(expected_dirty_metahandles.size(), snapshot.dirty_metas.size());
    329     for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
    330          i != snapshot.dirty_metas.end();
    331          ++i) {
    332       EXPECT_TRUE((*i)->is_dirty());
    333     }
    334     dir()->VacuumAfterSaveChanges(snapshot);
    335   }
    336 }
    337 
    338 TEST_F(SyncableDirectoryTest, TakeSnapshotGetsOnlyDirtyHandlesTest) {
    339   const int metahandles_to_create = 100;
    340 
    341   // half of 2 * metahandles_to_create
    342   const unsigned int number_changed = 100u;
    343   std::vector<int64> expected_dirty_metahandles;
    344   {
    345     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    346     for (int i = 0; i < metahandles_to_create; i++) {
    347       MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), "foo");
    348       expected_dirty_metahandles.push_back(e.GetMetahandle());
    349       e.PutIsUnsynced(true);
    350     }
    351   }
    352   dir()->SaveChanges();
    353   // Put a new value with existing transactions as well as adding new ones.
    354   {
    355     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    356     std::vector<int64> new_dirty_metahandles;
    357     for (std::vector<int64>::const_iterator i =
    358              expected_dirty_metahandles.begin();
    359          i != expected_dirty_metahandles.end();
    360          ++i) {
    361       // Change existing entries to directories to dirty them.
    362       MutableEntry e1(&trans, GET_BY_HANDLE, *i);
    363       ASSERT_TRUE(e1.good());
    364       e1.PutIsDir(true);
    365       e1.PutIsUnsynced(true);
    366       // Add new entries
    367       MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), "bar");
    368       e2.PutIsUnsynced(true);
    369       new_dirty_metahandles.push_back(e2.GetMetahandle());
    370     }
    371     expected_dirty_metahandles.insert(expected_dirty_metahandles.end(),
    372                                       new_dirty_metahandles.begin(),
    373                                       new_dirty_metahandles.end());
    374   }
    375   dir()->SaveChanges();
    376   // Don't make any changes whatsoever and ensure nothing comes back.
    377   {
    378     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    379     for (std::vector<int64>::const_iterator i =
    380              expected_dirty_metahandles.begin();
    381          i != expected_dirty_metahandles.end();
    382          ++i) {
    383       MutableEntry e(&trans, GET_BY_HANDLE, *i);
    384       ASSERT_TRUE(e.good());
    385       // We aren't doing anything to dirty these entries.
    386     }
    387   }
    388   // Fake SaveChanges() and make sure we got what we expected.
    389   {
    390     Directory::SaveChangesSnapshot snapshot;
    391     base::AutoLock scoped_lock(dir()->kernel_->save_changes_mutex);
    392     dir()->TakeSnapshotForSaveChanges(&snapshot);
    393     // Make sure there are no dirty_metahandles.
    394     EXPECT_EQ(0u, snapshot.dirty_metas.size());
    395     dir()->VacuumAfterSaveChanges(snapshot);
    396   }
    397   {
    398     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    399     bool should_change = false;
    400     for (std::vector<int64>::const_iterator i =
    401              expected_dirty_metahandles.begin();
    402          i != expected_dirty_metahandles.end();
    403          ++i) {
    404       // Maybe change entries by flipping IS_DIR.
    405       MutableEntry e(&trans, GET_BY_HANDLE, *i);
    406       ASSERT_TRUE(e.good());
    407       should_change = !should_change;
    408       if (should_change) {
    409         bool not_dir = !e.GetIsDir();
    410         e.PutIsDir(not_dir);
    411         e.PutIsUnsynced(true);
    412       }
    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 changed metahandle.  Make sure all
    421     // entries are marked dirty.
    422     EXPECT_EQ(number_changed, snapshot.dirty_metas.size());
    423     for (EntryKernelSet::const_iterator i = snapshot.dirty_metas.begin();
    424          i != snapshot.dirty_metas.end();
    425          ++i) {
    426       EXPECT_TRUE((*i)->is_dirty());
    427     }
    428     dir()->VacuumAfterSaveChanges(snapshot);
    429   }
    430 }
    431 
    432 // Test delete journals management.
    433 TEST_F(SyncableDirectoryTest, ManageDeleteJournals) {
    434   sync_pb::EntitySpecifics bookmark_specifics;
    435   AddDefaultFieldValue(BOOKMARKS, &bookmark_specifics);
    436   bookmark_specifics.mutable_bookmark()->set_url("url");
    437 
    438   Id id1 = TestIdFactory::FromNumber(-1);
    439   Id id2 = TestIdFactory::FromNumber(-2);
    440   int64 handle1 = 0;
    441   int64 handle2 = 0;
    442   {
    443     // Create two bookmark entries and save in database.
    444     CreateEntry(BOOKMARKS, "item1", id1);
    445     CreateEntry(BOOKMARKS, "item2", id2);
    446     {
    447       WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    448       MutableEntry item1(&trans, GET_BY_ID, id1);
    449       ASSERT_TRUE(item1.good());
    450       handle1 = item1.GetMetahandle();
    451       item1.PutSpecifics(bookmark_specifics);
    452       item1.PutServerSpecifics(bookmark_specifics);
    453       MutableEntry item2(&trans, GET_BY_ID, id2);
    454       ASSERT_TRUE(item2.good());
    455       handle2 = item2.GetMetahandle();
    456       item2.PutSpecifics(bookmark_specifics);
    457       item2.PutServerSpecifics(bookmark_specifics);
    458     }
    459     ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
    460   }
    461 
    462   {  // Test adding and saving delete journals.
    463     DeleteJournal* delete_journal = dir()->delete_journal();
    464     {
    465       WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    466       EntryKernelSet journal_entries;
    467       delete_journal->GetDeleteJournals(&trans, BOOKMARKS, &journal_entries);
    468       ASSERT_EQ(0u, journal_entries.size());
    469 
    470       // Set SERVER_IS_DEL of the entries to true and they should be added to
    471       // delete journals.
    472       MutableEntry item1(&trans, GET_BY_ID, id1);
    473       ASSERT_TRUE(item1.good());
    474       item1.PutServerIsDel(true);
    475       MutableEntry item2(&trans, GET_BY_ID, id2);
    476       ASSERT_TRUE(item2.good());
    477       item2.PutServerIsDel(true);
    478       EntryKernel tmp;
    479       tmp.put(ID, id1);
    480       EXPECT_TRUE(delete_journal->delete_journals_.count(&tmp));
    481       tmp.put(ID, id2);
    482       EXPECT_TRUE(delete_journal->delete_journals_.count(&tmp));
    483     }
    484 
    485     // Save delete journals in database and verify memory clearing.
    486     ASSERT_TRUE(dir()->SaveChanges());
    487     {
    488       ReadTransaction trans(FROM_HERE, dir().get());
    489       EXPECT_EQ(0u, delete_journal->GetDeleteJournalSize(&trans));
    490     }
    491     ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
    492   }
    493 
    494   {
    495     {
    496       // Test reading delete journals from database.
    497       WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    498       DeleteJournal* delete_journal = dir()->delete_journal();
    499       EntryKernelSet journal_entries;
    500       delete_journal->GetDeleteJournals(&trans, BOOKMARKS, &journal_entries);
    501       ASSERT_EQ(2u, journal_entries.size());
    502       EntryKernel tmp;
    503       tmp.put(META_HANDLE, handle1);
    504       EXPECT_TRUE(journal_entries.count(&tmp));
    505       tmp.put(META_HANDLE, handle2);
    506       EXPECT_TRUE(journal_entries.count(&tmp));
    507 
    508       // Purge item2.
    509       MetahandleSet to_purge;
    510       to_purge.insert(handle2);
    511       delete_journal->PurgeDeleteJournals(&trans, to_purge);
    512 
    513       // Verify that item2 is purged from journals in memory and will be
    514       // purged from database.
    515       tmp.put(ID, id2);
    516       EXPECT_FALSE(delete_journal->delete_journals_.count(&tmp));
    517       EXPECT_EQ(1u, delete_journal->delete_journals_to_purge_.size());
    518       EXPECT_TRUE(delete_journal->delete_journals_to_purge_.count(handle2));
    519     }
    520     ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
    521   }
    522 
    523   {
    524     {
    525       // Verify purged entry is gone in database.
    526       WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    527       DeleteJournal* delete_journal = dir()->delete_journal();
    528       EntryKernelSet journal_entries;
    529       delete_journal->GetDeleteJournals(&trans, BOOKMARKS, &journal_entries);
    530       ASSERT_EQ(1u, journal_entries.size());
    531       EntryKernel tmp;
    532       tmp.put(ID, id1);
    533       tmp.put(META_HANDLE, handle1);
    534       EXPECT_TRUE(journal_entries.count(&tmp));
    535 
    536       // Undelete item1.
    537       MutableEntry item1(&trans, GET_BY_ID, id1);
    538       ASSERT_TRUE(item1.good());
    539       item1.PutServerIsDel(false);
    540       EXPECT_TRUE(delete_journal->delete_journals_.empty());
    541       EXPECT_EQ(1u, delete_journal->delete_journals_to_purge_.size());
    542       EXPECT_TRUE(delete_journal->delete_journals_to_purge_.count(handle1));
    543     }
    544     ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
    545   }
    546 
    547   {
    548     // Verify undeleted entry is gone from database.
    549     ReadTransaction trans(FROM_HERE, dir().get());
    550     DeleteJournal* delete_journal = dir()->delete_journal();
    551     ASSERT_EQ(0u, delete_journal->GetDeleteJournalSize(&trans));
    552   }
    553 }
    554 
    555 TEST_F(SyncableDirectoryTest, TestBasicLookupNonExistantID) {
    556   ReadTransaction rtrans(FROM_HERE, dir().get());
    557   Entry e(&rtrans, GET_BY_ID, TestIdFactory::FromNumber(-99));
    558   ASSERT_FALSE(e.good());
    559 }
    560 
    561 TEST_F(SyncableDirectoryTest, TestBasicLookupValidID) {
    562   CreateEntry(BOOKMARKS, "rtc");
    563   ReadTransaction rtrans(FROM_HERE, dir().get());
    564   Entry e(&rtrans, GET_BY_ID, TestIdFactory::FromNumber(-99));
    565   ASSERT_TRUE(e.good());
    566 }
    567 
    568 TEST_F(SyncableDirectoryTest, TestDelete) {
    569   std::string name = "peanut butter jelly time";
    570   WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    571   MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
    572   ASSERT_TRUE(e1.good());
    573   e1.PutIsDel(true);
    574   MutableEntry e2(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
    575   ASSERT_TRUE(e2.good());
    576   e2.PutIsDel(true);
    577   MutableEntry e3(&trans, CREATE, BOOKMARKS, trans.root_id(), name);
    578   ASSERT_TRUE(e3.good());
    579   e3.PutIsDel(true);
    580 
    581   e1.PutIsDel(false);
    582   e2.PutIsDel(false);
    583   e3.PutIsDel(false);
    584 
    585   e1.PutIsDel(true);
    586   e2.PutIsDel(true);
    587   e3.PutIsDel(true);
    588 }
    589 
    590 TEST_F(SyncableDirectoryTest, TestGetUnsynced) {
    591   Directory::Metahandles handles;
    592   int64 handle1, handle2;
    593   {
    594     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    595 
    596     dir()->GetUnsyncedMetaHandles(&trans, &handles);
    597     ASSERT_TRUE(0 == handles.size());
    598 
    599     MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "abba");
    600     ASSERT_TRUE(e1.good());
    601     handle1 = e1.GetMetahandle();
    602     e1.PutBaseVersion(1);
    603     e1.PutIsDir(true);
    604     e1.PutId(TestIdFactory::FromNumber(101));
    605 
    606     MutableEntry e2(&trans, CREATE, BOOKMARKS, e1.GetId(), "bread");
    607     ASSERT_TRUE(e2.good());
    608     handle2 = e2.GetMetahandle();
    609     e2.PutBaseVersion(1);
    610     e2.PutId(TestIdFactory::FromNumber(102));
    611   }
    612   dir()->SaveChanges();
    613   {
    614     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    615 
    616     dir()->GetUnsyncedMetaHandles(&trans, &handles);
    617     ASSERT_TRUE(0 == handles.size());
    618 
    619     MutableEntry e3(&trans, GET_BY_HANDLE, handle1);
    620     ASSERT_TRUE(e3.good());
    621     e3.PutIsUnsynced(true);
    622   }
    623   dir()->SaveChanges();
    624   {
    625     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    626     dir()->GetUnsyncedMetaHandles(&trans, &handles);
    627     ASSERT_TRUE(1 == handles.size());
    628     ASSERT_TRUE(handle1 == handles[0]);
    629 
    630     MutableEntry e4(&trans, GET_BY_HANDLE, handle2);
    631     ASSERT_TRUE(e4.good());
    632     e4.PutIsUnsynced(true);
    633   }
    634   dir()->SaveChanges();
    635   {
    636     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    637     dir()->GetUnsyncedMetaHandles(&trans, &handles);
    638     ASSERT_TRUE(2 == handles.size());
    639     if (handle1 == handles[0]) {
    640       ASSERT_TRUE(handle2 == handles[1]);
    641     } else {
    642       ASSERT_TRUE(handle2 == handles[0]);
    643       ASSERT_TRUE(handle1 == handles[1]);
    644     }
    645 
    646     MutableEntry e5(&trans, GET_BY_HANDLE, handle1);
    647     ASSERT_TRUE(e5.good());
    648     ASSERT_TRUE(e5.GetIsUnsynced());
    649     ASSERT_TRUE(e5.PutIsUnsynced(false));
    650     ASSERT_FALSE(e5.GetIsUnsynced());
    651   }
    652   dir()->SaveChanges();
    653   {
    654     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    655     dir()->GetUnsyncedMetaHandles(&trans, &handles);
    656     ASSERT_TRUE(1 == handles.size());
    657     ASSERT_TRUE(handle2 == handles[0]);
    658   }
    659 }
    660 
    661 TEST_F(SyncableDirectoryTest, TestGetUnappliedUpdates) {
    662   std::vector<int64> handles;
    663   int64 handle1, handle2;
    664   const FullModelTypeSet all_types = FullModelTypeSet::All();
    665   {
    666     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    667 
    668     dir()->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
    669     ASSERT_TRUE(0 == handles.size());
    670 
    671     MutableEntry e1(&trans, CREATE, BOOKMARKS, trans.root_id(), "abba");
    672     ASSERT_TRUE(e1.good());
    673     handle1 = e1.GetMetahandle();
    674     e1.PutIsUnappliedUpdate(false);
    675     e1.PutBaseVersion(1);
    676     e1.PutId(TestIdFactory::FromNumber(101));
    677     e1.PutIsDir(true);
    678 
    679     MutableEntry e2(&trans, CREATE, BOOKMARKS, e1.GetId(), "bread");
    680     ASSERT_TRUE(e2.good());
    681     handle2 = e2.GetMetahandle();
    682     e2.PutIsUnappliedUpdate(false);
    683     e2.PutBaseVersion(1);
    684     e2.PutId(TestIdFactory::FromNumber(102));
    685   }
    686   dir()->SaveChanges();
    687   {
    688     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    689 
    690     dir()->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
    691     ASSERT_TRUE(0 == handles.size());
    692 
    693     MutableEntry e3(&trans, GET_BY_HANDLE, handle1);
    694     ASSERT_TRUE(e3.good());
    695     e3.PutIsUnappliedUpdate(true);
    696   }
    697   dir()->SaveChanges();
    698   {
    699     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    700     dir()->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
    701     ASSERT_TRUE(1 == handles.size());
    702     ASSERT_TRUE(handle1 == handles[0]);
    703 
    704     MutableEntry e4(&trans, GET_BY_HANDLE, handle2);
    705     ASSERT_TRUE(e4.good());
    706     e4.PutIsUnappliedUpdate(true);
    707   }
    708   dir()->SaveChanges();
    709   {
    710     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    711     dir()->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
    712     ASSERT_TRUE(2 == handles.size());
    713     if (handle1 == handles[0]) {
    714       ASSERT_TRUE(handle2 == handles[1]);
    715     } else {
    716       ASSERT_TRUE(handle2 == handles[0]);
    717       ASSERT_TRUE(handle1 == handles[1]);
    718     }
    719 
    720     MutableEntry e5(&trans, GET_BY_HANDLE, handle1);
    721     ASSERT_TRUE(e5.good());
    722     e5.PutIsUnappliedUpdate(false);
    723   }
    724   dir()->SaveChanges();
    725   {
    726     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    727     dir()->GetUnappliedUpdateMetaHandles(&trans, all_types, &handles);
    728     ASSERT_TRUE(1 == handles.size());
    729     ASSERT_TRUE(handle2 == handles[0]);
    730   }
    731 }
    732 
    733 TEST_F(SyncableDirectoryTest, DeleteBug_531383) {
    734   // Try to evoke a check failure...
    735   TestIdFactory id_factory;
    736   int64 grandchild_handle;
    737   {
    738     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get());
    739     MutableEntry parent(&wtrans, CREATE, BOOKMARKS, id_factory.root(), "Bob");
    740     ASSERT_TRUE(parent.good());
    741     parent.PutIsDir(true);
    742     parent.PutId(id_factory.NewServerId());
    743     parent.PutBaseVersion(1);
    744     MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "Bob");
    745     ASSERT_TRUE(child.good());
    746     child.PutIsDir(true);
    747     child.PutId(id_factory.NewServerId());
    748     child.PutBaseVersion(1);
    749     MutableEntry grandchild(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob");
    750     ASSERT_TRUE(grandchild.good());
    751     grandchild.PutId(id_factory.NewServerId());
    752     grandchild.PutBaseVersion(1);
    753     grandchild.PutIsDel(true);
    754     MutableEntry twin(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob");
    755     ASSERT_TRUE(twin.good());
    756     twin.PutIsDel(true);
    757     grandchild.PutIsDel(false);
    758 
    759     grandchild_handle = grandchild.GetMetahandle();
    760   }
    761   dir()->SaveChanges();
    762   {
    763     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get());
    764     MutableEntry grandchild(&wtrans, GET_BY_HANDLE, grandchild_handle);
    765     grandchild.PutIsDel(true);  // Used to CHECK fail here.
    766   }
    767 }
    768 
    769 TEST_F(SyncableDirectoryTest, TestIsLegalNewParent) {
    770   TestIdFactory id_factory;
    771   WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get());
    772   Entry root(&wtrans, GET_BY_ID, id_factory.root());
    773   ASSERT_TRUE(root.good());
    774   MutableEntry parent(&wtrans, CREATE, BOOKMARKS, root.GetId(), "Bob");
    775   ASSERT_TRUE(parent.good());
    776   parent.PutIsDir(true);
    777   parent.PutId(id_factory.NewServerId());
    778   parent.PutBaseVersion(1);
    779   MutableEntry child(&wtrans, CREATE, BOOKMARKS, parent.GetId(), "Bob");
    780   ASSERT_TRUE(child.good());
    781   child.PutIsDir(true);
    782   child.PutId(id_factory.NewServerId());
    783   child.PutBaseVersion(1);
    784   MutableEntry grandchild(&wtrans, CREATE, BOOKMARKS, child.GetId(), "Bob");
    785   ASSERT_TRUE(grandchild.good());
    786   grandchild.PutId(id_factory.NewServerId());
    787   grandchild.PutBaseVersion(1);
    788 
    789   MutableEntry parent2(&wtrans, CREATE, BOOKMARKS, root.GetId(), "Pete");
    790   ASSERT_TRUE(parent2.good());
    791   parent2.PutIsDir(true);
    792   parent2.PutId(id_factory.NewServerId());
    793   parent2.PutBaseVersion(1);
    794   MutableEntry child2(&wtrans, CREATE, BOOKMARKS, parent2.GetId(), "Pete");
    795   ASSERT_TRUE(child2.good());
    796   child2.PutIsDir(true);
    797   child2.PutId(id_factory.NewServerId());
    798   child2.PutBaseVersion(1);
    799   MutableEntry grandchild2(&wtrans, CREATE, BOOKMARKS, child2.GetId(), "Pete");
    800   ASSERT_TRUE(grandchild2.good());
    801   grandchild2.PutId(id_factory.NewServerId());
    802   grandchild2.PutBaseVersion(1);
    803   // resulting tree
    804   //           root
    805   //           /  |
    806   //     parent    parent2
    807   //          |    |
    808   //      child    child2
    809   //          |    |
    810   // grandchild    grandchild2
    811   ASSERT_TRUE(IsLegalNewParent(child, root));
    812   ASSERT_TRUE(IsLegalNewParent(child, parent));
    813   ASSERT_FALSE(IsLegalNewParent(child, child));
    814   ASSERT_FALSE(IsLegalNewParent(child, grandchild));
    815   ASSERT_TRUE(IsLegalNewParent(child, parent2));
    816   ASSERT_TRUE(IsLegalNewParent(child, grandchild2));
    817   ASSERT_FALSE(IsLegalNewParent(parent, grandchild));
    818   ASSERT_FALSE(IsLegalNewParent(root, grandchild));
    819   ASSERT_FALSE(IsLegalNewParent(parent, grandchild));
    820 }
    821 
    822 TEST_F(SyncableDirectoryTest, TestEntryIsInFolder) {
    823   // Create a subdir and an entry.
    824   int64 entry_handle;
    825   syncable::Id folder_id;
    826   syncable::Id entry_id;
    827   std::string entry_name = "entry";
    828 
    829   {
    830     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    831     MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "folder");
    832     ASSERT_TRUE(folder.good());
    833     folder.PutIsDir(true);
    834     EXPECT_TRUE(folder.PutIsUnsynced(true));
    835     folder_id = folder.GetId();
    836 
    837     MutableEntry entry(&trans, CREATE, BOOKMARKS, folder.GetId(), entry_name);
    838     ASSERT_TRUE(entry.good());
    839     entry_handle = entry.GetMetahandle();
    840     entry.PutIsUnsynced(true);
    841     entry_id = entry.GetId();
    842   }
    843 
    844   // Make sure we can find the entry in the folder.
    845   {
    846     ReadTransaction trans(FROM_HERE, dir().get());
    847     EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), entry_name));
    848     EXPECT_EQ(1, CountEntriesWithName(&trans, folder_id, entry_name));
    849 
    850     Entry entry(&trans, GET_BY_ID, entry_id);
    851     ASSERT_TRUE(entry.good());
    852     EXPECT_EQ(entry_handle, entry.GetMetahandle());
    853     EXPECT_TRUE(entry.GetNonUniqueName() == entry_name);
    854     EXPECT_TRUE(entry.GetParentId() == folder_id);
    855   }
    856 }
    857 
    858 TEST_F(SyncableDirectoryTest, TestParentIdIndexUpdate) {
    859   std::string child_name = "child";
    860 
    861   WriteTransaction wt(FROM_HERE, UNITTEST, dir().get());
    862   MutableEntry parent_folder(&wt, CREATE, BOOKMARKS, wt.root_id(), "folder1");
    863   parent_folder.PutIsUnsynced(true);
    864   parent_folder.PutIsDir(true);
    865 
    866   MutableEntry parent_folder2(&wt, CREATE, BOOKMARKS, wt.root_id(), "folder2");
    867   parent_folder2.PutIsUnsynced(true);
    868   parent_folder2.PutIsDir(true);
    869 
    870   MutableEntry child(&wt, CREATE, BOOKMARKS, parent_folder.GetId(), child_name);
    871   child.PutIsDir(true);
    872   child.PutIsUnsynced(true);
    873 
    874   ASSERT_TRUE(child.good());
    875 
    876   EXPECT_EQ(0, CountEntriesWithName(&wt, wt.root_id(), child_name));
    877   EXPECT_EQ(parent_folder.GetId(), child.GetParentId());
    878   EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder.GetId(), child_name));
    879   EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder2.GetId(), child_name));
    880   child.PutParentId(parent_folder2.GetId());
    881   EXPECT_EQ(parent_folder2.GetId(), child.GetParentId());
    882   EXPECT_EQ(0, CountEntriesWithName(&wt, parent_folder.GetId(), child_name));
    883   EXPECT_EQ(1, CountEntriesWithName(&wt, parent_folder2.GetId(), child_name));
    884 }
    885 
    886 TEST_F(SyncableDirectoryTest, TestNoReindexDeletedItems) {
    887   std::string folder_name = "folder";
    888   std::string new_name = "new_name";
    889 
    890   WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    891   MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), folder_name);
    892   ASSERT_TRUE(folder.good());
    893   folder.PutIsDir(true);
    894   folder.PutIsDel(true);
    895 
    896   EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
    897 
    898   MutableEntry deleted(&trans, GET_BY_ID, folder.GetId());
    899   ASSERT_TRUE(deleted.good());
    900   deleted.PutParentId(trans.root_id());
    901   deleted.PutNonUniqueName(new_name);
    902 
    903   EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), folder_name));
    904   EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), new_name));
    905 }
    906 
    907 TEST_F(SyncableDirectoryTest, TestCaseChangeRename) {
    908   WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    909   MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "CaseChange");
    910   ASSERT_TRUE(folder.good());
    911   folder.PutParentId(trans.root_id());
    912   folder.PutNonUniqueName("CASECHANGE");
    913   folder.PutIsDel(true);
    914 }
    915 
    916 // Create items of each model type, and check that GetModelType and
    917 // GetServerModelType return the right value.
    918 TEST_F(SyncableDirectoryTest, GetModelType) {
    919   TestIdFactory id_factory;
    920   ModelTypeSet protocol_types = ProtocolTypes();
    921   for (ModelTypeSet::Iterator iter = protocol_types.First(); iter.Good();
    922        iter.Inc()) {
    923     ModelType datatype = iter.Get();
    924     SCOPED_TRACE(testing::Message("Testing model type ") << datatype);
    925     switch (datatype) {
    926       case UNSPECIFIED:
    927       case TOP_LEVEL_FOLDER:
    928         continue;  // Datatype isn't a function of Specifics.
    929       default:
    930         break;
    931     }
    932     sync_pb::EntitySpecifics specifics;
    933     AddDefaultFieldValue(datatype, &specifics);
    934 
    935     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
    936 
    937     MutableEntry folder(&trans, CREATE, BOOKMARKS, trans.root_id(), "Folder");
    938     ASSERT_TRUE(folder.good());
    939     folder.PutId(id_factory.NewServerId());
    940     folder.PutSpecifics(specifics);
    941     folder.PutBaseVersion(1);
    942     folder.PutIsDir(true);
    943     folder.PutIsDel(false);
    944     ASSERT_EQ(datatype, folder.GetModelType());
    945 
    946     MutableEntry item(&trans, CREATE, BOOKMARKS, trans.root_id(), "Item");
    947     ASSERT_TRUE(item.good());
    948     item.PutId(id_factory.NewServerId());
    949     item.PutSpecifics(specifics);
    950     item.PutBaseVersion(1);
    951     item.PutIsDir(false);
    952     item.PutIsDel(false);
    953     ASSERT_EQ(datatype, item.GetModelType());
    954 
    955     // It's critical that deletion records retain their datatype, so that
    956     // they can be dispatched to the appropriate change processor.
    957     MutableEntry deleted_item(
    958         &trans, CREATE, BOOKMARKS, trans.root_id(), "Deleted Item");
    959     ASSERT_TRUE(item.good());
    960     deleted_item.PutId(id_factory.NewServerId());
    961     deleted_item.PutSpecifics(specifics);
    962     deleted_item.PutBaseVersion(1);
    963     deleted_item.PutIsDir(false);
    964     deleted_item.PutIsDel(true);
    965     ASSERT_EQ(datatype, deleted_item.GetModelType());
    966 
    967     MutableEntry server_folder(
    968         &trans, CREATE_NEW_UPDATE_ITEM, id_factory.NewServerId());
    969     ASSERT_TRUE(server_folder.good());
    970     server_folder.PutServerSpecifics(specifics);
    971     server_folder.PutBaseVersion(1);
    972     server_folder.PutServerIsDir(true);
    973     server_folder.PutServerIsDel(false);
    974     ASSERT_EQ(datatype, server_folder.GetServerModelType());
    975 
    976     MutableEntry server_item(
    977         &trans, CREATE_NEW_UPDATE_ITEM, id_factory.NewServerId());
    978     ASSERT_TRUE(server_item.good());
    979     server_item.PutServerSpecifics(specifics);
    980     server_item.PutBaseVersion(1);
    981     server_item.PutServerIsDir(false);
    982     server_item.PutServerIsDel(false);
    983     ASSERT_EQ(datatype, server_item.GetServerModelType());
    984 
    985     sync_pb::SyncEntity folder_entity;
    986     folder_entity.set_id_string(SyncableIdToProto(id_factory.NewServerId()));
    987     folder_entity.set_deleted(false);
    988     folder_entity.set_folder(true);
    989     folder_entity.mutable_specifics()->CopyFrom(specifics);
    990     ASSERT_EQ(datatype, GetModelType(folder_entity));
    991 
    992     sync_pb::SyncEntity item_entity;
    993     item_entity.set_id_string(SyncableIdToProto(id_factory.NewServerId()));
    994     item_entity.set_deleted(false);
    995     item_entity.set_folder(false);
    996     item_entity.mutable_specifics()->CopyFrom(specifics);
    997     ASSERT_EQ(datatype, GetModelType(item_entity));
    998   }
    999 }
   1000 
   1001 // A test that roughly mimics the directory interaction that occurs when a
   1002 // bookmark folder and entry are created then synced for the first time.  It is
   1003 // a more common variant of the 'DeletedAndUnsyncedChild' scenario tested below.
   1004 TEST_F(SyncableDirectoryTest, ChangeEntryIDAndUpdateChildren_ParentAndChild) {
   1005   TestIdFactory id_factory;
   1006   Id orig_parent_id;
   1007   Id orig_child_id;
   1008 
   1009   {
   1010     // Create two client-side items, a parent and child.
   1011     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1012 
   1013     MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
   1014     parent.PutIsDir(true);
   1015     parent.PutIsUnsynced(true);
   1016 
   1017     MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child");
   1018     child.PutIsUnsynced(true);
   1019 
   1020     orig_parent_id = parent.GetId();
   1021     orig_child_id = child.GetId();
   1022   }
   1023 
   1024   {
   1025     // Simulate what happens after committing two items.  Their IDs will be
   1026     // replaced with server IDs.  The child is renamed first, then the parent.
   1027     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1028 
   1029     MutableEntry parent(&trans, GET_BY_ID, orig_parent_id);
   1030     MutableEntry child(&trans, GET_BY_ID, orig_child_id);
   1031 
   1032     ChangeEntryIDAndUpdateChildren(&trans, &child, id_factory.NewServerId());
   1033     child.PutIsUnsynced(false);
   1034     child.PutBaseVersion(1);
   1035     child.PutServerVersion(1);
   1036 
   1037     ChangeEntryIDAndUpdateChildren(&trans, &parent, id_factory.NewServerId());
   1038     parent.PutIsUnsynced(false);
   1039     parent.PutBaseVersion(1);
   1040     parent.PutServerVersion(1);
   1041   }
   1042 
   1043   // Final check for validity.
   1044   EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
   1045 }
   1046 
   1047 // A test based on the scenario where we create a bookmark folder and entry
   1048 // locally, but with a twist.  In this case, the bookmark is deleted before we
   1049 // are able to sync either it or its parent folder.  This scenario used to cause
   1050 // directory corruption, see crbug.com/125381.
   1051 TEST_F(SyncableDirectoryTest,
   1052        ChangeEntryIDAndUpdateChildren_DeletedAndUnsyncedChild) {
   1053   TestIdFactory id_factory;
   1054   Id orig_parent_id;
   1055   Id orig_child_id;
   1056 
   1057   {
   1058     // Create two client-side items, a parent and child.
   1059     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1060 
   1061     MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
   1062     parent.PutIsDir(true);
   1063     parent.PutIsUnsynced(true);
   1064 
   1065     MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child");
   1066     child.PutIsUnsynced(true);
   1067 
   1068     orig_parent_id = parent.GetId();
   1069     orig_child_id = child.GetId();
   1070   }
   1071 
   1072   {
   1073     // Delete the child.
   1074     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1075 
   1076     MutableEntry child(&trans, GET_BY_ID, orig_child_id);
   1077     child.PutIsDel(true);
   1078   }
   1079 
   1080   {
   1081     // Simulate what happens after committing the parent.  Its ID will be
   1082     // replaced with server a ID.
   1083     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1084 
   1085     MutableEntry parent(&trans, GET_BY_ID, orig_parent_id);
   1086 
   1087     ChangeEntryIDAndUpdateChildren(&trans, &parent, id_factory.NewServerId());
   1088     parent.PutIsUnsynced(false);
   1089     parent.PutBaseVersion(1);
   1090     parent.PutServerVersion(1);
   1091   }
   1092 
   1093   // Final check for validity.
   1094   EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
   1095 }
   1096 
   1097 // Ask the directory to generate a unique ID.  Close and re-open the database
   1098 // without saving, then ask for another unique ID.  Verify IDs are not reused.
   1099 // This scenario simulates a crash within the first few seconds of operation.
   1100 TEST_F(SyncableDirectoryTest, LocalIdReuseTest) {
   1101   Id pre_crash_id = dir()->NextId();
   1102   SimulateCrashAndReloadDir();
   1103   Id post_crash_id = dir()->NextId();
   1104   EXPECT_NE(pre_crash_id, post_crash_id);
   1105 }
   1106 
   1107 // Ask the directory to generate a unique ID.  Save the directory.  Close and
   1108 // re-open the database without saving, then ask for another unique ID.  Verify
   1109 // IDs are not reused.  This scenario simulates a steady-state crash.
   1110 TEST_F(SyncableDirectoryTest, LocalIdReuseTestWithSave) {
   1111   Id pre_crash_id = dir()->NextId();
   1112   dir()->SaveChanges();
   1113   SimulateCrashAndReloadDir();
   1114   Id post_crash_id = dir()->NextId();
   1115   EXPECT_NE(pre_crash_id, post_crash_id);
   1116 }
   1117 
   1118 // Ensure that the unsynced, is_del and server unkown entries that may have been
   1119 // left in the database by old clients will be deleted when we open the old
   1120 // database.
   1121 TEST_F(SyncableDirectoryTest, OldClientLeftUnsyncedDeletedLocalItem) {
   1122   // We must create an entry with the offending properties.  This is done with
   1123   // some abuse of the MutableEntry's API; it doesn't expect us to modify an
   1124   // item after it is deleted.  If this hack becomes impractical we will need to
   1125   // find a new way to simulate this scenario.
   1126 
   1127   TestIdFactory id_factory;
   1128 
   1129   // Happy-path: These valid entries should not get deleted.
   1130   Id server_knows_id = id_factory.NewServerId();
   1131   Id not_is_del_id = id_factory.NewLocalId();
   1132 
   1133   // The ID of the entry which will be unsynced, is_del and !ServerKnows().
   1134   Id zombie_id = id_factory.NewLocalId();
   1135 
   1136   // We're about to do some bad things.  Tell the directory verification
   1137   // routines to look the other way.
   1138   dir()->SetInvariantCheckLevel(OFF);
   1139 
   1140   {
   1141     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1142 
   1143     // Create an uncommitted tombstone entry.
   1144     MutableEntry server_knows(
   1145         &trans, CREATE, BOOKMARKS, id_factory.root(), "server_knows");
   1146     server_knows.PutId(server_knows_id);
   1147     server_knows.PutIsUnsynced(true);
   1148     server_knows.PutIsDel(true);
   1149     server_knows.PutBaseVersion(5);
   1150     server_knows.PutServerVersion(4);
   1151 
   1152     // Create a valid update entry.
   1153     MutableEntry not_is_del(
   1154         &trans, CREATE, BOOKMARKS, id_factory.root(), "not_is_del");
   1155     not_is_del.PutId(not_is_del_id);
   1156     not_is_del.PutIsDel(false);
   1157     not_is_del.PutIsUnsynced(true);
   1158 
   1159     // Create a tombstone which should never be sent to the server because the
   1160     // server never knew about the item's existence.
   1161     //
   1162     // New clients should never put entries into this state.  We work around
   1163     // this by setting IS_DEL before setting IS_UNSYNCED, something which the
   1164     // client should never do in practice.
   1165     MutableEntry zombie(&trans, CREATE, BOOKMARKS, id_factory.root(), "zombie");
   1166     zombie.PutId(zombie_id);
   1167     zombie.PutIsDel(true);
   1168     zombie.PutIsUnsynced(true);
   1169   }
   1170 
   1171   ASSERT_EQ(OPENED, SimulateSaveAndReloadDir());
   1172 
   1173   {
   1174     ReadTransaction trans(FROM_HERE, dir().get());
   1175 
   1176     // The directory loading routines should have cleaned things up, making it
   1177     // safe to check invariants once again.
   1178     dir()->FullyCheckTreeInvariants(&trans);
   1179 
   1180     Entry server_knows(&trans, GET_BY_ID, server_knows_id);
   1181     EXPECT_TRUE(server_knows.good());
   1182 
   1183     Entry not_is_del(&trans, GET_BY_ID, not_is_del_id);
   1184     EXPECT_TRUE(not_is_del.good());
   1185 
   1186     Entry zombie(&trans, GET_BY_ID, zombie_id);
   1187     EXPECT_FALSE(zombie.good());
   1188   }
   1189 }
   1190 
   1191 TEST_F(SyncableDirectoryTest, PositionWithNullSurvivesSaveAndReload) {
   1192   TestIdFactory id_factory;
   1193   Id null_child_id;
   1194   const char null_cstr[] = "\0null\0test";
   1195   std::string null_str(null_cstr, arraysize(null_cstr) - 1);
   1196   // Pad up to the minimum length with 0x7f characters, then add a string that
   1197   // contains a few NULLs to the end.  This is slightly wrong, since the suffix
   1198   // part of a UniquePosition shouldn't contain NULLs, but it's good enough for
   1199   // this test.
   1200   std::string suffix =
   1201       std::string(UniquePosition::kSuffixLength - null_str.length(), '\x7f') +
   1202       null_str;
   1203   UniquePosition null_pos = UniquePosition::FromInt64(10, suffix);
   1204 
   1205   {
   1206     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1207 
   1208     MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
   1209     parent.PutIsDir(true);
   1210     parent.PutIsUnsynced(true);
   1211 
   1212     MutableEntry child(&trans, CREATE, BOOKMARKS, parent.GetId(), "child");
   1213     child.PutIsUnsynced(true);
   1214     child.PutUniquePosition(null_pos);
   1215     child.PutServerUniquePosition(null_pos);
   1216 
   1217     null_child_id = child.GetId();
   1218   }
   1219 
   1220   EXPECT_EQ(OPENED, SimulateSaveAndReloadDir());
   1221 
   1222   {
   1223     ReadTransaction trans(FROM_HERE, dir().get());
   1224 
   1225     Entry null_ordinal_child(&trans, GET_BY_ID, null_child_id);
   1226     EXPECT_TRUE(null_pos.Equals(null_ordinal_child.GetUniquePosition()));
   1227     EXPECT_TRUE(null_pos.Equals(null_ordinal_child.GetServerUniquePosition()));
   1228   }
   1229 }
   1230 
   1231 // Any item with BOOKMARKS in their local specifics should have a valid local
   1232 // unique position.  If there is an item in the loaded DB that does not match
   1233 // this criteria, we consider the whole DB to be corrupt.
   1234 TEST_F(SyncableDirectoryTest, BadPositionCountsAsCorruption) {
   1235   TestIdFactory id_factory;
   1236 
   1237   {
   1238     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1239 
   1240     MutableEntry parent(&trans, CREATE, BOOKMARKS, id_factory.root(), "parent");
   1241     parent.PutIsDir(true);
   1242     parent.PutIsUnsynced(true);
   1243 
   1244     // The code is littered with DCHECKs that try to stop us from doing what
   1245     // we're about to do.  Our work-around is to create a bookmark based on
   1246     // a server update, then update its local specifics without updating its
   1247     // local unique position.
   1248 
   1249     MutableEntry child(
   1250         &trans, CREATE_NEW_UPDATE_ITEM, id_factory.MakeServer("child"));
   1251     sync_pb::EntitySpecifics specifics;
   1252     AddDefaultFieldValue(BOOKMARKS, &specifics);
   1253     child.PutIsUnappliedUpdate(true);
   1254     child.PutSpecifics(specifics);
   1255 
   1256     EXPECT_TRUE(child.ShouldMaintainPosition());
   1257     EXPECT_TRUE(!child.GetUniquePosition().IsValid());
   1258   }
   1259 
   1260   EXPECT_EQ(FAILED_DATABASE_CORRUPT, SimulateSaveAndReloadDir());
   1261 }
   1262 
   1263 TEST_F(SyncableDirectoryTest, General) {
   1264   int64 written_metahandle;
   1265   const Id id = TestIdFactory::FromNumber(99);
   1266   std::string name = "Jeff";
   1267   // Test simple read operations on an empty DB.
   1268   {
   1269     ReadTransaction rtrans(FROM_HERE, dir().get());
   1270     Entry e(&rtrans, GET_BY_ID, id);
   1271     ASSERT_FALSE(e.good());  // Hasn't been written yet.
   1272 
   1273     Directory::Metahandles child_handles;
   1274     dir()->GetChildHandlesById(&rtrans, rtrans.root_id(), &child_handles);
   1275     EXPECT_TRUE(child_handles.empty());
   1276   }
   1277 
   1278   // Test creating a new meta entry.
   1279   {
   1280     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get());
   1281     MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
   1282     ASSERT_TRUE(me.good());
   1283     me.PutId(id);
   1284     me.PutBaseVersion(1);
   1285     written_metahandle = me.GetMetahandle();
   1286   }
   1287 
   1288   // Test GetChildHandles* after something is now in the DB.
   1289   // Also check that GET_BY_ID works.
   1290   {
   1291     ReadTransaction rtrans(FROM_HERE, dir().get());
   1292     Entry e(&rtrans, GET_BY_ID, id);
   1293     ASSERT_TRUE(e.good());
   1294 
   1295     Directory::Metahandles child_handles;
   1296     dir()->GetChildHandlesById(&rtrans, rtrans.root_id(), &child_handles);
   1297     EXPECT_EQ(1u, child_handles.size());
   1298 
   1299     for (Directory::Metahandles::iterator i = child_handles.begin();
   1300          i != child_handles.end(); ++i) {
   1301       EXPECT_EQ(*i, written_metahandle);
   1302     }
   1303   }
   1304 
   1305   // Test writing data to an entity. Also check that GET_BY_HANDLE works.
   1306   static const char s[] = "Hello World.";
   1307   {
   1308     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1309     MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
   1310     ASSERT_TRUE(e.good());
   1311     PutDataAsBookmarkFavicon(&trans, &e, s, sizeof(s));
   1312   }
   1313 
   1314   // Test reading back the contents that we just wrote.
   1315   {
   1316     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1317     MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
   1318     ASSERT_TRUE(e.good());
   1319     ExpectDataFromBookmarkFaviconEquals(&trans, &e, s, sizeof(s));
   1320   }
   1321 
   1322   // Verify it exists in the folder.
   1323   {
   1324     ReadTransaction rtrans(FROM_HERE, dir().get());
   1325     EXPECT_EQ(1, CountEntriesWithName(&rtrans, rtrans.root_id(), name));
   1326   }
   1327 
   1328   // Now delete it.
   1329   {
   1330     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1331     MutableEntry e(&trans, GET_BY_HANDLE, written_metahandle);
   1332     e.PutIsDel(true);
   1333 
   1334     EXPECT_EQ(0, CountEntriesWithName(&trans, trans.root_id(), name));
   1335   }
   1336 
   1337   dir()->SaveChanges();
   1338 }
   1339 
   1340 TEST_F(SyncableDirectoryTest, ChildrenOps) {
   1341   int64 written_metahandle;
   1342   const Id id = TestIdFactory::FromNumber(99);
   1343   std::string name = "Jeff";
   1344   {
   1345     ReadTransaction rtrans(FROM_HERE, dir().get());
   1346     Entry e(&rtrans, GET_BY_ID, id);
   1347     ASSERT_FALSE(e.good());  // Hasn't been written yet.
   1348 
   1349     Entry root(&rtrans, GET_BY_ID, rtrans.root_id());
   1350     ASSERT_TRUE(root.good());
   1351     EXPECT_FALSE(dir()->HasChildren(&rtrans, rtrans.root_id()));
   1352     EXPECT_TRUE(root.GetFirstChildId().IsRoot());
   1353   }
   1354 
   1355   {
   1356     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get());
   1357     MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
   1358     ASSERT_TRUE(me.good());
   1359     me.PutId(id);
   1360     me.PutBaseVersion(1);
   1361     written_metahandle = me.GetMetahandle();
   1362   }
   1363 
   1364   // Test children ops after something is now in the DB.
   1365   {
   1366     ReadTransaction rtrans(FROM_HERE, dir().get());
   1367     Entry e(&rtrans, GET_BY_ID, id);
   1368     ASSERT_TRUE(e.good());
   1369 
   1370     Entry child(&rtrans, GET_BY_HANDLE, written_metahandle);
   1371     ASSERT_TRUE(child.good());
   1372 
   1373     Entry root(&rtrans, GET_BY_ID, rtrans.root_id());
   1374     ASSERT_TRUE(root.good());
   1375     EXPECT_TRUE(dir()->HasChildren(&rtrans, rtrans.root_id()));
   1376     EXPECT_EQ(e.GetId(), root.GetFirstChildId());
   1377   }
   1378 
   1379   {
   1380     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get());
   1381     MutableEntry me(&wtrans, GET_BY_HANDLE, written_metahandle);
   1382     ASSERT_TRUE(me.good());
   1383     me.PutIsDel(true);
   1384   }
   1385 
   1386   // Test children ops after the children have been deleted.
   1387   {
   1388     ReadTransaction rtrans(FROM_HERE, dir().get());
   1389     Entry e(&rtrans, GET_BY_ID, id);
   1390     ASSERT_TRUE(e.good());
   1391 
   1392     Entry root(&rtrans, GET_BY_ID, rtrans.root_id());
   1393     ASSERT_TRUE(root.good());
   1394     EXPECT_FALSE(dir()->HasChildren(&rtrans, rtrans.root_id()));
   1395     EXPECT_TRUE(root.GetFirstChildId().IsRoot());
   1396   }
   1397 
   1398   dir()->SaveChanges();
   1399 }
   1400 
   1401 TEST_F(SyncableDirectoryTest, ClientIndexRebuildsProperly) {
   1402   int64 written_metahandle;
   1403   TestIdFactory factory;
   1404   const Id id = factory.NewServerId();
   1405   std::string name = "cheesepuffs";
   1406   std::string tag = "dietcoke";
   1407 
   1408   // Test creating a new meta entry.
   1409   {
   1410     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get());
   1411     MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), name);
   1412     ASSERT_TRUE(me.good());
   1413     me.PutId(id);
   1414     me.PutBaseVersion(1);
   1415     me.PutUniqueClientTag(tag);
   1416     written_metahandle = me.GetMetahandle();
   1417   }
   1418   dir()->SaveChanges();
   1419 
   1420   // Close and reopen, causing index regeneration.
   1421   ReopenDirectory();
   1422   {
   1423     ReadTransaction trans(FROM_HERE, dir().get());
   1424     Entry me(&trans, GET_BY_CLIENT_TAG, tag);
   1425     ASSERT_TRUE(me.good());
   1426     EXPECT_EQ(me.GetId(), id);
   1427     EXPECT_EQ(me.GetBaseVersion(), 1);
   1428     EXPECT_EQ(me.GetUniqueClientTag(), tag);
   1429     EXPECT_EQ(me.GetMetahandle(), written_metahandle);
   1430   }
   1431 }
   1432 
   1433 TEST_F(SyncableDirectoryTest, ClientIndexRebuildsDeletedProperly) {
   1434   TestIdFactory factory;
   1435   const Id id = factory.NewServerId();
   1436   std::string tag = "dietcoke";
   1437 
   1438   // Test creating a deleted, unsynced, server meta entry.
   1439   {
   1440     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get());
   1441     MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "deleted");
   1442     ASSERT_TRUE(me.good());
   1443     me.PutId(id);
   1444     me.PutBaseVersion(1);
   1445     me.PutUniqueClientTag(tag);
   1446     me.PutIsDel(true);
   1447     me.PutIsUnsynced(true);  // Or it might be purged.
   1448   }
   1449   dir()->SaveChanges();
   1450 
   1451   // Close and reopen, causing index regeneration.
   1452   ReopenDirectory();
   1453   {
   1454     ReadTransaction trans(FROM_HERE, dir().get());
   1455     Entry me(&trans, GET_BY_CLIENT_TAG, tag);
   1456     // Should still be present and valid in the client tag index.
   1457     ASSERT_TRUE(me.good());
   1458     EXPECT_EQ(me.GetId(), id);
   1459     EXPECT_EQ(me.GetUniqueClientTag(), tag);
   1460     EXPECT_TRUE(me.GetIsDel());
   1461     EXPECT_TRUE(me.GetIsUnsynced());
   1462   }
   1463 }
   1464 
   1465 TEST_F(SyncableDirectoryTest, ToValue) {
   1466   const Id id = TestIdFactory::FromNumber(99);
   1467   {
   1468     ReadTransaction rtrans(FROM_HERE, dir().get());
   1469     Entry e(&rtrans, GET_BY_ID, id);
   1470     EXPECT_FALSE(e.good());  // Hasn't been written yet.
   1471 
   1472     scoped_ptr<base::DictionaryValue> value(e.ToValue(NULL));
   1473     ExpectDictBooleanValue(false, *value, "good");
   1474     EXPECT_EQ(1u, value->size());
   1475   }
   1476 
   1477   // Test creating a new meta entry.
   1478   {
   1479     WriteTransaction wtrans(FROM_HERE, UNITTEST, dir().get());
   1480     MutableEntry me(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "new");
   1481     ASSERT_TRUE(me.good());
   1482     me.PutId(id);
   1483     me.PutBaseVersion(1);
   1484 
   1485     scoped_ptr<base::DictionaryValue> value(me.ToValue(NULL));
   1486     ExpectDictBooleanValue(true, *value, "good");
   1487     EXPECT_TRUE(value->HasKey("kernel"));
   1488     ExpectDictStringValue("Bookmarks", *value, "modelType");
   1489     ExpectDictBooleanValue(true, *value, "existsOnClientBecauseNameIsNonEmpty");
   1490     ExpectDictBooleanValue(false, *value, "isRoot");
   1491   }
   1492 
   1493   dir()->SaveChanges();
   1494 }
   1495 
   1496 // Test that the bookmark tag generation algorithm remains unchanged.
   1497 TEST_F(SyncableDirectoryTest, BookmarkTagTest) {
   1498   // This test needs its own InMemoryDirectoryBackingStore because it needs to
   1499   // call request_consistent_cache_guid().
   1500   InMemoryDirectoryBackingStore* store = new InMemoryDirectoryBackingStore("x");
   1501 
   1502   // The two inputs that form the bookmark tag are the directory's cache_guid
   1503   // and its next_id value.  We don't need to take any action to ensure
   1504   // consistent next_id values, but we do need to explicitly request that our
   1505   // InMemoryDirectoryBackingStore always return the same cache_guid.
   1506   store->request_consistent_cache_guid();
   1507 
   1508   Directory dir(store, unrecoverable_error_handler(), NULL, NULL, NULL);
   1509   ASSERT_EQ(
   1510       OPENED,
   1511       dir.Open("x", directory_change_delegate(), NullTransactionObserver()));
   1512 
   1513   {
   1514     WriteTransaction wtrans(FROM_HERE, UNITTEST, &dir);
   1515     MutableEntry bm(&wtrans, CREATE, BOOKMARKS, wtrans.root_id(), "bm");
   1516     bm.PutIsUnsynced(true);
   1517 
   1518     // If this assertion fails, that might indicate that the algorithm used to
   1519     // generate bookmark tags has been modified.  This could have implications
   1520     // for bookmark ordering.  Please make sure you know what you're doing if
   1521     // you intend to make such a change.
   1522     ASSERT_EQ("6wHRAb3kbnXV5GHrejp4/c1y5tw=", bm.GetUniqueBookmarkTag());
   1523   }
   1524 }
   1525 
   1526 // A thread that creates a bunch of directory entries.
   1527 class StressTransactionsDelegate : public base::PlatformThread::Delegate {
   1528  public:
   1529   StressTransactionsDelegate(Directory* dir, int thread_number)
   1530       : dir_(dir), thread_number_(thread_number) {}
   1531 
   1532  private:
   1533   Directory* const dir_;
   1534   const int thread_number_;
   1535 
   1536   // PlatformThread::Delegate methods:
   1537   virtual void ThreadMain() OVERRIDE {
   1538     int entry_count = 0;
   1539     std::string path_name;
   1540 
   1541     for (int i = 0; i < 20; ++i) {
   1542       const int rand_action = rand() % 10;
   1543       if (rand_action < 4 && !path_name.empty()) {
   1544         ReadTransaction trans(FROM_HERE, dir_);
   1545         CHECK(1 == CountEntriesWithName(&trans, trans.root_id(), path_name));
   1546         base::PlatformThread::Sleep(
   1547             base::TimeDelta::FromMilliseconds(rand() % 10));
   1548       } else {
   1549         std::string unique_name =
   1550             base::StringPrintf("%d.%d", thread_number_, entry_count++);
   1551         path_name.assign(unique_name.begin(), unique_name.end());
   1552         WriteTransaction trans(FROM_HERE, UNITTEST, dir_);
   1553         MutableEntry e(&trans, CREATE, BOOKMARKS, trans.root_id(), path_name);
   1554         CHECK(e.good());
   1555         base::PlatformThread::Sleep(
   1556             base::TimeDelta::FromMilliseconds(rand() % 20));
   1557         e.PutIsUnsynced(true);
   1558         if (e.PutId(TestIdFactory::FromNumber(rand())) &&
   1559             e.GetId().ServerKnows() && !e.GetId().IsRoot()) {
   1560           e.PutBaseVersion(1);
   1561         }
   1562       }
   1563     }
   1564   }
   1565 
   1566   DISALLOW_COPY_AND_ASSIGN(StressTransactionsDelegate);
   1567 };
   1568 
   1569 // Stress test Directory by accessing it from several threads concurrently.
   1570 TEST_F(SyncableDirectoryTest, StressTransactions) {
   1571   const int kThreadCount = 7;
   1572   base::PlatformThreadHandle threads[kThreadCount];
   1573   scoped_ptr<StressTransactionsDelegate> thread_delegates[kThreadCount];
   1574 
   1575   for (int i = 0; i < kThreadCount; ++i) {
   1576     thread_delegates[i].reset(new StressTransactionsDelegate(dir().get(), i));
   1577     ASSERT_TRUE(base::PlatformThread::Create(
   1578         0, thread_delegates[i].get(), &threads[i]));
   1579   }
   1580 
   1581   for (int i = 0; i < kThreadCount; ++i) {
   1582     base::PlatformThread::Join(threads[i]);
   1583   }
   1584 }
   1585 
   1586 // Verify that Directory is notifed when a MutableEntry's AttachmentMetadata
   1587 // changes.
   1588 TEST_F(SyncableDirectoryTest, MutableEntry_PutAttachmentMetadata) {
   1589   sync_pb::AttachmentMetadata attachment_metadata;
   1590   sync_pb::AttachmentMetadataRecord* record = attachment_metadata.add_record();
   1591   sync_pb::AttachmentIdProto attachment_id_proto =
   1592       syncer::CreateAttachmentIdProto();
   1593   *record->mutable_id() = attachment_id_proto;
   1594   ASSERT_FALSE(dir()->IsAttachmentLinked(attachment_id_proto));
   1595   {
   1596     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1597 
   1598     // Create an entry with attachment metadata and see that the attachment id
   1599     // is not linked.
   1600     MutableEntry entry(
   1601         &trans, CREATE, PREFERENCES, trans.root_id(), "some entry");
   1602     entry.PutId(TestIdFactory::FromNumber(-1));
   1603     entry.PutIsUnsynced(true);
   1604 
   1605     Directory::Metahandles metahandles;
   1606     ASSERT_FALSE(dir()->IsAttachmentLinked(attachment_id_proto));
   1607     dir()->GetMetahandlesByAttachmentId(
   1608         &trans, attachment_id_proto, &metahandles);
   1609     ASSERT_TRUE(metahandles.empty());
   1610 
   1611     // Now add the attachment metadata and see that Directory believes it is
   1612     // linked.
   1613     entry.PutAttachmentMetadata(attachment_metadata);
   1614     ASSERT_TRUE(dir()->IsAttachmentLinked(attachment_id_proto));
   1615     dir()->GetMetahandlesByAttachmentId(
   1616         &trans, attachment_id_proto, &metahandles);
   1617     ASSERT_FALSE(metahandles.empty());
   1618     ASSERT_EQ(metahandles[0], entry.GetMetahandle());
   1619 
   1620     // Clear out the attachment metadata and see that it's no longer linked.
   1621     sync_pb::AttachmentMetadata empty_attachment_metadata;
   1622     entry.PutAttachmentMetadata(empty_attachment_metadata);
   1623     ASSERT_FALSE(dir()->IsAttachmentLinked(attachment_id_proto));
   1624     dir()->GetMetahandlesByAttachmentId(
   1625         &trans, attachment_id_proto, &metahandles);
   1626     ASSERT_TRUE(metahandles.empty());
   1627   }
   1628   ASSERT_FALSE(dir()->IsAttachmentLinked(attachment_id_proto));
   1629 }
   1630 
   1631 // Verify that UpdateAttachmentId updates attachment_id and is_on_server flag.
   1632 TEST_F(SyncableDirectoryTest, MutableEntry_UpdateAttachmentId) {
   1633   sync_pb::AttachmentMetadata attachment_metadata;
   1634   sync_pb::AttachmentMetadataRecord* r1 = attachment_metadata.add_record();
   1635   sync_pb::AttachmentMetadataRecord* r2 = attachment_metadata.add_record();
   1636   *r1->mutable_id() = syncer::CreateAttachmentIdProto();
   1637   *r2->mutable_id() = syncer::CreateAttachmentIdProto();
   1638   sync_pb::AttachmentIdProto attachment_id_proto = r1->id();
   1639 
   1640   WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1641 
   1642   MutableEntry entry(
   1643       &trans, CREATE, PREFERENCES, trans.root_id(), "some entry");
   1644   entry.PutId(TestIdFactory::FromNumber(-1));
   1645   entry.PutAttachmentMetadata(attachment_metadata);
   1646 
   1647   const sync_pb::AttachmentMetadata& entry_metadata =
   1648       entry.GetAttachmentMetadata();
   1649   ASSERT_EQ(2, entry_metadata.record_size());
   1650   ASSERT_FALSE(entry_metadata.record(0).is_on_server());
   1651   ASSERT_FALSE(entry_metadata.record(1).is_on_server());
   1652   ASSERT_FALSE(entry.GetIsUnsynced());
   1653 
   1654   entry.MarkAttachmentAsOnServer(attachment_id_proto);
   1655 
   1656   ASSERT_TRUE(entry_metadata.record(0).is_on_server());
   1657   ASSERT_FALSE(entry_metadata.record(1).is_on_server());
   1658   ASSERT_TRUE(entry.GetIsUnsynced());
   1659 }
   1660 
   1661 // Verify that deleted entries with attachments will retain the attachments.
   1662 TEST_F(SyncableDirectoryTest, Directory_DeleteDoesNotUnlinkAttachments) {
   1663   sync_pb::AttachmentMetadata attachment_metadata;
   1664   sync_pb::AttachmentMetadataRecord* record = attachment_metadata.add_record();
   1665   sync_pb::AttachmentIdProto attachment_id_proto =
   1666       syncer::CreateAttachmentIdProto();
   1667   *record->mutable_id() = attachment_id_proto;
   1668   ASSERT_FALSE(dir()->IsAttachmentLinked(attachment_id_proto));
   1669   const Id id = TestIdFactory::FromNumber(-1);
   1670 
   1671   // Create an entry with attachment metadata and see that the attachment id
   1672   // is linked.
   1673   CreateEntryWithAttachmentMetadata(
   1674       PREFERENCES, "some entry", id, attachment_metadata);
   1675   ASSERT_TRUE(dir()->IsAttachmentLinked(attachment_id_proto));
   1676 
   1677   // Delete the entry and see that it's still linked because the entry hasn't
   1678   // yet been purged.
   1679   DeleteEntry(id);
   1680   ASSERT_TRUE(dir()->IsAttachmentLinked(attachment_id_proto));
   1681 
   1682   // Reload the Directory, purging the deleted entry, and see that the
   1683   // attachment is no longer linked.
   1684   SimulateSaveAndReloadDir();
   1685   ASSERT_FALSE(dir()->IsAttachmentLinked(attachment_id_proto));
   1686 }
   1687 
   1688 // Verify that a given attachment can be referenced by multiple entries and that
   1689 // any one of the references is sufficient to ensure it remains linked.
   1690 TEST_F(SyncableDirectoryTest, Directory_LastReferenceUnlinksAttachments) {
   1691   // Create one attachment.
   1692   sync_pb::AttachmentMetadata attachment_metadata;
   1693   sync_pb::AttachmentMetadataRecord* record = attachment_metadata.add_record();
   1694   sync_pb::AttachmentIdProto attachment_id_proto =
   1695       syncer::CreateAttachmentIdProto();
   1696   *record->mutable_id() = attachment_id_proto;
   1697 
   1698   // Create two entries, each referencing the attachment.
   1699   const Id id1 = TestIdFactory::FromNumber(-1);
   1700   const Id id2 = TestIdFactory::FromNumber(-2);
   1701   CreateEntryWithAttachmentMetadata(
   1702       PREFERENCES, "some entry", id1, attachment_metadata);
   1703   CreateEntryWithAttachmentMetadata(
   1704       PREFERENCES, "some other entry", id2, attachment_metadata);
   1705 
   1706   // See that the attachment is considered linked.
   1707   ASSERT_TRUE(dir()->IsAttachmentLinked(attachment_id_proto));
   1708 
   1709   // Delete the first entry, reload the Directory, see that the attachment is
   1710   // still linked.
   1711   DeleteEntry(id1);
   1712   SimulateSaveAndReloadDir();
   1713   ASSERT_TRUE(dir()->IsAttachmentLinked(attachment_id_proto));
   1714 
   1715   // Delete the second entry, reload the Directory, see that the attachment is
   1716   // no loner linked.
   1717   DeleteEntry(id2);
   1718   SimulateSaveAndReloadDir();
   1719   ASSERT_FALSE(dir()->IsAttachmentLinked(attachment_id_proto));
   1720 }
   1721 
   1722 TEST_F(SyncableDirectoryTest, Directory_GetAttachmentIdsToUpload) {
   1723   // Create one attachment, referenced by two entries.
   1724   AttachmentId attachment_id = AttachmentId::Create();
   1725   sync_pb::AttachmentIdProto attachment_id_proto = attachment_id.GetProto();
   1726   sync_pb::AttachmentMetadata attachment_metadata;
   1727   sync_pb::AttachmentMetadataRecord* record = attachment_metadata.add_record();
   1728   *record->mutable_id() = attachment_id_proto;
   1729   const Id id1 = TestIdFactory::FromNumber(-1);
   1730   const Id id2 = TestIdFactory::FromNumber(-2);
   1731   CreateEntryWithAttachmentMetadata(
   1732       PREFERENCES, "some entry", id1, attachment_metadata);
   1733   CreateEntryWithAttachmentMetadata(
   1734       PREFERENCES, "some other entry", id2, attachment_metadata);
   1735 
   1736   // See that Directory reports that this attachment is not on the server.
   1737   AttachmentIdSet id_set;
   1738   {
   1739     ReadTransaction trans(FROM_HERE, dir().get());
   1740     dir()->GetAttachmentIdsToUpload(&trans, PREFERENCES, &id_set);
   1741   }
   1742   ASSERT_EQ(1U, id_set.size());
   1743   ASSERT_EQ(attachment_id, *id_set.begin());
   1744 
   1745   // Call again, but this time with a ModelType for which there are no entries.
   1746   // See that Directory correctly reports that there are none.
   1747   {
   1748     ReadTransaction trans(FROM_HERE, dir().get());
   1749     dir()->GetAttachmentIdsToUpload(&trans, PASSWORDS, &id_set);
   1750   }
   1751   ASSERT_TRUE(id_set.empty());
   1752 
   1753   // Now, mark the attachment as "on the server" via entry_1.
   1754   {
   1755     WriteTransaction trans(FROM_HERE, UNITTEST, dir().get());
   1756     MutableEntry entry_1(&trans, GET_BY_ID, id1);
   1757     entry_1.MarkAttachmentAsOnServer(attachment_id_proto);
   1758   }
   1759 
   1760   // See that Directory no longer reports that this attachment is not on the
   1761   // server.
   1762   {
   1763     ReadTransaction trans(FROM_HERE, dir().get());
   1764     dir()->GetAttachmentIdsToUpload(&trans, PREFERENCES, &id_set);
   1765   }
   1766   ASSERT_TRUE(id_set.empty());
   1767 }
   1768 
   1769 }  // namespace syncable
   1770 
   1771 }  // namespace syncer
   1772