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:
DownloadUpdatesTest()28 DownloadUpdatesTest()
29 : update_handler_map_deleter_(&update_handler_map_) {
30 }
31
SetUp()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
TearDown()40 virtual void TearDown() {
41 dir_maker_.TearDown();
42 }
43
proto_request_types()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
directory()53 syncable::Directory* directory() {
54 return dir_maker_.directory();
55 }
56
update_handler_map()57 UpdateHandlerMap* update_handler_map() {
58 return &update_handler_map_;
59 }
60
InitFakeUpdateResponse(sync_pb::GetUpdatesResponse * response)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:
AddUpdateHandler(ModelType type,ModelSafeGroup group)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.
TEST_F(DownloadUpdatesTest,BookmarkNudge)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.
TEST_F(DownloadUpdatesTest,NotifyMany)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
TEST_F(DownloadUpdatesTest,ConfigureTest)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
TEST_F(DownloadUpdatesTest,PollTest)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.
TEST_F(DownloadUpdatesTest,InvalidResponse)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.
TEST_F(DownloadUpdatesTest,MoreToDownloadResponse)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.
TEST_F(DownloadUpdatesTest,NormalResponseTest)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:
DownloadUpdatesDebugInfoTest()269 DownloadUpdatesDebugInfoTest() {}
~DownloadUpdatesDebugInfoTest()270 virtual ~DownloadUpdatesDebugInfoTest() {}
271
status()272 sessions::StatusController* status() {
273 return &status_;
274 }
275
debug_info_getter()276 sessions::DebugInfoGetter* debug_info_getter() {
277 return &debug_info_getter_;
278 }
279
AddDebugEvent()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.
TEST_F(DownloadUpdatesDebugInfoTest,VerifyCopyClientDebugInfo_Empty)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
TEST_F(DownloadUpdatesDebugInfoTest,VerifyCopyOverwrites)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