Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / web_resource / promo_resource_service_unittest.cc
blob4fe7a73d25c1d10ccb52c027cefe14ccb21557f2
1 // Copyright (c) 2012 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 <utility>
6 #include <vector>
8 #include "base/bind.h"
9 #include "base/json/json_reader.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/prefs/testing_pref_service.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "base/values.h"
17 #include "components/version_info/version_info.h"
18 #include "components/web_resource/notification_promo.h"
19 #include "net/url_request/test_url_fetcher_factory.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
23 namespace web_resource {
25 namespace {
27 version_info::Channel kChannel = version_info::Channel::UNKNOWN;
29 const char kDateFormat[] = "dd MMM yyyy HH:mm:ss zzzz";
31 bool YearFromNow(double* date_epoch, std::string* date_string) {
32 *date_epoch = (base::Time::Now() + base::TimeDelta::FromDays(365)).ToTimeT();
34 UErrorCode status = U_ZERO_ERROR;
35 icu::SimpleDateFormat simple_formatter(icu::UnicodeString(kDateFormat),
36 icu::Locale("en_US"), status);
37 if (!U_SUCCESS(status))
38 return false;
40 icu::UnicodeString date_unicode_string;
41 simple_formatter.format(static_cast<UDate>(*date_epoch * 1000),
42 date_unicode_string, status);
43 if (!U_SUCCESS(status))
44 return false;
46 return base::UTF16ToUTF8(date_unicode_string.getBuffer(),
47 static_cast<size_t>(date_unicode_string.length()),
48 date_string);
51 } // namespace
53 class NotificationPromoTest : public testing::Test {
54 public:
55 NotificationPromoTest()
56 : notification_promo_(&local_state_),
57 received_notification_(false),
58 start_(0.0),
59 end_(0.0),
60 num_groups_(0),
61 initial_segment_(0),
62 increment_(1),
63 time_slice_(0),
64 max_group_(0),
65 max_views_(0),
66 closed_(false) {
67 NotificationPromo::RegisterPrefs(local_state_.registry());
70 void Init(const std::string& json,
71 const std::string& promo_text,
72 double start,
73 int num_groups,
74 int initial_segment,
75 int increment,
76 int time_slice,
77 int max_group,
78 int max_views) {
79 double year_from_now_epoch;
80 std::string year_from_now_string;
81 ASSERT_TRUE(YearFromNow(&year_from_now_epoch, &year_from_now_string));
83 std::vector<std::string> replacements;
84 replacements.push_back(year_from_now_string);
86 std::string json_with_end_date(
87 base::ReplaceStringPlaceholders(json, replacements, NULL));
88 base::Value* value(base::JSONReader::Read(json_with_end_date).release());
89 ASSERT_TRUE(value);
91 base::DictionaryValue* dict = NULL;
92 value->GetAsDictionary(&dict);
93 ASSERT_TRUE(dict);
94 test_json_.reset(dict);
96 promo_type_ = NotificationPromo::NTP_NOTIFICATION_PROMO;
97 promo_text_ = promo_text;
99 start_ = start;
100 end_ = year_from_now_epoch;
102 num_groups_ = num_groups;
103 initial_segment_ = initial_segment;
104 increment_ = increment;
105 time_slice_ = time_slice;
106 max_group_ = max_group;
108 max_views_ = max_views;
110 closed_ = false;
111 received_notification_ = false;
114 void InitPromoFromJson(bool should_receive_notification) {
115 notification_promo_.InitFromJson(*test_json_, promo_type_);
116 EXPECT_EQ(should_receive_notification,
117 notification_promo_.new_notification());
119 // Test the fields.
120 TestNotification();
123 void TestNotification() {
124 // Check values.
125 EXPECT_EQ(notification_promo_.promo_text_, promo_text_);
127 EXPECT_EQ(notification_promo_.start_, start_);
128 EXPECT_EQ(notification_promo_.end_, end_);
130 EXPECT_EQ(notification_promo_.num_groups_, num_groups_);
131 EXPECT_EQ(notification_promo_.initial_segment_, initial_segment_);
132 EXPECT_EQ(notification_promo_.increment_, increment_);
133 EXPECT_EQ(notification_promo_.time_slice_, time_slice_);
134 EXPECT_EQ(notification_promo_.max_group_, max_group_);
136 EXPECT_EQ(notification_promo_.max_views_, max_views_);
137 EXPECT_EQ(notification_promo_.closed_, closed_);
139 // Check group within bounds.
140 EXPECT_GE(notification_promo_.group_, 0);
141 EXPECT_LT(notification_promo_.group_, num_groups_);
143 // Views should be 0 for now.
144 EXPECT_EQ(notification_promo_.views_, 0);
147 // Create a new NotificationPromo from prefs and compare to current
148 // notification.
149 void TestInitFromPrefs() {
150 NotificationPromo prefs_notification_promo(&local_state_);
151 prefs_notification_promo.InitFromPrefs(promo_type_);
153 EXPECT_EQ(notification_promo_.local_state_,
154 prefs_notification_promo.local_state_);
155 EXPECT_EQ(notification_promo_.promo_text_,
156 prefs_notification_promo.promo_text_);
157 EXPECT_EQ(notification_promo_.start_, prefs_notification_promo.start_);
158 EXPECT_EQ(notification_promo_.end_, prefs_notification_promo.end_);
159 EXPECT_EQ(notification_promo_.num_groups_,
160 prefs_notification_promo.num_groups_);
161 EXPECT_EQ(notification_promo_.initial_segment_,
162 prefs_notification_promo.initial_segment_);
163 EXPECT_EQ(notification_promo_.increment_,
164 prefs_notification_promo.increment_);
165 EXPECT_EQ(notification_promo_.time_slice_,
166 prefs_notification_promo.time_slice_);
167 EXPECT_EQ(notification_promo_.max_group_,
168 prefs_notification_promo.max_group_);
169 EXPECT_EQ(notification_promo_.max_views_,
170 prefs_notification_promo.max_views_);
171 EXPECT_EQ(notification_promo_.group_, prefs_notification_promo.group_);
172 EXPECT_EQ(notification_promo_.views_, prefs_notification_promo.views_);
173 EXPECT_EQ(notification_promo_.closed_, prefs_notification_promo.closed_);
176 void TestGroup() {
177 // Test out of range groups.
178 const int incr = num_groups_ / 20;
179 for (int i = max_group_; i < num_groups_; i += incr) {
180 notification_promo_.group_ = i;
181 EXPECT_FALSE(notification_promo_.CanShow());
184 // Test in-range groups.
185 for (int i = 0; i < max_group_; i += incr) {
186 notification_promo_.group_ = i;
187 EXPECT_TRUE(notification_promo_.CanShow());
190 // When max_group_ is 0, all groups pass.
191 notification_promo_.max_group_ = 0;
192 for (int i = 0; i < num_groups_; i += incr) {
193 notification_promo_.group_ = i;
194 EXPECT_TRUE(notification_promo_.CanShow());
196 notification_promo_.WritePrefs();
199 void TestViews() {
200 notification_promo_.views_ = notification_promo_.max_views_ - 2;
201 notification_promo_.WritePrefs();
203 NotificationPromo::HandleViewed(promo_type_, &local_state_);
204 NotificationPromo new_promo(&local_state_);
205 new_promo.InitFromPrefs(promo_type_);
206 EXPECT_EQ(new_promo.max_views_ - 1, new_promo.views_);
207 EXPECT_TRUE(new_promo.CanShow());
208 NotificationPromo::HandleViewed(promo_type_, &local_state_);
209 new_promo.InitFromPrefs(promo_type_);
210 EXPECT_EQ(new_promo.max_views_, new_promo.views_);
211 EXPECT_FALSE(new_promo.CanShow());
213 // Test out of range views.
214 for (int i = max_views_; i < max_views_ * 2; ++i) {
215 new_promo.views_ = i;
216 EXPECT_FALSE(new_promo.CanShow());
219 // Test in range views.
220 for (int i = 0; i < max_views_; ++i) {
221 new_promo.views_ = i;
222 EXPECT_TRUE(new_promo.CanShow());
224 new_promo.WritePrefs();
227 void TestClosed() {
228 NotificationPromo new_promo(&local_state_);
229 new_promo.InitFromPrefs(promo_type_);
230 EXPECT_FALSE(new_promo.closed_);
231 EXPECT_TRUE(new_promo.CanShow());
233 NotificationPromo::HandleClosed(promo_type_, &local_state_);
234 new_promo.InitFromPrefs(promo_type_);
235 EXPECT_TRUE(new_promo.closed_);
236 EXPECT_FALSE(new_promo.CanShow());
238 new_promo.closed_ = false;
239 EXPECT_TRUE(new_promo.CanShow());
240 new_promo.WritePrefs();
243 void TestPromoText() {
244 notification_promo_.promo_text_.clear();
245 EXPECT_FALSE(notification_promo_.CanShow());
247 notification_promo_.promo_text_ = promo_text_;
248 EXPECT_TRUE(notification_promo_.CanShow());
251 void TestTime() {
252 const double now = base::Time::Now().ToDoubleT();
253 const double qhour = 15 * 60;
255 notification_promo_.group_ = 0; // For simplicity.
257 notification_promo_.start_ = now - qhour;
258 notification_promo_.end_ = now + qhour;
259 EXPECT_TRUE(notification_promo_.CanShow());
261 // Start time has not arrived.
262 notification_promo_.start_ = now + qhour;
263 notification_promo_.end_ = now + qhour;
264 EXPECT_FALSE(notification_promo_.CanShow());
266 // End time has past.
267 notification_promo_.start_ = now - qhour;
268 notification_promo_.end_ = now - qhour;
269 EXPECT_FALSE(notification_promo_.CanShow());
271 notification_promo_.start_ = start_;
272 notification_promo_.end_ = end_;
273 EXPECT_TRUE(notification_promo_.CanShow());
276 void TestIncrement() {
277 const double now = base::Time::Now().ToDoubleT();
278 const double slice = 60;
280 notification_promo_.num_groups_ = 18;
281 notification_promo_.initial_segment_ = 5;
282 notification_promo_.increment_ = 3;
283 notification_promo_.time_slice_ = slice;
285 notification_promo_.start_ = now - 1;
286 notification_promo_.end_ = now + slice;
288 // Test initial segment.
289 notification_promo_.group_ = 4;
290 EXPECT_TRUE(notification_promo_.CanShow());
291 notification_promo_.group_ = 5;
292 EXPECT_FALSE(notification_promo_.CanShow());
294 // Test first increment.
295 notification_promo_.start_ -= slice;
296 notification_promo_.group_ = 7;
297 EXPECT_TRUE(notification_promo_.CanShow());
298 notification_promo_.group_ = 8;
299 EXPECT_FALSE(notification_promo_.CanShow());
301 // Test second increment.
302 notification_promo_.start_ -= slice;
303 notification_promo_.group_ = 10;
304 EXPECT_TRUE(notification_promo_.CanShow());
305 notification_promo_.group_ = 11;
306 EXPECT_FALSE(notification_promo_.CanShow());
308 // Test penultimate increment.
309 notification_promo_.start_ -= 2 * slice;
310 notification_promo_.group_ = 16;
311 EXPECT_TRUE(notification_promo_.CanShow());
312 notification_promo_.group_ = 17;
313 EXPECT_FALSE(notification_promo_.CanShow());
315 // Test last increment.
316 notification_promo_.start_ -= slice;
317 EXPECT_TRUE(notification_promo_.CanShow());
320 const NotificationPromo& promo() const { return notification_promo_; }
322 protected:
323 TestingPrefServiceSimple local_state_;
325 private:
326 base::MessageLoop loop_;
327 NotificationPromo notification_promo_;
328 bool received_notification_;
329 scoped_ptr<base::DictionaryValue> test_json_;
331 NotificationPromo::PromoType promo_type_;
332 std::string promo_text_;
334 double start_;
335 double end_;
337 int num_groups_;
338 int initial_segment_;
339 int increment_;
340 int time_slice_;
341 int max_group_;
343 int max_views_;
345 bool closed_;
348 // Test that everything gets parsed correctly, notifications are sent,
349 // and CanShow() is handled correctly under variety of conditions.
350 // Additionally, test that the first string in |strings| is used if
351 // no payload.promo_short_message is specified in the JSON response.
352 TEST_F(NotificationPromoTest, NotificationPromoTest) {
353 // Set up start date and promo line in a Dictionary as if parsed from the
354 // service. date[0].end is replaced with a date 1 year in the future.
355 Init(
357 " \"ntp_notification_promo\": ["
358 " {"
359 " \"date\":"
360 " ["
361 " {"
362 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
363 " \"end\":\"$1\""
364 " }"
365 " ],"
366 " \"strings\":"
367 " {"
368 " \"NTP4_HOW_DO_YOU_FEEL_ABOUT_CHROME\":"
369 " \"What do you think of Chrome?\""
370 " },"
371 " \"grouping\":"
372 " {"
373 " \"buckets\":1000,"
374 " \"segment\":200,"
375 " \"increment\":100,"
376 " \"increment_frequency\":3600,"
377 " \"increment_max\":400"
378 " },"
379 " \"payload\":"
380 " {"
381 " \"days_active\":7,"
382 " \"install_age_days\":21"
383 " },"
384 " \"max_views\":30"
385 " }"
386 " ]"
387 "}",
388 "What do you think of Chrome?",
389 // The starting date is in 1999 to make tests pass
390 // on Android devices with incorrect or unset date/time.
391 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
392 1000, 200, 100, 3600, 400, 30);
394 InitPromoFromJson(true);
396 // Second time should not trigger a notification.
397 InitPromoFromJson(false);
399 TestInitFromPrefs();
401 // Test various conditions of CanShow.
402 // TestGroup Has the side effect of setting us to a passing group.
403 TestGroup();
404 TestViews();
405 TestClosed();
406 TestPromoText();
407 TestTime();
408 TestIncrement();
411 // Test that payload.promo_message_short is used if present.
412 TEST_F(NotificationPromoTest, NotificationPromoCompatNoStringsTest) {
413 // Set up start date and promo line in a Dictionary as if parsed from the
414 // service. date[0].end is replaced with a date 1 year in the future.
415 Init(
417 " \"ntp_notification_promo\": ["
418 " {"
419 " \"date\":"
420 " ["
421 " {"
422 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
423 " \"end\":\"$1\""
424 " }"
425 " ],"
426 " \"grouping\":"
427 " {"
428 " \"buckets\":1000,"
429 " \"segment\":200,"
430 " \"increment\":100,"
431 " \"increment_frequency\":3600,"
432 " \"increment_max\":400"
433 " },"
434 " \"payload\":"
435 " {"
436 " \"promo_message_short\":"
437 " \"What do you think of Chrome?\","
438 " \"days_active\":7,"
439 " \"install_age_days\":21"
440 " },"
441 " \"max_views\":30"
442 " }"
443 " ]"
444 "}",
445 "What do you think of Chrome?",
446 // The starting date is in 1999 to make tests pass
447 // on Android devices with incorrect or unset date/time.
448 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
449 1000, 200, 100, 3600, 400, 30);
451 InitPromoFromJson(true);
452 // Second time should not trigger a notification.
453 InitPromoFromJson(false);
454 TestInitFromPrefs();
457 // Test that strings.|payload.promo_message_short| is used if present.
458 TEST_F(NotificationPromoTest, NotificationPromoCompatPayloadStringsTest) {
459 // Set up start date and promo line in a Dictionary as if parsed from the
460 // service. date[0].end is replaced with a date 1 year in the future.
461 Init(
463 " \"ntp_notification_promo\": ["
464 " {"
465 " \"date\":"
466 " ["
467 " {"
468 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
469 " \"end\":\"$1\""
470 " }"
471 " ],"
472 " \"grouping\":"
473 " {"
474 " \"buckets\":1000,"
475 " \"segment\":200,"
476 " \"increment\":100,"
477 " \"increment_frequency\":3600,"
478 " \"increment_max\":400"
479 " },"
480 " \"strings\":"
481 " {"
482 " \"bogus\":\"string\","
483 " \"GOOD_STRING\":"
484 " \"What do you think of Chrome?\""
485 " },"
486 " \"payload\":"
487 " {"
488 " \"promo_message_short\":"
489 " \"GOOD_STRING\","
490 " \"days_active\":7,"
491 " \"install_age_days\":21"
492 " },"
493 " \"max_views\":30"
494 " }"
495 " ]"
496 "}",
497 "What do you think of Chrome?",
498 // The starting date is in 1999 to make tests pass
499 // on Android devices with incorrect or unset date/time.
500 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
501 1000, 200, 100, 3600, 400, 30);
503 InitPromoFromJson(true);
504 // Second time should not trigger a notification.
505 InitPromoFromJson(false);
506 TestInitFromPrefs();
509 TEST_F(NotificationPromoTest, PromoServerURLTest) {
510 GURL promo_server_url = NotificationPromo::PromoServerURL(kChannel);
511 EXPECT_FALSE(promo_server_url.is_empty());
512 EXPECT_TRUE(promo_server_url.is_valid());
513 EXPECT_TRUE(promo_server_url.SchemeIs(url::kHttpsScheme));
514 // TODO(achuith): Test this better.
517 } // namespace web_resource