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.
8 #include "base/json/json_reader.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/time/time.h"
15 #include "base/values.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/prefs/browser_prefs.h"
18 #include "chrome/browser/web_resource/notification_promo.h"
19 #include "chrome/browser/web_resource/promo_resource_service.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/common/url_constants.h"
22 #include "chrome/test/base/scoped_testing_local_state.h"
23 #include "chrome/test/base/testing_browser_process.h"
24 #include "net/url_request/test_url_fetcher_factory.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
30 const char kDateFormat
[] = "dd MMM yyyy HH:mm:ss zzzz";
32 bool YearFromNow(double* date_epoch
, std::string
* date_string
) {
33 *date_epoch
= (base::Time::Now() + base::TimeDelta::FromDays(365)).ToTimeT();
35 UErrorCode status
= U_ZERO_ERROR
;
36 icu::SimpleDateFormat
simple_formatter(icu::UnicodeString(kDateFormat
),
39 if (!U_SUCCESS(status
))
42 icu::UnicodeString date_unicode_string
;
43 simple_formatter
.format(static_cast<UDate
>(*date_epoch
* 1000),
46 if (!U_SUCCESS(status
))
49 return base::UTF16ToUTF8(date_unicode_string
.getBuffer(),
50 static_cast<size_t>(date_unicode_string
.length()),
56 class PromoResourceServiceTest
: public testing::Test
{
58 // |promo_resource_service_| must be created after |local_state_|.
59 PromoResourceServiceTest()
60 : local_state_(TestingBrowserProcess::GetGlobal()),
61 promo_resource_service_(new PromoResourceService
) {}
64 ScopedTestingLocalState local_state_
;
65 scoped_ptr
<PromoResourceService
> promo_resource_service_
;
66 base::MessageLoop loop_
;
69 class NotificationPromoTest
{
71 NotificationPromoTest()
72 : received_notification_(false),
83 void Init(const std::string
& json
,
84 const std::string
& promo_text
,
86 int num_groups
, int initial_segment
, int increment
,
87 int time_slice
, int max_group
, int max_views
) {
88 double year_from_now_epoch
;
89 std::string year_from_now_string
;
90 ASSERT_TRUE(YearFromNow(&year_from_now_epoch
, &year_from_now_string
));
92 std::vector
<std::string
> replacements
;
93 replacements
.push_back(year_from_now_string
);
95 std::string
json_with_end_date(
96 ReplaceStringPlaceholders(json
, replacements
, NULL
));
97 base::Value
* value(base::JSONReader::Read(json_with_end_date
));
100 base::DictionaryValue
* dict
= NULL
;
101 value
->GetAsDictionary(&dict
);
103 test_json_
.reset(dict
);
105 promo_type_
= NotificationPromo::NTP_NOTIFICATION_PROMO
;
106 promo_text_
= promo_text
;
109 end_
= year_from_now_epoch
;
111 num_groups_
= num_groups
;
112 initial_segment_
= initial_segment
;
113 increment_
= increment
;
114 time_slice_
= time_slice
;
115 max_group_
= max_group
;
117 max_views_
= max_views
;
120 received_notification_
= false;
123 void InitPromoFromJson(bool should_receive_notification
) {
124 notification_promo_
.InitFromJson(*test_json_
, promo_type_
);
125 EXPECT_EQ(should_receive_notification
,
126 notification_promo_
.new_notification());
132 void TestNotification() {
134 EXPECT_EQ(notification_promo_
.promo_text_
, promo_text_
);
136 EXPECT_EQ(notification_promo_
.start_
, start_
);
137 EXPECT_EQ(notification_promo_
.end_
, end_
);
139 EXPECT_EQ(notification_promo_
.num_groups_
, num_groups_
);
140 EXPECT_EQ(notification_promo_
.initial_segment_
, initial_segment_
);
141 EXPECT_EQ(notification_promo_
.increment_
, increment_
);
142 EXPECT_EQ(notification_promo_
.time_slice_
, time_slice_
);
143 EXPECT_EQ(notification_promo_
.max_group_
, max_group_
);
145 EXPECT_EQ(notification_promo_
.max_views_
, max_views_
);
146 EXPECT_EQ(notification_promo_
.closed_
, closed_
);
148 // Check group within bounds.
149 EXPECT_GE(notification_promo_
.group_
, 0);
150 EXPECT_LT(notification_promo_
.group_
, num_groups_
);
152 // Views should be 0 for now.
153 EXPECT_EQ(notification_promo_
.views_
, 0);
156 // Create a new NotificationPromo from prefs and compare to current
158 void TestInitFromPrefs() {
159 NotificationPromo prefs_notification_promo
;
160 prefs_notification_promo
.InitFromPrefs(promo_type_
);
162 EXPECT_EQ(notification_promo_
.prefs_
,
163 prefs_notification_promo
.prefs_
);
164 EXPECT_EQ(notification_promo_
.promo_text_
,
165 prefs_notification_promo
.promo_text_
);
166 EXPECT_EQ(notification_promo_
.start_
,
167 prefs_notification_promo
.start_
);
168 EXPECT_EQ(notification_promo_
.end_
,
169 prefs_notification_promo
.end_
);
170 EXPECT_EQ(notification_promo_
.num_groups_
,
171 prefs_notification_promo
.num_groups_
);
172 EXPECT_EQ(notification_promo_
.initial_segment_
,
173 prefs_notification_promo
.initial_segment_
);
174 EXPECT_EQ(notification_promo_
.increment_
,
175 prefs_notification_promo
.increment_
);
176 EXPECT_EQ(notification_promo_
.time_slice_
,
177 prefs_notification_promo
.time_slice_
);
178 EXPECT_EQ(notification_promo_
.max_group_
,
179 prefs_notification_promo
.max_group_
);
180 EXPECT_EQ(notification_promo_
.max_views_
,
181 prefs_notification_promo
.max_views_
);
182 EXPECT_EQ(notification_promo_
.group_
,
183 prefs_notification_promo
.group_
);
184 EXPECT_EQ(notification_promo_
.views_
,
185 prefs_notification_promo
.views_
);
186 EXPECT_EQ(notification_promo_
.closed_
,
187 prefs_notification_promo
.closed_
);
191 // Test out of range groups.
192 const int incr
= num_groups_
/ 20;
193 for (int i
= max_group_
; i
< num_groups_
; i
+= incr
) {
194 notification_promo_
.group_
= i
;
195 EXPECT_FALSE(notification_promo_
.CanShow());
198 // Test in-range groups.
199 for (int i
= 0; i
< max_group_
; i
+= incr
) {
200 notification_promo_
.group_
= i
;
201 EXPECT_TRUE(notification_promo_
.CanShow());
204 // When max_group_ is 0, all groups pass.
205 notification_promo_
.max_group_
= 0;
206 for (int i
= 0; i
< num_groups_
; i
+= incr
) {
207 notification_promo_
.group_
= i
;
208 EXPECT_TRUE(notification_promo_
.CanShow());
210 notification_promo_
.WritePrefs();
214 notification_promo_
.views_
= notification_promo_
.max_views_
- 2;
215 notification_promo_
.WritePrefs();
217 NotificationPromo::HandleViewed(promo_type_
);
218 NotificationPromo new_promo
;
219 new_promo
.InitFromPrefs(promo_type_
);
220 EXPECT_EQ(new_promo
.max_views_
- 1, new_promo
.views_
);
221 EXPECT_TRUE(new_promo
.CanShow());
222 NotificationPromo::HandleViewed(promo_type_
);
223 new_promo
.InitFromPrefs(promo_type_
);
224 EXPECT_EQ(new_promo
.max_views_
, new_promo
.views_
);
225 EXPECT_FALSE(new_promo
.CanShow());
227 // Test out of range views.
228 for (int i
= max_views_
; i
< max_views_
* 2; ++i
) {
229 new_promo
.views_
= i
;
230 EXPECT_FALSE(new_promo
.CanShow());
233 // Test in range views.
234 for (int i
= 0; i
< max_views_
; ++i
) {
235 new_promo
.views_
= i
;
236 EXPECT_TRUE(new_promo
.CanShow());
238 new_promo
.WritePrefs();
242 NotificationPromo new_promo
;
243 new_promo
.InitFromPrefs(promo_type_
);
244 EXPECT_FALSE(new_promo
.closed_
);
245 EXPECT_TRUE(new_promo
.CanShow());
247 NotificationPromo::HandleClosed(promo_type_
);
248 new_promo
.InitFromPrefs(promo_type_
);
249 EXPECT_TRUE(new_promo
.closed_
);
250 EXPECT_FALSE(new_promo
.CanShow());
252 new_promo
.closed_
= false;
253 EXPECT_TRUE(new_promo
.CanShow());
254 new_promo
.WritePrefs();
257 void TestPromoText() {
258 notification_promo_
.promo_text_
.clear();
259 EXPECT_FALSE(notification_promo_
.CanShow());
261 notification_promo_
.promo_text_
= promo_text_
;
262 EXPECT_TRUE(notification_promo_
.CanShow());
266 const double now
= base::Time::Now().ToDoubleT();
267 const double qhour
= 15 * 60;
269 notification_promo_
.group_
= 0; // For simplicity.
271 notification_promo_
.start_
= now
- qhour
;
272 notification_promo_
.end_
= now
+ qhour
;
273 EXPECT_TRUE(notification_promo_
.CanShow());
275 // Start time has not arrived.
276 notification_promo_
.start_
= now
+ qhour
;
277 notification_promo_
.end_
= now
+ qhour
;
278 EXPECT_FALSE(notification_promo_
.CanShow());
280 // End time has past.
281 notification_promo_
.start_
= now
- qhour
;
282 notification_promo_
.end_
= now
- qhour
;
283 EXPECT_FALSE(notification_promo_
.CanShow());
285 notification_promo_
.start_
= start_
;
286 notification_promo_
.end_
= end_
;
287 EXPECT_TRUE(notification_promo_
.CanShow());
290 void TestIncrement() {
291 const double now
= base::Time::Now().ToDoubleT();
292 const double slice
= 60;
294 notification_promo_
.num_groups_
= 18;
295 notification_promo_
.initial_segment_
= 5;
296 notification_promo_
.increment_
= 3;
297 notification_promo_
.time_slice_
= slice
;
299 notification_promo_
.start_
= now
- 1;
300 notification_promo_
.end_
= now
+ slice
;
302 // Test initial segment.
303 notification_promo_
.group_
= 4;
304 EXPECT_TRUE(notification_promo_
.CanShow());
305 notification_promo_
.group_
= 5;
306 EXPECT_FALSE(notification_promo_
.CanShow());
308 // Test first increment.
309 notification_promo_
.start_
-= slice
;
310 notification_promo_
.group_
= 7;
311 EXPECT_TRUE(notification_promo_
.CanShow());
312 notification_promo_
.group_
= 8;
313 EXPECT_FALSE(notification_promo_
.CanShow());
315 // Test second increment.
316 notification_promo_
.start_
-= slice
;
317 notification_promo_
.group_
= 10;
318 EXPECT_TRUE(notification_promo_
.CanShow());
319 notification_promo_
.group_
= 11;
320 EXPECT_FALSE(notification_promo_
.CanShow());
322 // Test penultimate increment.
323 notification_promo_
.start_
-= 2 * slice
;
324 notification_promo_
.group_
= 16;
325 EXPECT_TRUE(notification_promo_
.CanShow());
326 notification_promo_
.group_
= 17;
327 EXPECT_FALSE(notification_promo_
.CanShow());
329 // Test last increment.
330 notification_promo_
.start_
-= slice
;
331 EXPECT_TRUE(notification_promo_
.CanShow());
334 const NotificationPromo
& promo() const { return notification_promo_
; }
337 NotificationPromo notification_promo_
;
338 bool received_notification_
;
339 scoped_ptr
<base::DictionaryValue
> test_json_
;
341 NotificationPromo::PromoType promo_type_
;
342 std::string promo_text_
;
348 int initial_segment_
;
358 // Test that everything gets parsed correctly, notifications are sent,
359 // and CanShow() is handled correctly under variety of conditions.
360 // Additionally, test that the first string in |strings| is used if
361 // no payload.promo_short_message is specified in the JSON response.
362 TEST_F(PromoResourceServiceTest
, NotificationPromoTest
) {
363 // Check that prefs are set correctly.
364 NotificationPromoTest promo_test
;
366 // Set up start date and promo line in a Dictionary as if parsed from the
367 // service. date[0].end is replaced with a date 1 year in the future.
369 " \"ntp_notification_promo\": ["
374 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
380 " \"NTP4_HOW_DO_YOU_FEEL_ABOUT_CHROME\":"
381 " \"What do you think of Chrome?\""
387 " \"increment\":100,"
388 " \"increment_frequency\":3600,"
389 " \"increment_max\":400"
393 " \"days_active\":7,"
394 " \"install_age_days\":21"
400 "What do you think of Chrome?",
401 // The starting date is in 1999 to make tests pass
402 // on Android devices with incorrect or unset date/time.
403 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
404 1000, 200, 100, 3600, 400, 30);
406 promo_test
.InitPromoFromJson(true);
408 // Second time should not trigger a notification.
409 promo_test
.InitPromoFromJson(false);
411 promo_test
.TestInitFromPrefs();
413 // Test various conditions of CanShow.
414 // TestGroup Has the side effect of setting us to a passing group.
415 promo_test
.TestGroup();
416 promo_test
.TestViews();
417 promo_test
.TestClosed();
418 promo_test
.TestPromoText();
419 promo_test
.TestTime();
420 promo_test
.TestIncrement();
423 // Test that payload.promo_message_short is used if present.
424 TEST_F(PromoResourceServiceTest
, NotificationPromoCompatNoStringsTest
) {
425 // Check that prefs are set correctly.
426 NotificationPromoTest promo_test
;
428 // Set up start date and promo line in a Dictionary as if parsed from the
429 // service. date[0].end is replaced with a date 1 year in the future.
431 " \"ntp_notification_promo\": ["
436 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
444 " \"increment\":100,"
445 " \"increment_frequency\":3600,"
446 " \"increment_max\":400"
450 " \"promo_message_short\":"
451 " \"What do you think of Chrome?\","
452 " \"days_active\":7,"
453 " \"install_age_days\":21"
459 "What do you think of Chrome?",
460 // The starting date is in 1999 to make tests pass
461 // on Android devices with incorrect or unset date/time.
462 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
463 1000, 200, 100, 3600, 400, 30);
465 promo_test
.InitPromoFromJson(true);
466 // Second time should not trigger a notification.
467 promo_test
.InitPromoFromJson(false);
468 promo_test
.TestInitFromPrefs();
471 // Test that strings.|payload.promo_message_short| is used if present.
472 TEST_F(PromoResourceServiceTest
, NotificationPromoCompatPayloadStringsTest
) {
473 // Check that prefs are set correctly.
474 NotificationPromoTest promo_test
;
476 // Set up start date and promo line in a Dictionary as if parsed from the
477 // service. date[0].end is replaced with a date 1 year in the future.
479 " \"ntp_notification_promo\": ["
484 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
492 " \"increment\":100,"
493 " \"increment_frequency\":3600,"
494 " \"increment_max\":400"
498 " \"bogus\":\"string\","
500 " \"What do you think of Chrome?\""
504 " \"promo_message_short\":"
506 " \"days_active\":7,"
507 " \"install_age_days\":21"
513 "What do you think of Chrome?",
514 // The starting date is in 1999 to make tests pass
515 // on Android devices with incorrect or unset date/time.
516 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
517 1000, 200, 100, 3600, 400, 30);
519 promo_test
.InitPromoFromJson(true);
520 // Second time should not trigger a notification.
521 promo_test
.InitPromoFromJson(false);
522 promo_test
.TestInitFromPrefs();
525 TEST_F(PromoResourceServiceTest
, PromoServerURLTest
) {
526 GURL promo_server_url
= NotificationPromo::PromoServerURL();
527 EXPECT_FALSE(promo_server_url
.is_empty());
528 EXPECT_TRUE(promo_server_url
.is_valid());
529 EXPECT_TRUE(promo_server_url
.SchemeIs(url::kHttpsScheme
));
530 // TODO(achuith): Test this better.
533 #if defined(ENABLE_APP_LIST)
534 TEST_F(PromoResourceServiceTest
, AppLauncherPromoTest
) {
535 // Check that prefs are set correctly.
536 NotificationPromoTest promo_test
;
538 // Set up start date and promo line in a Dictionary as if parsed from the
539 // service. date[0].end is replaced with a date 1 year in the future.
541 " \"ntp_notification_promo\": ["
546 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
554 " \"increment\":100,"
555 " \"increment_frequency\":3600,"
556 " \"increment_max\":400"
560 " \"promo_message_short\":"
561 " \"What do you think of Chrome?\","
562 " \"days_active\":7,"
563 " \"install_age_days\":21,"
564 " \"is_app_launcher_promo\":true"
570 "What do you think of Chrome?",
571 // The starting date is in 1999 to make tests pass
572 // on Android devices with incorrect or unset date/time.
573 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
574 1000, 200, 100, 3600, 400, 30);
575 promo_test
.InitPromoFromJson(true);
576 local_state_
.Get()->SetBoolean(prefs::kAppLauncherHasBeenEnabled
, true);
577 EXPECT_FALSE(promo_test
.promo().CanShow());