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.
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
{
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
))
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
))
46 return base::UTF16ToUTF8(date_unicode_string
.getBuffer(),
47 static_cast<size_t>(date_unicode_string
.length()),
53 class NotificationPromoTest
: public testing::Test
{
55 NotificationPromoTest()
56 : notification_promo_(&local_state_
),
57 received_notification_(false),
67 NotificationPromo::RegisterPrefs(local_state_
.registry());
70 void Init(const std::string
& json
,
71 const std::string
& promo_text
,
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());
91 base::DictionaryValue
* dict
= NULL
;
92 value
->GetAsDictionary(&dict
);
94 test_json_
.reset(dict
);
96 promo_type_
= NotificationPromo::NTP_NOTIFICATION_PROMO
;
97 promo_text_
= promo_text
;
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
;
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());
123 void TestNotification() {
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
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_
);
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();
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();
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());
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_
; }
323 TestingPrefServiceSimple local_state_
;
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_
;
338 int initial_segment_
;
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.
357 " \"ntp_notification_promo\": ["
362 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
368 " \"NTP4_HOW_DO_YOU_FEEL_ABOUT_CHROME\":"
369 " \"What do you think of Chrome?\""
375 " \"increment\":100,"
376 " \"increment_frequency\":3600,"
377 " \"increment_max\":400"
381 " \"days_active\":7,"
382 " \"install_age_days\":21"
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);
401 // Test various conditions of CanShow.
402 // TestGroup Has the side effect of setting us to a passing group.
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.
417 " \"ntp_notification_promo\": ["
422 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
430 " \"increment\":100,"
431 " \"increment_frequency\":3600,"
432 " \"increment_max\":400"
436 " \"promo_message_short\":"
437 " \"What do you think of Chrome?\","
438 " \"days_active\":7,"
439 " \"install_age_days\":21"
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);
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.
463 " \"ntp_notification_promo\": ["
468 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
476 " \"increment\":100,"
477 " \"increment_frequency\":3600,"
478 " \"increment_max\":400"
482 " \"bogus\":\"string\","
484 " \"What do you think of Chrome?\""
488 " \"promo_message_short\":"
490 " \"days_active\":7,"
491 " \"install_age_days\":21"
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);
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