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/chrome_notification_types.h"
18 #include "chrome/browser/prefs/browser_prefs.h"
19 #include "chrome/browser/web_resource/notification_promo.h"
20 #include "chrome/browser/web_resource/promo_resource_service.h"
21 #include "chrome/common/pref_names.h"
22 #include "chrome/common/url_constants.h"
23 #include "chrome/test/base/scoped_testing_local_state.h"
24 #include "chrome/test/base/testing_browser_process.h"
25 #include "content/public/browser/notification_registrar.h"
26 #include "content/public/browser/notification_service.h"
27 #include "net/url_request/test_url_fetcher_factory.h"
28 #include "testing/gtest/include/gtest/gtest.h"
29 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
33 const char kDateFormat
[] = "dd MMM yyyy HH:mm:ss zzz";
35 bool YearFromNow(double* date_epoch
, std::string
* date_string
) {
36 *date_epoch
= (base::Time::Now() + base::TimeDelta::FromDays(365)).ToTimeT();
38 UErrorCode status
= U_ZERO_ERROR
;
39 icu::SimpleDateFormat
simple_formatter(icu::UnicodeString(kDateFormat
),
42 if (!U_SUCCESS(status
))
45 icu::UnicodeString date_unicode_string
;
46 simple_formatter
.format(static_cast<UDate
>(*date_epoch
* 1000),
49 if (!U_SUCCESS(status
))
52 return base::UTF16ToUTF8(date_unicode_string
.getBuffer(),
53 static_cast<size_t>(date_unicode_string
.length()),
59 class PromoResourceServiceTest
: public testing::Test
{
61 // |promo_resource_service_| must be created after |local_state_|.
62 PromoResourceServiceTest()
63 : local_state_(TestingBrowserProcess::GetGlobal()),
64 promo_resource_service_(new PromoResourceService
) {}
67 ScopedTestingLocalState local_state_
;
68 scoped_refptr
<PromoResourceService
> promo_resource_service_
;
69 base::MessageLoop loop_
;
72 class NotificationPromoTest
{
74 NotificationPromoTest()
75 : received_notification_(false),
86 void Init(const std::string
& json
,
87 const std::string
& promo_text
,
89 int num_groups
, int initial_segment
, int increment
,
90 int time_slice
, int max_group
, int max_views
) {
91 double year_from_now_epoch
;
92 std::string year_from_now_string
;
93 ASSERT_TRUE(YearFromNow(&year_from_now_epoch
, &year_from_now_string
));
95 std::vector
<std::string
> replacements
;
96 replacements
.push_back(year_from_now_string
);
98 std::string
json_with_end_date(
99 ReplaceStringPlaceholders(json
, replacements
, NULL
));
100 base::Value
* value(base::JSONReader::Read(json_with_end_date
));
103 base::DictionaryValue
* dict
= NULL
;
104 value
->GetAsDictionary(&dict
);
106 test_json_
.reset(dict
);
108 promo_type_
= NotificationPromo::NTP_NOTIFICATION_PROMO
;
109 promo_text_
= promo_text
;
112 end_
= year_from_now_epoch
;
114 num_groups_
= num_groups
;
115 initial_segment_
= initial_segment
;
116 increment_
= increment
;
117 time_slice_
= time_slice
;
118 max_group_
= max_group
;
120 max_views_
= max_views
;
123 received_notification_
= false;
126 void InitPromoFromJson(bool should_receive_notification
) {
127 notification_promo_
.InitFromJson(*test_json_
, promo_type_
);
128 EXPECT_EQ(should_receive_notification
,
129 notification_promo_
.new_notification());
135 void TestNotification() {
137 EXPECT_EQ(notification_promo_
.promo_text_
, promo_text_
);
139 EXPECT_EQ(notification_promo_
.start_
, start_
);
140 EXPECT_EQ(notification_promo_
.end_
, end_
);
142 EXPECT_EQ(notification_promo_
.num_groups_
, num_groups_
);
143 EXPECT_EQ(notification_promo_
.initial_segment_
, initial_segment_
);
144 EXPECT_EQ(notification_promo_
.increment_
, increment_
);
145 EXPECT_EQ(notification_promo_
.time_slice_
, time_slice_
);
146 EXPECT_EQ(notification_promo_
.max_group_
, max_group_
);
148 EXPECT_EQ(notification_promo_
.max_views_
, max_views_
);
149 EXPECT_EQ(notification_promo_
.closed_
, closed_
);
151 // Check group within bounds.
152 EXPECT_GE(notification_promo_
.group_
, 0);
153 EXPECT_LT(notification_promo_
.group_
, num_groups_
);
155 // Views should be 0 for now.
156 EXPECT_EQ(notification_promo_
.views_
, 0);
159 // Create a new NotificationPromo from prefs and compare to current
161 void TestInitFromPrefs() {
162 NotificationPromo prefs_notification_promo
;
163 prefs_notification_promo
.InitFromPrefs(promo_type_
);
165 EXPECT_EQ(notification_promo_
.prefs_
,
166 prefs_notification_promo
.prefs_
);
167 EXPECT_EQ(notification_promo_
.promo_text_
,
168 prefs_notification_promo
.promo_text_
);
169 EXPECT_EQ(notification_promo_
.start_
,
170 prefs_notification_promo
.start_
);
171 EXPECT_EQ(notification_promo_
.end_
,
172 prefs_notification_promo
.end_
);
173 EXPECT_EQ(notification_promo_
.num_groups_
,
174 prefs_notification_promo
.num_groups_
);
175 EXPECT_EQ(notification_promo_
.initial_segment_
,
176 prefs_notification_promo
.initial_segment_
);
177 EXPECT_EQ(notification_promo_
.increment_
,
178 prefs_notification_promo
.increment_
);
179 EXPECT_EQ(notification_promo_
.time_slice_
,
180 prefs_notification_promo
.time_slice_
);
181 EXPECT_EQ(notification_promo_
.max_group_
,
182 prefs_notification_promo
.max_group_
);
183 EXPECT_EQ(notification_promo_
.max_views_
,
184 prefs_notification_promo
.max_views_
);
185 EXPECT_EQ(notification_promo_
.group_
,
186 prefs_notification_promo
.group_
);
187 EXPECT_EQ(notification_promo_
.views_
,
188 prefs_notification_promo
.views_
);
189 EXPECT_EQ(notification_promo_
.closed_
,
190 prefs_notification_promo
.closed_
);
194 // Test out of range groups.
195 const int incr
= num_groups_
/ 20;
196 for (int i
= max_group_
; i
< num_groups_
; i
+= incr
) {
197 notification_promo_
.group_
= i
;
198 EXPECT_FALSE(notification_promo_
.CanShow());
201 // Test in-range groups.
202 for (int i
= 0; i
< max_group_
; i
+= incr
) {
203 notification_promo_
.group_
= i
;
204 EXPECT_TRUE(notification_promo_
.CanShow());
207 // When max_group_ is 0, all groups pass.
208 notification_promo_
.max_group_
= 0;
209 for (int i
= 0; i
< num_groups_
; i
+= incr
) {
210 notification_promo_
.group_
= i
;
211 EXPECT_TRUE(notification_promo_
.CanShow());
213 notification_promo_
.WritePrefs();
217 notification_promo_
.views_
= notification_promo_
.max_views_
- 2;
218 notification_promo_
.WritePrefs();
220 NotificationPromo::HandleViewed(promo_type_
);
221 NotificationPromo new_promo
;
222 new_promo
.InitFromPrefs(promo_type_
);
223 EXPECT_EQ(new_promo
.max_views_
- 1, new_promo
.views_
);
224 EXPECT_TRUE(new_promo
.CanShow());
225 NotificationPromo::HandleViewed(promo_type_
);
226 new_promo
.InitFromPrefs(promo_type_
);
227 EXPECT_EQ(new_promo
.max_views_
, new_promo
.views_
);
228 EXPECT_FALSE(new_promo
.CanShow());
230 // Test out of range views.
231 for (int i
= max_views_
; i
< max_views_
* 2; ++i
) {
232 new_promo
.views_
= i
;
233 EXPECT_FALSE(new_promo
.CanShow());
236 // Test in range views.
237 for (int i
= 0; i
< max_views_
; ++i
) {
238 new_promo
.views_
= i
;
239 EXPECT_TRUE(new_promo
.CanShow());
241 new_promo
.WritePrefs();
245 NotificationPromo new_promo
;
246 new_promo
.InitFromPrefs(promo_type_
);
247 EXPECT_FALSE(new_promo
.closed_
);
248 EXPECT_TRUE(new_promo
.CanShow());
250 NotificationPromo::HandleClosed(promo_type_
);
251 new_promo
.InitFromPrefs(promo_type_
);
252 EXPECT_TRUE(new_promo
.closed_
);
253 EXPECT_FALSE(new_promo
.CanShow());
255 new_promo
.closed_
= false;
256 EXPECT_TRUE(new_promo
.CanShow());
257 new_promo
.WritePrefs();
260 void TestPromoText() {
261 notification_promo_
.promo_text_
.clear();
262 EXPECT_FALSE(notification_promo_
.CanShow());
264 notification_promo_
.promo_text_
= promo_text_
;
265 EXPECT_TRUE(notification_promo_
.CanShow());
269 const double now
= base::Time::Now().ToDoubleT();
270 const double qhour
= 15 * 60;
272 notification_promo_
.group_
= 0; // For simplicity.
274 notification_promo_
.start_
= now
- qhour
;
275 notification_promo_
.end_
= now
+ qhour
;
276 EXPECT_TRUE(notification_promo_
.CanShow());
278 // Start time has not arrived.
279 notification_promo_
.start_
= now
+ qhour
;
280 notification_promo_
.end_
= now
+ qhour
;
281 EXPECT_FALSE(notification_promo_
.CanShow());
283 // End time has past.
284 notification_promo_
.start_
= now
- qhour
;
285 notification_promo_
.end_
= now
- qhour
;
286 EXPECT_FALSE(notification_promo_
.CanShow());
288 notification_promo_
.start_
= start_
;
289 notification_promo_
.end_
= end_
;
290 EXPECT_TRUE(notification_promo_
.CanShow());
293 void TestIncrement() {
294 const double now
= base::Time::Now().ToDoubleT();
295 const double slice
= 60;
297 notification_promo_
.num_groups_
= 18;
298 notification_promo_
.initial_segment_
= 5;
299 notification_promo_
.increment_
= 3;
300 notification_promo_
.time_slice_
= slice
;
302 notification_promo_
.start_
= now
- 1;
303 notification_promo_
.end_
= now
+ slice
;
305 // Test initial segment.
306 notification_promo_
.group_
= 4;
307 EXPECT_TRUE(notification_promo_
.CanShow());
308 notification_promo_
.group_
= 5;
309 EXPECT_FALSE(notification_promo_
.CanShow());
311 // Test first increment.
312 notification_promo_
.start_
-= slice
;
313 notification_promo_
.group_
= 7;
314 EXPECT_TRUE(notification_promo_
.CanShow());
315 notification_promo_
.group_
= 8;
316 EXPECT_FALSE(notification_promo_
.CanShow());
318 // Test second increment.
319 notification_promo_
.start_
-= slice
;
320 notification_promo_
.group_
= 10;
321 EXPECT_TRUE(notification_promo_
.CanShow());
322 notification_promo_
.group_
= 11;
323 EXPECT_FALSE(notification_promo_
.CanShow());
325 // Test penultimate increment.
326 notification_promo_
.start_
-= 2 * slice
;
327 notification_promo_
.group_
= 16;
328 EXPECT_TRUE(notification_promo_
.CanShow());
329 notification_promo_
.group_
= 17;
330 EXPECT_FALSE(notification_promo_
.CanShow());
332 // Test last increment.
333 notification_promo_
.start_
-= slice
;
334 EXPECT_TRUE(notification_promo_
.CanShow());
337 const NotificationPromo
& promo() const { return notification_promo_
; }
340 NotificationPromo notification_promo_
;
341 bool received_notification_
;
342 scoped_ptr
<base::DictionaryValue
> test_json_
;
344 NotificationPromo::PromoType promo_type_
;
345 std::string promo_text_
;
351 int initial_segment_
;
361 // Test that everything gets parsed correctly, notifications are sent,
362 // and CanShow() is handled correctly under variety of conditions.
363 // Additionally, test that the first string in |strings| is used if
364 // no payload.promo_short_message is specified in the JSON response.
365 TEST_F(PromoResourceServiceTest
, NotificationPromoTest
) {
366 // Check that prefs are set correctly.
367 NotificationPromoTest promo_test
;
369 // Set up start date and promo line in a Dictionary as if parsed from the
370 // service. date[0].end is replaced with a date 1 year in the future.
372 " \"ntp_notification_promo\": ["
377 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
383 " \"NTP4_HOW_DO_YOU_FEEL_ABOUT_CHROME\":"
384 " \"What do you think of Chrome?\""
390 " \"increment\":100,"
391 " \"increment_frequency\":3600,"
392 " \"increment_max\":400"
396 " \"days_active\":7,"
397 " \"install_age_days\":21"
403 "What do you think of Chrome?",
404 // The starting date is in 1999 to make tests pass
405 // on Android devices with incorrect or unset date/time.
406 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
407 1000, 200, 100, 3600, 400, 30);
409 promo_test
.InitPromoFromJson(true);
411 // Second time should not trigger a notification.
412 promo_test
.InitPromoFromJson(false);
414 promo_test
.TestInitFromPrefs();
416 // Test various conditions of CanShow.
417 // TestGroup Has the side effect of setting us to a passing group.
418 promo_test
.TestGroup();
419 promo_test
.TestViews();
420 promo_test
.TestClosed();
421 promo_test
.TestPromoText();
422 promo_test
.TestTime();
423 promo_test
.TestIncrement();
426 // Test that payload.promo_message_short is used if present.
427 TEST_F(PromoResourceServiceTest
, NotificationPromoCompatNoStringsTest
) {
428 // Check that prefs are set correctly.
429 NotificationPromoTest promo_test
;
431 // Set up start date and promo line in a Dictionary as if parsed from the
432 // service. date[0].end is replaced with a date 1 year in the future.
434 " \"ntp_notification_promo\": ["
439 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
447 " \"increment\":100,"
448 " \"increment_frequency\":3600,"
449 " \"increment_max\":400"
453 " \"promo_message_short\":"
454 " \"What do you think of Chrome?\","
455 " \"days_active\":7,"
456 " \"install_age_days\":21"
462 "What do you think of Chrome?",
463 // The starting date is in 1999 to make tests pass
464 // on Android devices with incorrect or unset date/time.
465 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
466 1000, 200, 100, 3600, 400, 30);
468 promo_test
.InitPromoFromJson(true);
469 // Second time should not trigger a notification.
470 promo_test
.InitPromoFromJson(false);
471 promo_test
.TestInitFromPrefs();
474 // Test that strings.|payload.promo_message_short| is used if present.
475 TEST_F(PromoResourceServiceTest
, NotificationPromoCompatPayloadStringsTest
) {
476 // Check that prefs are set correctly.
477 NotificationPromoTest promo_test
;
479 // Set up start date and promo line in a Dictionary as if parsed from the
480 // service. date[0].end is replaced with a date 1 year in the future.
482 " \"ntp_notification_promo\": ["
487 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
495 " \"increment\":100,"
496 " \"increment_frequency\":3600,"
497 " \"increment_max\":400"
501 " \"bogus\":\"string\","
503 " \"What do you think of Chrome?\""
507 " \"promo_message_short\":"
509 " \"days_active\":7,"
510 " \"install_age_days\":21"
516 "What do you think of Chrome?",
517 // The starting date is in 1999 to make tests pass
518 // on Android devices with incorrect or unset date/time.
519 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
520 1000, 200, 100, 3600, 400, 30);
522 promo_test
.InitPromoFromJson(true);
523 // Second time should not trigger a notification.
524 promo_test
.InitPromoFromJson(false);
525 promo_test
.TestInitFromPrefs();
528 TEST_F(PromoResourceServiceTest
, PromoServerURLTest
) {
529 GURL promo_server_url
= NotificationPromo::PromoServerURL();
530 EXPECT_FALSE(promo_server_url
.is_empty());
531 EXPECT_TRUE(promo_server_url
.is_valid());
532 EXPECT_TRUE(promo_server_url
.SchemeIs(content::kHttpsScheme
));
533 // TODO(achuith): Test this better.
536 #if defined(ENABLE_APP_LIST)
537 TEST_F(PromoResourceServiceTest
, AppLauncherPromoTest
) {
538 // Check that prefs are set correctly.
539 NotificationPromoTest promo_test
;
541 // Set up start date and promo line in a Dictionary as if parsed from the
542 // service. date[0].end is replaced with a date 1 year in the future.
544 " \"ntp_notification_promo\": ["
549 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
557 " \"increment\":100,"
558 " \"increment_frequency\":3600,"
559 " \"increment_max\":400"
563 " \"promo_message_short\":"
564 " \"What do you think of Chrome?\","
565 " \"days_active\":7,"
566 " \"install_age_days\":21,"
567 " \"is_app_launcher_promo\":true"
573 "What do you think of Chrome?",
574 // The starting date is in 1999 to make tests pass
575 // on Android devices with incorrect or unset date/time.
576 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
577 1000, 200, 100, 3600, 400, 30);
578 promo_test
.InitPromoFromJson(true);
579 local_state_
.Get()->SetBoolean(prefs::kAppLauncherIsEnabled
, true);
580 EXPECT_FALSE(promo_test
.promo().CanShow());