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.
5 #include "sync/engine/download.h"
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"
23 using sessions::MockDebugInfoGetter
;
25 // A test fixture for tests exercising download updates functions.
26 class DownloadUpdatesTest
: public ::testing::Test
{
28 DownloadUpdatesTest() : update_handler_deleter_(&update_handler_map_
) {}
30 virtual void SetUp() {
33 AddUpdateHandler(AUTOFILL
, GROUP_DB
);
34 AddUpdateHandler(BOOKMARKS
, GROUP_UI
);
35 AddUpdateHandler(PREFERENCES
, GROUP_UI
);
38 virtual void TearDown() {
39 dir_maker_
.TearDown();
42 ModelTypeSet
proto_request_types() {
43 return proto_request_types_
;
46 syncable::Directory
* directory() {
47 return dir_maker_
.directory();
50 GetUpdatesProcessor
* get_updates_processor() {
51 return get_updates_processor_
.get();
54 void InitFakeUpdateResponse(sync_pb::GetUpdatesResponse
* response
) {
55 ModelTypeSet types
= proto_request_types();
57 for (ModelTypeSet::Iterator it
= types
.First(); it
.Good(); it
.Inc()) {
58 sync_pb::DataTypeProgressMarker
* marker
=
59 response
->add_new_progress_marker();
60 marker
->set_data_type_id(GetSpecificsFieldNumberFromModelType(it
.Get()));
61 marker
->set_token("foobarbaz");
64 response
->set_changes_remaining(0);
68 void AddUpdateHandler(ModelType type
, ModelSafeGroup group
) {
71 proto_request_types_
.Put(type
);
73 scoped_refptr
<ModelSafeWorker
> worker
= new FakeModelWorker(group
);
74 SyncDirectoryUpdateHandler
* handler
=
75 new SyncDirectoryUpdateHandler(directory(), type
, worker
);
76 update_handler_map_
.insert(std::make_pair(type
, handler
));
77 get_updates_processor_
.reset(new GetUpdatesProcessor(&update_handler_map_
));
80 base::MessageLoop loop_
; // Needed for directory init.
81 TestDirectorySetterUpper dir_maker_
;
83 ModelTypeSet proto_request_types_
;
84 UpdateHandlerMap update_handler_map_
;
85 STLValueDeleter
<UpdateHandlerMap
> update_handler_deleter_
;
86 scoped_ptr
<GetUpdatesProcessor
> get_updates_processor_
;
88 DISALLOW_COPY_AND_ASSIGN(DownloadUpdatesTest
);
91 // Basic test to make sure nudges are expressed properly in the request.
92 TEST_F(DownloadUpdatesTest
, BookmarkNudge
) {
93 sessions::NudgeTracker nudge_tracker
;
94 nudge_tracker
.RecordLocalChange(ModelTypeSet(BOOKMARKS
));
96 sync_pb::ClientToServerMessage msg
;
97 download::BuildNormalDownloadUpdatesImpl(proto_request_types(),
98 get_updates_processor(),
100 msg
.mutable_get_updates());
102 const sync_pb::GetUpdatesMessage
& gu_msg
= msg
.get_updates();
103 EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::LOCAL
,
104 gu_msg
.caller_info().source());
105 EXPECT_EQ(sync_pb::SyncEnums::GU_TRIGGER
, gu_msg
.get_updates_origin());
106 for (int i
= 0; i
< gu_msg
.from_progress_marker_size(); ++i
) {
107 syncer::ModelType type
= GetModelTypeFromSpecificsFieldNumber(
108 gu_msg
.from_progress_marker(i
).data_type_id());
110 const sync_pb::DataTypeProgressMarker
& progress_marker
=
111 gu_msg
.from_progress_marker(i
);
112 const sync_pb::GetUpdateTriggers
& gu_trigger
=
113 progress_marker
.get_update_triggers();
115 // We perform some basic tests of GU trigger and source fields here. The
116 // more complicated scenarios are tested by the NudgeTracker tests.
117 if (type
== BOOKMARKS
) {
118 EXPECT_TRUE(progress_marker
.has_notification_hint());
119 EXPECT_EQ("", progress_marker
.notification_hint());
120 EXPECT_EQ(1, gu_trigger
.local_modification_nudges());
121 EXPECT_EQ(0, gu_trigger
.datatype_refresh_nudges());
123 EXPECT_FALSE(progress_marker
.has_notification_hint());
124 EXPECT_EQ(0, gu_trigger
.local_modification_nudges());
125 EXPECT_EQ(0, gu_trigger
.datatype_refresh_nudges());
130 // Basic test to ensure invalidation payloads are expressed in the request.
131 TEST_F(DownloadUpdatesTest
, NotifyMany
) {
132 sessions::NudgeTracker nudge_tracker
;
133 nudge_tracker
.RecordRemoteInvalidation(
134 BuildInvalidationMap(AUTOFILL
, 1, "autofill_payload"));
135 nudge_tracker
.RecordRemoteInvalidation(
136 BuildInvalidationMap(BOOKMARKS
, 1, "bookmark_payload"));
137 nudge_tracker
.RecordRemoteInvalidation(
138 BuildInvalidationMap(PREFERENCES
, 1, "preferences_payload"));
139 ModelTypeSet notified_types
;
140 notified_types
.Put(AUTOFILL
);
141 notified_types
.Put(BOOKMARKS
);
142 notified_types
.Put(PREFERENCES
);
144 sync_pb::ClientToServerMessage msg
;
145 download::BuildNormalDownloadUpdatesImpl(proto_request_types(),
146 get_updates_processor(),
148 msg
.mutable_get_updates());
150 const sync_pb::GetUpdatesMessage
& gu_msg
= msg
.get_updates();
151 EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::NOTIFICATION
,
152 gu_msg
.caller_info().source());
153 EXPECT_EQ(sync_pb::SyncEnums::GU_TRIGGER
, gu_msg
.get_updates_origin());
154 for (int i
= 0; i
< gu_msg
.from_progress_marker_size(); ++i
) {
155 syncer::ModelType type
= GetModelTypeFromSpecificsFieldNumber(
156 gu_msg
.from_progress_marker(i
).data_type_id());
158 const sync_pb::DataTypeProgressMarker
& progress_marker
=
159 gu_msg
.from_progress_marker(i
);
160 const sync_pb::GetUpdateTriggers
& gu_trigger
=
161 progress_marker
.get_update_triggers();
163 // We perform some basic tests of GU trigger and source fields here. The
164 // more complicated scenarios are tested by the NudgeTracker tests.
165 if (notified_types
.Has(type
)) {
166 EXPECT_TRUE(progress_marker
.has_notification_hint());
167 EXPECT_FALSE(progress_marker
.notification_hint().empty());
168 EXPECT_EQ(1, gu_trigger
.notification_hint_size());
170 EXPECT_FALSE(progress_marker
.has_notification_hint());
171 EXPECT_EQ(0, gu_trigger
.notification_hint_size());
176 TEST_F(DownloadUpdatesTest
, ConfigureTest
) {
177 sync_pb::ClientToServerMessage msg
;
178 download::BuildDownloadUpdatesForConfigureImpl(
179 proto_request_types(),
180 get_updates_processor(),
181 sync_pb::GetUpdatesCallerInfo::RECONFIGURATION
,
182 msg
.mutable_get_updates());
184 const sync_pb::GetUpdatesMessage
& gu_msg
= msg
.get_updates();
186 EXPECT_EQ(sync_pb::SyncEnums::RECONFIGURATION
, gu_msg
.get_updates_origin());
187 EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::RECONFIGURATION
,
188 gu_msg
.caller_info().source());
190 ModelTypeSet progress_types
;
191 for (int i
= 0; i
< gu_msg
.from_progress_marker_size(); ++i
) {
192 syncer::ModelType type
= GetModelTypeFromSpecificsFieldNumber(
193 gu_msg
.from_progress_marker(i
).data_type_id());
194 progress_types
.Put(type
);
196 EXPECT_TRUE(proto_request_types().Equals(progress_types
));
199 TEST_F(DownloadUpdatesTest
, PollTest
) {
200 sync_pb::ClientToServerMessage msg
;
201 download::BuildDownloadUpdatesForPollImpl(
202 proto_request_types(),
203 get_updates_processor(),
204 msg
.mutable_get_updates());
206 const sync_pb::GetUpdatesMessage
& gu_msg
= msg
.get_updates();
208 EXPECT_EQ(sync_pb::SyncEnums::PERIODIC
, gu_msg
.get_updates_origin());
209 EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::PERIODIC
,
210 gu_msg
.caller_info().source());
212 ModelTypeSet progress_types
;
213 for (int i
= 0; i
< gu_msg
.from_progress_marker_size(); ++i
) {
214 syncer::ModelType type
= GetModelTypeFromSpecificsFieldNumber(
215 gu_msg
.from_progress_marker(i
).data_type_id());
216 progress_types
.Put(type
);
218 EXPECT_TRUE(proto_request_types().Equals(progress_types
));
221 TEST_F(DownloadUpdatesTest
, RetryTest
) {
222 sync_pb::ClientToServerMessage msg
;
223 download::BuildDownloadUpdatesForRetryImpl(
224 proto_request_types(),
225 get_updates_processor(),
226 msg
.mutable_get_updates());
228 const sync_pb::GetUpdatesMessage
& gu_msg
= msg
.get_updates();
230 EXPECT_EQ(sync_pb::SyncEnums::RETRY
, gu_msg
.get_updates_origin());
231 EXPECT_EQ(sync_pb::GetUpdatesCallerInfo::RETRY
,
232 gu_msg
.caller_info().source());
233 EXPECT_TRUE(gu_msg
.is_retry());
235 ModelTypeSet progress_types
;
236 for (int i
= 0; i
< gu_msg
.from_progress_marker_size(); ++i
) {
237 syncer::ModelType type
= GetModelTypeFromSpecificsFieldNumber(
238 gu_msg
.from_progress_marker(i
).data_type_id());
239 progress_types
.Put(type
);
241 EXPECT_TRUE(proto_request_types().Equals(progress_types
));
244 TEST_F(DownloadUpdatesTest
, NudgeWithRetryTest
) {
245 sessions::NudgeTracker nudge_tracker
;
246 nudge_tracker
.RecordLocalChange(ModelTypeSet(BOOKMARKS
));
247 nudge_tracker
.set_next_retry_time(
248 base::TimeTicks::Now() - base::TimeDelta::FromSeconds(1));
250 sync_pb::ClientToServerMessage msg
;
251 download::BuildNormalDownloadUpdatesImpl(proto_request_types(),
252 get_updates_processor(),
254 msg
.mutable_get_updates());
255 EXPECT_TRUE(msg
.get_updates().is_retry());
258 // Verify that a bogus response message is detected.
259 TEST_F(DownloadUpdatesTest
, InvalidResponse
) {
260 sync_pb::GetUpdatesResponse gu_response
;
261 InitFakeUpdateResponse(&gu_response
);
263 // This field is essential for making the client stop looping. If it's unset
264 // then something is very wrong. The client should detect this.
265 gu_response
.clear_changes_remaining();
267 sessions::StatusController status
;
268 SyncerError error
= download::ProcessResponse(gu_response
,
269 proto_request_types(),
270 get_updates_processor(),
272 EXPECT_EQ(error
, SERVER_RESPONSE_VALIDATION_FAILED
);
275 // Verify that we correctly detect when there's more work to be done.
276 TEST_F(DownloadUpdatesTest
, MoreToDownloadResponse
) {
277 sync_pb::GetUpdatesResponse gu_response
;
278 InitFakeUpdateResponse(&gu_response
);
279 gu_response
.set_changes_remaining(1);
281 sessions::StatusController status
;
282 SyncerError error
= download::ProcessResponse(gu_response
,
283 proto_request_types(),
284 get_updates_processor(),
286 EXPECT_EQ(error
, SERVER_MORE_TO_DOWNLOAD
);
289 // A simple scenario: No updates returned and nothing more to download.
290 TEST_F(DownloadUpdatesTest
, NormalResponseTest
) {
291 sync_pb::GetUpdatesResponse gu_response
;
292 InitFakeUpdateResponse(&gu_response
);
293 gu_response
.set_changes_remaining(0);
295 sessions::StatusController status
;
296 SyncerError error
= download::ProcessResponse(gu_response
,
297 proto_request_types(),
298 get_updates_processor(),
300 EXPECT_EQ(error
, SYNCER_OK
);
303 class DownloadUpdatesDebugInfoTest
: public ::testing::Test
{
305 DownloadUpdatesDebugInfoTest() {}
306 virtual ~DownloadUpdatesDebugInfoTest() {}
308 sessions::StatusController
* status() {
312 sessions::DebugInfoGetter
* debug_info_getter() {
313 return &debug_info_getter_
;
316 void AddDebugEvent() {
317 debug_info_getter_
.AddDebugEvent();
321 sessions::StatusController status_
;
322 MockDebugInfoGetter debug_info_getter_
;
326 // Verify CopyClientDebugInfo when there are no events to upload.
327 TEST_F(DownloadUpdatesDebugInfoTest
, VerifyCopyClientDebugInfo_Empty
) {
328 sync_pb::DebugInfo debug_info
;
329 download::CopyClientDebugInfo(debug_info_getter(), &debug_info
);
330 EXPECT_EQ(0, debug_info
.events_size());
333 TEST_F(DownloadUpdatesDebugInfoTest
, VerifyCopyOverwrites
) {
334 sync_pb::DebugInfo debug_info
;
336 download::CopyClientDebugInfo(debug_info_getter(), &debug_info
);
337 EXPECT_EQ(1, debug_info
.events_size());
338 download::CopyClientDebugInfo(debug_info_getter(), &debug_info
);
339 EXPECT_EQ(1, debug_info
.events_size());
342 } // namespace syncer