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