Home | History | Annotate | Download | only in engine
      1 // Copyright 2013 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/download.h"
      6 
      7 #include "base/message_loop/message_loop.h"
      8 #include "base/stl_util.h"
      9 #include "sync/engine/sync_directory_update_handler.h"
     10 #include "sync/internal_api/public/base/model_type_test_util.h"
     11 #include "sync/protocol/sync.pb.h"
     12 #include "sync/sessions/debug_info_getter.h"
     13 #include "sync/sessions/nudge_tracker.h"
     14 #include "sync/sessions/status_controller.h"
     15 #include "sync/syncable/directory.h"
     16 #include "sync/test/engine/fake_model_worker.h"
     17 #include "sync/test/engine/test_directory_setter_upper.h"
     18 #include "sync/test/sessions/mock_debug_info_getter.h"
     19 #include "testing/gtest/include/gtest/gtest.h"
     20 
     21 namespace syncer {
     22 
     23 using sessions::MockDebugInfoGetter;
     24 
     25 // A test fixture for tests exercising download updates functions.
     26 class DownloadUpdatesTest : public ::testing::Test {
     27  protected:
     28   DownloadUpdatesTest()
     29       : update_handler_map_deleter_(&update_handler_map_) {
     30   }
     31 
     32   virtual void SetUp() {
     33     dir_maker_.SetUp();
     34 
     35     AddUpdateHandler(AUTOFILL, GROUP_DB);
     36     AddUpdateHandler(BOOKMARKS, GROUP_UI);
     37     AddUpdateHandler(PREFERENCES, GROUP_UI);
     38   }
     39 
     40   virtual void TearDown() {
     41     dir_maker_.TearDown();
     42   }
     43 
     44   ModelTypeSet proto_request_types() {
     45     ModelTypeSet types;
     46     for (UpdateHandlerMap::iterator it = update_handler_map_.begin();
     47          it != update_handler_map_.end(); ++it) {
     48       types.Put(it->first);
     49     }
     50     return types;
     51   }
     52 
     53   syncable::Directory* directory() {
     54     return dir_maker_.directory();
     55   }
     56 
     57   UpdateHandlerMap* update_handler_map() {
     58     return &update_handler_map_;
     59   }
     60 
     61   void InitFakeUpdateResponse(sync_pb::GetUpdatesResponse* response) {
     62     ModelTypeSet types = proto_request_types();
     63 
     64     for (ModelTypeSet::Iterator it = types.First(); it.Good(); it.Inc()) {
     65       sync_pb::DataTypeProgressMarker* marker =
     66           response->add_new_progress_marker();
     67       marker->set_data_type_id(GetSpecificsFieldNumberFromModelType(it.Get()));
     68       marker->set_token("foobarbaz");
     69     }
     70 
     71     response->set_changes_remaining(0);
     72   }
     73 
     74  private:
     75   void AddUpdateHandler(ModelType type, ModelSafeGroup group) {
     76     DCHECK(directory());
     77     scoped_refptr<ModelSafeWorker> worker = new FakeModelWorker(group);
     78     SyncDirectoryUpdateHandler* handler =
     79         new SyncDirectoryUpdateHandler(directory(), type, worker);
     80     update_handler_map_.insert(std::make_pair(type, handler));
     81   }
     82 
     83   base::MessageLoop loop_;  // Needed for directory init.
     84   TestDirectorySetterUpper dir_maker_;
     85 
     86   UpdateHandlerMap update_handler_map_;
     87   STLValueDeleter<UpdateHandlerMap> update_handler_map_deleter_;
     88 
     89   DISALLOW_COPY_AND_ASSIGN(DownloadUpdatesTest);
     90 };
     91 
     92 // Basic test to make sure nudges are expressed properly in the request.
     93 TEST_F(DownloadUpdatesTest, BookmarkNudge) {
     94   sessions::NudgeTracker nudge_tracker;
     95   nudge_tracker.RecordLocalChange(ModelTypeSet(BOOKMARKS));
     96 
     97   sync_pb::ClientToServerMessage msg;
     98   download::BuildNormalDownloadUpdatesImpl(proto_request_types(),
     99                                            update_handler_map(),
    100                                            nudge_tracker,
    101                                            msg.mutable_get_updates());
    102 
    103   const sync_pb::GetUpdatesMessage& gu_msg = msg.get_updates();
    104   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::LOCAL,
    105             gu_msg.caller_info().source());
    106   EXPECT_EQ(sync_pb::SyncEnums::GU_TRIGGER, gu_msg.get_updates_origin());
    107   for (int i = 0; i < gu_msg.from_progress_marker_size(); ++i) {
    108     syncer::ModelType type = GetModelTypeFromSpecificsFieldNumber(
    109         gu_msg.from_progress_marker(i).data_type_id());
    110 
    111     const sync_pb::DataTypeProgressMarker& progress_marker =
    112         gu_msg.from_progress_marker(i);
    113     const sync_pb::GetUpdateTriggers& gu_trigger =
    114         progress_marker.get_update_triggers();
    115 
    116     // We perform some basic tests of GU trigger and source fields here.  The
    117     // more complicated scenarios are tested by the NudgeTracker tests.
    118     if (type == BOOKMARKS) {
    119       EXPECT_TRUE(progress_marker.has_notification_hint());
    120       EXPECT_EQ("", progress_marker.notification_hint());
    121       EXPECT_EQ(1, gu_trigger.local_modification_nudges());
    122       EXPECT_EQ(0, gu_trigger.datatype_refresh_nudges());
    123     } else {
    124       EXPECT_FALSE(progress_marker.has_notification_hint());
    125       EXPECT_EQ(0, gu_trigger.local_modification_nudges());
    126       EXPECT_EQ(0, gu_trigger.datatype_refresh_nudges());
    127     }
    128   }
    129 }
    130 
    131 // Basic test to ensure invalidation payloads are expressed in the request.
    132 TEST_F(DownloadUpdatesTest, NotifyMany) {
    133   sessions::NudgeTracker nudge_tracker;
    134   nudge_tracker.RecordRemoteInvalidation(
    135       BuildInvalidationMap(AUTOFILL, 1, "autofill_payload"));
    136   nudge_tracker.RecordRemoteInvalidation(
    137       BuildInvalidationMap(BOOKMARKS, 1, "bookmark_payload"));
    138   nudge_tracker.RecordRemoteInvalidation(
    139       BuildInvalidationMap(PREFERENCES, 1, "preferences_payload"));
    140   ModelTypeSet notified_types;
    141   notified_types.Put(AUTOFILL);
    142   notified_types.Put(BOOKMARKS);
    143   notified_types.Put(PREFERENCES);
    144 
    145   sync_pb::ClientToServerMessage msg;
    146   download::BuildNormalDownloadUpdatesImpl(proto_request_types(),
    147                                            update_handler_map(),
    148                                            nudge_tracker,
    149                                            msg.mutable_get_updates());
    150 
    151   const sync_pb::GetUpdatesMessage& gu_msg = msg.get_updates();
    152   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::NOTIFICATION,
    153             gu_msg.caller_info().source());
    154   EXPECT_EQ(sync_pb::SyncEnums::GU_TRIGGER, gu_msg.get_updates_origin());
    155   for (int i = 0; i < gu_msg.from_progress_marker_size(); ++i) {
    156     syncer::ModelType type = GetModelTypeFromSpecificsFieldNumber(
    157         gu_msg.from_progress_marker(i).data_type_id());
    158 
    159     const sync_pb::DataTypeProgressMarker& progress_marker =
    160         gu_msg.from_progress_marker(i);
    161     const sync_pb::GetUpdateTriggers& gu_trigger =
    162         progress_marker.get_update_triggers();
    163 
    164     // We perform some basic tests of GU trigger and source fields here.  The
    165     // more complicated scenarios are tested by the NudgeTracker tests.
    166     if (notified_types.Has(type)) {
    167       EXPECT_TRUE(progress_marker.has_notification_hint());
    168       EXPECT_FALSE(progress_marker.notification_hint().empty());
    169       EXPECT_EQ(1, gu_trigger.notification_hint_size());
    170     } else {
    171       EXPECT_FALSE(progress_marker.has_notification_hint());
    172       EXPECT_EQ(0, gu_trigger.notification_hint_size());
    173     }
    174   }
    175 }
    176 
    177 TEST_F(DownloadUpdatesTest, ConfigureTest) {
    178   sync_pb::ClientToServerMessage msg;
    179   download::BuildDownloadUpdatesForConfigureImpl(
    180       proto_request_types(),
    181       update_handler_map(),
    182       sync_pb::GetUpdatesCallerInfo::RECONFIGURATION,
    183       msg.mutable_get_updates());
    184 
    185   const sync_pb::GetUpdatesMessage& gu_msg = msg.get_updates();
    186 
    187   EXPECT_EQ(sync_pb::SyncEnums::RECONFIGURATION, gu_msg.get_updates_origin());
    188   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::RECONFIGURATION,
    189             gu_msg.caller_info().source());
    190 
    191   ModelTypeSet progress_types;
    192   for (int i = 0; i < gu_msg.from_progress_marker_size(); ++i) {
    193     syncer::ModelType type = GetModelTypeFromSpecificsFieldNumber(
    194         gu_msg.from_progress_marker(i).data_type_id());
    195     progress_types.Put(type);
    196   }
    197   EXPECT_TRUE(proto_request_types().Equals(progress_types));
    198 }
    199 
    200 TEST_F(DownloadUpdatesTest, PollTest) {
    201   sync_pb::ClientToServerMessage msg;
    202   download::BuildDownloadUpdatesForPollImpl(
    203       proto_request_types(),
    204       update_handler_map(),
    205       msg.mutable_get_updates());
    206 
    207   const sync_pb::GetUpdatesMessage& gu_msg = msg.get_updates();
    208 
    209   EXPECT_EQ(sync_pb::SyncEnums::PERIODIC, gu_msg.get_updates_origin());
    210   EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::PERIODIC,
    211             gu_msg.caller_info().source());
    212 
    213   ModelTypeSet progress_types;
    214   for (int i = 0; i < gu_msg.from_progress_marker_size(); ++i) {
    215     syncer::ModelType type = GetModelTypeFromSpecificsFieldNumber(
    216         gu_msg.from_progress_marker(i).data_type_id());
    217     progress_types.Put(type);
    218   }
    219   EXPECT_TRUE(proto_request_types().Equals(progress_types));
    220 }
    221 
    222 // Verify that a bogus response message is detected.
    223 TEST_F(DownloadUpdatesTest, InvalidResponse) {
    224   sync_pb::GetUpdatesResponse gu_response;
    225   InitFakeUpdateResponse(&gu_response);
    226 
    227   // This field is essential for making the client stop looping.  If it's unset
    228   // then something is very wrong.  The client should detect this.
    229   gu_response.clear_changes_remaining();
    230 
    231   sessions::StatusController status;
    232   SyncerError error = download::ProcessResponse(gu_response,
    233                                                 proto_request_types(),
    234                                                 update_handler_map(),
    235                                                 &status);
    236   EXPECT_EQ(error, SERVER_RESPONSE_VALIDATION_FAILED);
    237 }
    238 
    239 // Verify that we correctly detect when there's more work to be done.
    240 TEST_F(DownloadUpdatesTest, MoreToDownloadResponse) {
    241   sync_pb::GetUpdatesResponse gu_response;
    242   InitFakeUpdateResponse(&gu_response);
    243   gu_response.set_changes_remaining(1);
    244 
    245   sessions::StatusController status;
    246   SyncerError error = download::ProcessResponse(gu_response,
    247                                                 proto_request_types(),
    248                                                 update_handler_map(),
    249                                                 &status);
    250   EXPECT_EQ(error, SERVER_MORE_TO_DOWNLOAD);
    251 }
    252 
    253 // A simple scenario: No updates returned and nothing more to download.
    254 TEST_F(DownloadUpdatesTest, NormalResponseTest) {
    255   sync_pb::GetUpdatesResponse gu_response;
    256   InitFakeUpdateResponse(&gu_response);
    257   gu_response.set_changes_remaining(0);
    258 
    259   sessions::StatusController status;
    260   SyncerError error = download::ProcessResponse(gu_response,
    261                                                 proto_request_types(),
    262                                                 update_handler_map(),
    263                                                 &status);
    264   EXPECT_EQ(error, SYNCER_OK);
    265 }
    266 
    267 class DownloadUpdatesDebugInfoTest : public ::testing::Test {
    268  public:
    269   DownloadUpdatesDebugInfoTest() {}
    270   virtual ~DownloadUpdatesDebugInfoTest() {}
    271 
    272   sessions::StatusController* status() {
    273     return &status_;
    274   }
    275 
    276   sessions::DebugInfoGetter* debug_info_getter() {
    277     return &debug_info_getter_;
    278   }
    279 
    280   void AddDebugEvent() {
    281     debug_info_getter_.AddDebugEvent();
    282   }
    283 
    284  private:
    285   sessions::StatusController status_;
    286   MockDebugInfoGetter debug_info_getter_;
    287 };
    288 
    289 
    290 // Verify CopyClientDebugInfo when there are no events to upload.
    291 TEST_F(DownloadUpdatesDebugInfoTest, VerifyCopyClientDebugInfo_Empty) {
    292   sync_pb::DebugInfo debug_info;
    293   download::CopyClientDebugInfo(debug_info_getter(), &debug_info);
    294   EXPECT_EQ(0, debug_info.events_size());
    295 }
    296 
    297 TEST_F(DownloadUpdatesDebugInfoTest, VerifyCopyOverwrites) {
    298   sync_pb::DebugInfo debug_info;
    299   AddDebugEvent();
    300   download::CopyClientDebugInfo(debug_info_getter(), &debug_info);
    301   EXPECT_EQ(1, debug_info.events_size());
    302   download::CopyClientDebugInfo(debug_info_getter(), &debug_info);
    303   EXPECT_EQ(1, debug_info.events_size());
    304 }
    305 
    306 }  // namespace syncer
    307