Home | History | Annotate | Download | only in engine
      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/engine/directory_commit_contribution.h"
      6 
      7 #include "base/message_loop/message_loop.h"
      8 #include "sync/sessions/status_controller.h"
      9 #include "sync/syncable/entry.h"
     10 #include "sync/syncable/mutable_entry.h"
     11 #include "sync/syncable/syncable_read_transaction.h"
     12 #include "sync/syncable/syncable_write_transaction.h"
     13 #include "sync/test/engine/test_directory_setter_upper.h"
     14 #include "sync/test/engine/test_id_factory.h"
     15 #include "sync/test/engine/test_syncable_utils.h"
     16 #include "testing/gtest/include/gtest/gtest.h"
     17 
     18 namespace syncer {
     19 
     20 class DirectoryCommitContributionTest : public ::testing::Test {
     21  public:
     22   virtual void SetUp() OVERRIDE {
     23     dir_maker_.SetUp();
     24 
     25     syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
     26     CreateTypeRoot(&trans, dir(), PREFERENCES);
     27     CreateTypeRoot(&trans, dir(), EXTENSIONS);
     28     CreateTypeRoot(&trans, dir(), BOOKMARKS);
     29   }
     30 
     31   virtual void TearDown() OVERRIDE {
     32     dir_maker_.TearDown();
     33   }
     34 
     35  protected:
     36   int64 CreateUnsyncedItem(syncable::WriteTransaction* trans,
     37                            ModelType type,
     38                            const std::string& tag) {
     39     syncable::Entry parent_entry(trans, syncable::GET_TYPE_ROOT, type);
     40     syncable::MutableEntry entry(
     41         trans,
     42         syncable::CREATE,
     43         type,
     44         parent_entry.GetId(),
     45         tag);
     46     entry.PutIsUnsynced(true);
     47     return entry.GetMetahandle();
     48   }
     49 
     50   int64 CreateSyncedItem(syncable::WriteTransaction* trans,
     51                          ModelType type,
     52                          const std::string& tag) {
     53     syncable::Entry parent_entry(trans, syncable::GET_TYPE_ROOT, type);
     54     syncable::MutableEntry entry(
     55         trans,
     56         syncable::CREATE,
     57         type,
     58         parent_entry.GetId(),
     59         tag);
     60 
     61     entry.PutId(syncable::Id::CreateFromServerId(
     62         id_factory_.NewServerId().GetServerId()));
     63     entry.PutBaseVersion(10);
     64     entry.PutServerVersion(10);
     65     entry.PutIsUnappliedUpdate(false);
     66     entry.PutIsUnsynced(false);
     67     entry.PutIsDel(false);
     68     entry.PutServerIsDel(false);
     69 
     70     return entry.GetMetahandle();
     71   }
     72 
     73   void CreateSuccessfulCommitResponse(
     74       const sync_pb::SyncEntity& entity,
     75       sync_pb::CommitResponse::EntryResponse* response) {
     76     response->set_response_type(sync_pb::CommitResponse::SUCCESS);
     77     response->set_non_unique_name(entity.name());
     78     response->set_version(entity.version() + 1);
     79     response->set_parent_id_string(entity.parent_id_string());
     80 
     81     if (entity.id_string()[0] == '-')  // Look for the - in 'c-1234' style IDs.
     82       response->set_id_string(id_factory_.NewServerId().GetServerId());
     83     else
     84       response->set_id_string(entity.id_string());
     85   }
     86 
     87   syncable::Directory* dir() {
     88     return dir_maker_.directory();
     89   }
     90 
     91   TestIdFactory id_factory_;
     92 
     93   // Used in construction of DirectoryTypeDebugInfoEmitters.
     94   ObserverList<TypeDebugInfoObserver> type_observers_;
     95 
     96  private:
     97   base::MessageLoop loop_;  // Neeed to initialize the directory.
     98   TestDirectorySetterUpper dir_maker_;
     99 };
    100 
    101 // Verify that the DirectoryCommitContribution contains only entries of its
    102 // specified type.
    103 TEST_F(DirectoryCommitContributionTest, GatherByTypes) {
    104   int64 pref1;
    105   {
    106     syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
    107     pref1 = CreateUnsyncedItem(&trans, PREFERENCES, "pref1");
    108     CreateUnsyncedItem(&trans, PREFERENCES, "pref2");
    109     CreateUnsyncedItem(&trans, EXTENSIONS, "extension1");
    110   }
    111 
    112   DirectoryTypeDebugInfoEmitter emitter(PREFERENCES, &type_observers_);
    113   scoped_ptr<DirectoryCommitContribution> cc(
    114       DirectoryCommitContribution::Build(dir(), PREFERENCES, 5, &emitter));
    115   ASSERT_EQ(2U, cc->GetNumEntries());
    116 
    117   const std::vector<int64>& metahandles = cc->metahandles_;
    118   EXPECT_TRUE(std::find(metahandles.begin(), metahandles.end(), pref1) !=
    119               metahandles.end());
    120   EXPECT_TRUE(std::find(metahandles.begin(), metahandles.end(), pref1) !=
    121               metahandles.end());
    122 
    123   cc->CleanUp();
    124 }
    125 
    126 // Verify that the DirectoryCommitContributionTest builder function
    127 // truncates if necessary.
    128 TEST_F(DirectoryCommitContributionTest, GatherAndTruncate) {
    129   int64 pref1;
    130   int64 pref2;
    131   {
    132     syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
    133     pref1 = CreateUnsyncedItem(&trans, PREFERENCES, "pref1");
    134     pref2 = CreateUnsyncedItem(&trans, PREFERENCES, "pref2");
    135     CreateUnsyncedItem(&trans, EXTENSIONS, "extension1");
    136   }
    137 
    138   DirectoryTypeDebugInfoEmitter emitter(PREFERENCES, &type_observers_);
    139   scoped_ptr<DirectoryCommitContribution> cc(
    140       DirectoryCommitContribution::Build(dir(), PREFERENCES, 1, &emitter));
    141   ASSERT_EQ(1U, cc->GetNumEntries());
    142 
    143   int64 only_metahandle = cc->metahandles_[0];
    144   EXPECT_TRUE(only_metahandle == pref1 || only_metahandle == pref2);
    145 
    146   cc->CleanUp();
    147 }
    148 
    149 // Sanity check for building commits from DirectoryCommitContributions.
    150 // This test makes two CommitContribution objects of different types and uses
    151 // them to initialize a commit message.  Then it checks that the contents of the
    152 // commit message match those of the directory they came from.
    153 TEST_F(DirectoryCommitContributionTest, PrepareCommit) {
    154   {
    155     syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
    156     CreateUnsyncedItem(&trans, PREFERENCES, "pref1");
    157     CreateUnsyncedItem(&trans, PREFERENCES, "pref2");
    158     CreateUnsyncedItem(&trans, EXTENSIONS, "extension1");
    159   }
    160 
    161   DirectoryTypeDebugInfoEmitter emitter1(PREFERENCES, &type_observers_);
    162   DirectoryTypeDebugInfoEmitter emitter2(EXTENSIONS, &type_observers_);
    163   scoped_ptr<DirectoryCommitContribution> pref_cc(
    164       DirectoryCommitContribution::Build(dir(), PREFERENCES, 25, &emitter1));
    165   scoped_ptr<DirectoryCommitContribution> ext_cc(
    166       DirectoryCommitContribution::Build(dir(), EXTENSIONS, 25, &emitter2));
    167 
    168   sync_pb::ClientToServerMessage message;
    169   pref_cc->AddToCommitMessage(&message);
    170   ext_cc->AddToCommitMessage(&message);
    171 
    172   const sync_pb::CommitMessage& commit_message = message.commit();
    173 
    174   std::set<syncable::Id> ids_for_commit;
    175   ASSERT_EQ(3, commit_message.entries_size());
    176   for (int i = 0; i < commit_message.entries_size(); ++i) {
    177     const sync_pb::SyncEntity& entity = commit_message.entries(i);
    178     // The entities in this test have client-style IDs since they've never been
    179     // committed before, so we must use CreateFromClientString to re-create them
    180     // from the commit message.
    181     ids_for_commit.insert(syncable::Id::CreateFromClientString(
    182             entity.id_string()));
    183   }
    184 
    185   ASSERT_EQ(3U, ids_for_commit.size());
    186   {
    187     syncable::ReadTransaction trans(FROM_HERE, dir());
    188     for (std::set<syncable::Id>::iterator it = ids_for_commit.begin();
    189          it != ids_for_commit.end(); ++it) {
    190       SCOPED_TRACE(it->value());
    191       syncable::Entry entry(&trans, syncable::GET_BY_ID, *it);
    192       ASSERT_TRUE(entry.good());
    193       EXPECT_TRUE(entry.GetSyncing());
    194     }
    195   }
    196 
    197   pref_cc->CleanUp();
    198   ext_cc->CleanUp();
    199 }
    200 
    201 // Check that deletion requests include a model type.
    202 // This was not always the case, but was implemented to allow us to loosen some
    203 // other restrictions in the protocol.
    204 TEST_F(DirectoryCommitContributionTest, DeletedItemsWithSpecifics) {
    205   int64 pref1;
    206   {
    207     syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
    208     pref1 = CreateSyncedItem(&trans, PREFERENCES, "pref1");
    209     syncable::MutableEntry e1(&trans, syncable::GET_BY_HANDLE, pref1);
    210     e1.PutIsDel(true);
    211     e1.PutIsUnsynced(true);
    212   }
    213 
    214   DirectoryTypeDebugInfoEmitter emitter(PREFERENCES, &type_observers_);
    215   scoped_ptr<DirectoryCommitContribution> pref_cc(
    216       DirectoryCommitContribution::Build(dir(), PREFERENCES, 25, &emitter));
    217   ASSERT_TRUE(pref_cc);
    218 
    219   sync_pb::ClientToServerMessage message;
    220   pref_cc->AddToCommitMessage(&message);
    221 
    222   const sync_pb::CommitMessage& commit_message = message.commit();
    223   ASSERT_EQ(1, commit_message.entries_size());
    224   EXPECT_TRUE(
    225       commit_message.entries(0).specifics().has_preference());
    226 
    227   pref_cc->CleanUp();
    228 }
    229 
    230 // As ususal, bookmarks are special.  Bookmark deletion is special.
    231 // Deleted bookmarks include a valid "is folder" bit and their full specifics
    232 // (especially the meta info, which is what server really wants).
    233 TEST_F(DirectoryCommitContributionTest, DeletedBookmarksWithSpecifics) {
    234   int64 bm1;
    235   {
    236     syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
    237     bm1 = CreateSyncedItem(&trans, BOOKMARKS, "bm1");
    238     syncable::MutableEntry e1(&trans, syncable::GET_BY_HANDLE, bm1);
    239 
    240     e1.PutIsDir(true);
    241     e1.PutServerIsDir(true);
    242 
    243     sync_pb::EntitySpecifics specifics;
    244     sync_pb::BookmarkSpecifics* bm_specifics = specifics.mutable_bookmark();
    245     bm_specifics->set_url("http://www.chrome.com");
    246     bm_specifics->set_title("Chrome");
    247     sync_pb::MetaInfo* meta_info = bm_specifics->add_meta_info();
    248     meta_info->set_key("K");
    249     meta_info->set_value("V");
    250     e1.PutSpecifics(specifics);
    251 
    252     e1.PutIsDel(true);
    253     e1.PutIsUnsynced(true);
    254   }
    255 
    256   DirectoryTypeDebugInfoEmitter emitter(BOOKMARKS, &type_observers_);
    257   scoped_ptr<DirectoryCommitContribution> bm_cc(
    258       DirectoryCommitContribution::Build(dir(), BOOKMARKS, 25, &emitter));
    259   ASSERT_TRUE(bm_cc);
    260 
    261   sync_pb::ClientToServerMessage message;
    262   bm_cc->AddToCommitMessage(&message);
    263 
    264   const sync_pb::CommitMessage& commit_message = message.commit();
    265   ASSERT_EQ(1, commit_message.entries_size());
    266 
    267   const sync_pb::SyncEntity& entity = commit_message.entries(0);
    268   EXPECT_TRUE(entity.has_folder());
    269   ASSERT_TRUE(entity.specifics().has_bookmark());
    270   ASSERT_EQ(1, entity.specifics().bookmark().meta_info_size());
    271   EXPECT_EQ("K", entity.specifics().bookmark().meta_info(0).key());
    272   EXPECT_EQ("V", entity.specifics().bookmark().meta_info(0).value());
    273 
    274   bm_cc->CleanUp();
    275 }
    276 
    277 // Creates some unsynced items, pretends to commit them, and hands back a
    278 // specially crafted response to the syncer in order to test commit response
    279 // processing.  The response simulates a succesful commit scenario.
    280 TEST_F(DirectoryCommitContributionTest, ProcessCommitResponse) {
    281   int64 pref1_handle;
    282   int64 pref2_handle;
    283   int64 ext1_handle;
    284   {
    285     syncable::WriteTransaction trans(FROM_HERE, syncable::UNITTEST, dir());
    286     pref1_handle = CreateUnsyncedItem(&trans, PREFERENCES, "pref1");
    287     pref2_handle = CreateUnsyncedItem(&trans, PREFERENCES, "pref2");
    288     ext1_handle = CreateUnsyncedItem(&trans, EXTENSIONS, "extension1");
    289   }
    290 
    291   DirectoryTypeDebugInfoEmitter emitter1(PREFERENCES, &type_observers_);
    292   DirectoryTypeDebugInfoEmitter emitter2(EXTENSIONS, &type_observers_);
    293   scoped_ptr<DirectoryCommitContribution> pref_cc(
    294       DirectoryCommitContribution::Build(dir(), PREFERENCES, 25, &emitter1));
    295   scoped_ptr<DirectoryCommitContribution> ext_cc(
    296       DirectoryCommitContribution::Build(dir(), EXTENSIONS, 25, &emitter2));
    297 
    298   sync_pb::ClientToServerMessage message;
    299   pref_cc->AddToCommitMessage(&message);
    300   ext_cc->AddToCommitMessage(&message);
    301 
    302   const sync_pb::CommitMessage& commit_message = message.commit();
    303   ASSERT_EQ(3, commit_message.entries_size());
    304 
    305   sync_pb::ClientToServerResponse response;
    306   for (int i = 0; i < commit_message.entries_size(); ++i) {
    307     sync_pb::SyncEntity entity = commit_message.entries(i);
    308     sync_pb::CommitResponse_EntryResponse* entry_response =
    309         response.mutable_commit()->add_entryresponse();
    310     CreateSuccessfulCommitResponse(entity, entry_response);
    311   }
    312 
    313   sessions::StatusController status;
    314 
    315   // Process these in reverse order.  Just because we can.
    316   ext_cc->ProcessCommitResponse(response, &status);
    317   pref_cc->ProcessCommitResponse(response, &status);
    318 
    319   {
    320     syncable::ReadTransaction trans(FROM_HERE, dir());
    321     syncable::Entry p1(&trans, syncable::GET_BY_HANDLE, pref1_handle);
    322     EXPECT_TRUE(p1.GetId().ServerKnows());
    323     EXPECT_FALSE(p1.GetSyncing());
    324     EXPECT_LT(0, p1.GetServerVersion());
    325 
    326     syncable::Entry p2(&trans, syncable::GET_BY_HANDLE, pref2_handle);
    327     EXPECT_TRUE(p2.GetId().ServerKnows());
    328     EXPECT_FALSE(p2.GetSyncing());
    329     EXPECT_LT(0, p2.GetServerVersion());
    330 
    331     syncable::Entry e1(&trans, syncable::GET_BY_HANDLE, ext1_handle);
    332     EXPECT_TRUE(e1.GetId().ServerKnows());
    333     EXPECT_FALSE(e1.GetSyncing());
    334     EXPECT_LT(0, e1.GetServerVersion());
    335   }
    336 
    337   pref_cc->CleanUp();
    338   ext_cc->CleanUp();
    339 }
    340 
    341 }  // namespace syncer
    342