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