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