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/test/base/scoped_testing_local_state.h"
22 #include "chrome/test/base/testing_browser_process.h"
23 #include "components/version_info/version_info.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 version_info::Channel kChannel
= version_info::Channel::UNKNOWN
;
32 const char kDateFormat
[] = "dd MMM yyyy HH:mm:ss zzzz";
34 bool YearFromNow(double* date_epoch
, std::string
* date_string
) {
35 *date_epoch
= (base::Time::Now() + base::TimeDelta::FromDays(365)).ToTimeT();
37 UErrorCode status
= U_ZERO_ERROR
;
38 icu::SimpleDateFormat
simple_formatter(icu::UnicodeString(kDateFormat
),
41 if (!U_SUCCESS(status
))
44 icu::UnicodeString date_unicode_string
;
45 simple_formatter
.format(static_cast<UDate
>(*date_epoch
* 1000),
48 if (!U_SUCCESS(status
))
51 return base::UTF16ToUTF8(date_unicode_string
.getBuffer(),
52 static_cast<size_t>(date_unicode_string
.length()),
58 class PromoResourceServiceTest
: public testing::Test
{
60 // |promo_resource_service_| must be created after |local_state_|.
61 PromoResourceServiceTest()
62 : local_state_(TestingBrowserProcess::GetGlobal()),
63 promo_resource_service_(new PromoResourceService(
64 g_browser_process
->local_state(),
69 web_resource::WebResourceService::ParseJSONCallback())) {}
72 ScopedTestingLocalState local_state_
;
73 scoped_ptr
<PromoResourceService
> promo_resource_service_
;
74 base::MessageLoop loop_
;
77 class NotificationPromoTest
{
79 explicit NotificationPromoTest(PrefService
* local_state
)
80 : local_state_(local_state
),
81 notification_promo_(local_state_
),
82 received_notification_(false),
93 void Init(const std::string
& json
,
94 const std::string
& promo_text
,
96 int num_groups
, int initial_segment
, int increment
,
97 int time_slice
, int max_group
, int max_views
) {
98 double year_from_now_epoch
;
99 std::string year_from_now_string
;
100 ASSERT_TRUE(YearFromNow(&year_from_now_epoch
, &year_from_now_string
));
102 std::vector
<std::string
> replacements
;
103 replacements
.push_back(year_from_now_string
);
105 std::string
json_with_end_date(
106 base::ReplaceStringPlaceholders(json
, replacements
, NULL
));
107 base::Value
* value(base::JSONReader::DeprecatedRead(json_with_end_date
));
110 base::DictionaryValue
* dict
= NULL
;
111 value
->GetAsDictionary(&dict
);
113 test_json_
.reset(dict
);
115 promo_type_
= NotificationPromo::NTP_NOTIFICATION_PROMO
;
116 promo_text_
= promo_text
;
119 end_
= year_from_now_epoch
;
121 num_groups_
= num_groups
;
122 initial_segment_
= initial_segment
;
123 increment_
= increment
;
124 time_slice_
= time_slice
;
125 max_group_
= max_group
;
127 max_views_
= max_views
;
130 received_notification_
= false;
133 void InitPromoFromJson(bool should_receive_notification
) {
134 notification_promo_
.InitFromJson(*test_json_
, promo_type_
);
135 EXPECT_EQ(should_receive_notification
,
136 notification_promo_
.new_notification());
142 void TestNotification() {
144 EXPECT_EQ(notification_promo_
.promo_text_
, promo_text_
);
146 EXPECT_EQ(notification_promo_
.start_
, start_
);
147 EXPECT_EQ(notification_promo_
.end_
, end_
);
149 EXPECT_EQ(notification_promo_
.num_groups_
, num_groups_
);
150 EXPECT_EQ(notification_promo_
.initial_segment_
, initial_segment_
);
151 EXPECT_EQ(notification_promo_
.increment_
, increment_
);
152 EXPECT_EQ(notification_promo_
.time_slice_
, time_slice_
);
153 EXPECT_EQ(notification_promo_
.max_group_
, max_group_
);
155 EXPECT_EQ(notification_promo_
.max_views_
, max_views_
);
156 EXPECT_EQ(notification_promo_
.closed_
, closed_
);
158 // Check group within bounds.
159 EXPECT_GE(notification_promo_
.group_
, 0);
160 EXPECT_LT(notification_promo_
.group_
, num_groups_
);
162 // Views should be 0 for now.
163 EXPECT_EQ(notification_promo_
.views_
, 0);
166 // Create a new NotificationPromo from prefs and compare to current
168 void TestInitFromPrefs() {
169 NotificationPromo
prefs_notification_promo(local_state_
);
170 prefs_notification_promo
.InitFromPrefs(promo_type_
);
172 EXPECT_EQ(notification_promo_
.local_state_
,
173 prefs_notification_promo
.local_state_
);
174 EXPECT_EQ(notification_promo_
.promo_text_
,
175 prefs_notification_promo
.promo_text_
);
176 EXPECT_EQ(notification_promo_
.start_
,
177 prefs_notification_promo
.start_
);
178 EXPECT_EQ(notification_promo_
.end_
,
179 prefs_notification_promo
.end_
);
180 EXPECT_EQ(notification_promo_
.num_groups_
,
181 prefs_notification_promo
.num_groups_
);
182 EXPECT_EQ(notification_promo_
.initial_segment_
,
183 prefs_notification_promo
.initial_segment_
);
184 EXPECT_EQ(notification_promo_
.increment_
,
185 prefs_notification_promo
.increment_
);
186 EXPECT_EQ(notification_promo_
.time_slice_
,
187 prefs_notification_promo
.time_slice_
);
188 EXPECT_EQ(notification_promo_
.max_group_
,
189 prefs_notification_promo
.max_group_
);
190 EXPECT_EQ(notification_promo_
.max_views_
,
191 prefs_notification_promo
.max_views_
);
192 EXPECT_EQ(notification_promo_
.group_
,
193 prefs_notification_promo
.group_
);
194 EXPECT_EQ(notification_promo_
.views_
,
195 prefs_notification_promo
.views_
);
196 EXPECT_EQ(notification_promo_
.closed_
,
197 prefs_notification_promo
.closed_
);
201 // Test out of range groups.
202 const int incr
= num_groups_
/ 20;
203 for (int i
= max_group_
; i
< num_groups_
; i
+= incr
) {
204 notification_promo_
.group_
= i
;
205 EXPECT_FALSE(notification_promo_
.CanShow());
208 // Test in-range groups.
209 for (int i
= 0; i
< max_group_
; i
+= incr
) {
210 notification_promo_
.group_
= i
;
211 EXPECT_TRUE(notification_promo_
.CanShow());
214 // When max_group_ is 0, all groups pass.
215 notification_promo_
.max_group_
= 0;
216 for (int i
= 0; i
< num_groups_
; i
+= incr
) {
217 notification_promo_
.group_
= i
;
218 EXPECT_TRUE(notification_promo_
.CanShow());
220 notification_promo_
.WritePrefs();
224 notification_promo_
.views_
= notification_promo_
.max_views_
- 2;
225 notification_promo_
.WritePrefs();
227 NotificationPromo::HandleViewed(promo_type_
, local_state_
);
228 NotificationPromo
new_promo(local_state_
);
229 new_promo
.InitFromPrefs(promo_type_
);
230 EXPECT_EQ(new_promo
.max_views_
- 1, new_promo
.views_
);
231 EXPECT_TRUE(new_promo
.CanShow());
232 NotificationPromo::HandleViewed(promo_type_
, local_state_
);
233 new_promo
.InitFromPrefs(promo_type_
);
234 EXPECT_EQ(new_promo
.max_views_
, new_promo
.views_
);
235 EXPECT_FALSE(new_promo
.CanShow());
237 // Test out of range views.
238 for (int i
= max_views_
; i
< max_views_
* 2; ++i
) {
239 new_promo
.views_
= i
;
240 EXPECT_FALSE(new_promo
.CanShow());
243 // Test in range views.
244 for (int i
= 0; i
< max_views_
; ++i
) {
245 new_promo
.views_
= i
;
246 EXPECT_TRUE(new_promo
.CanShow());
248 new_promo
.WritePrefs();
252 NotificationPromo
new_promo(local_state_
);
253 new_promo
.InitFromPrefs(promo_type_
);
254 EXPECT_FALSE(new_promo
.closed_
);
255 EXPECT_TRUE(new_promo
.CanShow());
257 NotificationPromo::HandleClosed(promo_type_
, local_state_
);
258 new_promo
.InitFromPrefs(promo_type_
);
259 EXPECT_TRUE(new_promo
.closed_
);
260 EXPECT_FALSE(new_promo
.CanShow());
262 new_promo
.closed_
= false;
263 EXPECT_TRUE(new_promo
.CanShow());
264 new_promo
.WritePrefs();
267 void TestPromoText() {
268 notification_promo_
.promo_text_
.clear();
269 EXPECT_FALSE(notification_promo_
.CanShow());
271 notification_promo_
.promo_text_
= promo_text_
;
272 EXPECT_TRUE(notification_promo_
.CanShow());
276 const double now
= base::Time::Now().ToDoubleT();
277 const double qhour
= 15 * 60;
279 notification_promo_
.group_
= 0; // For simplicity.
281 notification_promo_
.start_
= now
- qhour
;
282 notification_promo_
.end_
= now
+ qhour
;
283 EXPECT_TRUE(notification_promo_
.CanShow());
285 // Start time has not arrived.
286 notification_promo_
.start_
= now
+ qhour
;
287 notification_promo_
.end_
= now
+ qhour
;
288 EXPECT_FALSE(notification_promo_
.CanShow());
290 // End time has past.
291 notification_promo_
.start_
= now
- qhour
;
292 notification_promo_
.end_
= now
- qhour
;
293 EXPECT_FALSE(notification_promo_
.CanShow());
295 notification_promo_
.start_
= start_
;
296 notification_promo_
.end_
= end_
;
297 EXPECT_TRUE(notification_promo_
.CanShow());
300 void TestIncrement() {
301 const double now
= base::Time::Now().ToDoubleT();
302 const double slice
= 60;
304 notification_promo_
.num_groups_
= 18;
305 notification_promo_
.initial_segment_
= 5;
306 notification_promo_
.increment_
= 3;
307 notification_promo_
.time_slice_
= slice
;
309 notification_promo_
.start_
= now
- 1;
310 notification_promo_
.end_
= now
+ slice
;
312 // Test initial segment.
313 notification_promo_
.group_
= 4;
314 EXPECT_TRUE(notification_promo_
.CanShow());
315 notification_promo_
.group_
= 5;
316 EXPECT_FALSE(notification_promo_
.CanShow());
318 // Test first increment.
319 notification_promo_
.start_
-= slice
;
320 notification_promo_
.group_
= 7;
321 EXPECT_TRUE(notification_promo_
.CanShow());
322 notification_promo_
.group_
= 8;
323 EXPECT_FALSE(notification_promo_
.CanShow());
325 // Test second increment.
326 notification_promo_
.start_
-= slice
;
327 notification_promo_
.group_
= 10;
328 EXPECT_TRUE(notification_promo_
.CanShow());
329 notification_promo_
.group_
= 11;
330 EXPECT_FALSE(notification_promo_
.CanShow());
332 // Test penultimate increment.
333 notification_promo_
.start_
-= 2 * slice
;
334 notification_promo_
.group_
= 16;
335 EXPECT_TRUE(notification_promo_
.CanShow());
336 notification_promo_
.group_
= 17;
337 EXPECT_FALSE(notification_promo_
.CanShow());
339 // Test last increment.
340 notification_promo_
.start_
-= slice
;
341 EXPECT_TRUE(notification_promo_
.CanShow());
344 const NotificationPromo
& promo() const { return notification_promo_
; }
347 PrefService
* local_state_
;
348 NotificationPromo notification_promo_
;
349 bool received_notification_
;
350 scoped_ptr
<base::DictionaryValue
> test_json_
;
352 NotificationPromo::PromoType promo_type_
;
353 std::string promo_text_
;
359 int initial_segment_
;
369 // Test that everything gets parsed correctly, notifications are sent,
370 // and CanShow() is handled correctly under variety of conditions.
371 // Additionally, test that the first string in |strings| is used if
372 // no payload.promo_short_message is specified in the JSON response.
373 TEST_F(PromoResourceServiceTest
, NotificationPromoTest
) {
374 // Check that prefs are set correctly.
375 NotificationPromoTest
promo_test(g_browser_process
->local_state());
377 // Set up start date and promo line in a Dictionary as if parsed from the
378 // service. date[0].end is replaced with a date 1 year in the future.
380 " \"ntp_notification_promo\": ["
385 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
391 " \"NTP4_HOW_DO_YOU_FEEL_ABOUT_CHROME\":"
392 " \"What do you think of Chrome?\""
398 " \"increment\":100,"
399 " \"increment_frequency\":3600,"
400 " \"increment_max\":400"
404 " \"days_active\":7,"
405 " \"install_age_days\":21"
411 "What do you think of Chrome?",
412 // The starting date is in 1999 to make tests pass
413 // on Android devices with incorrect or unset date/time.
414 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
415 1000, 200, 100, 3600, 400, 30);
417 promo_test
.InitPromoFromJson(true);
419 // Second time should not trigger a notification.
420 promo_test
.InitPromoFromJson(false);
422 promo_test
.TestInitFromPrefs();
424 // Test various conditions of CanShow.
425 // TestGroup Has the side effect of setting us to a passing group.
426 promo_test
.TestGroup();
427 promo_test
.TestViews();
428 promo_test
.TestClosed();
429 promo_test
.TestPromoText();
430 promo_test
.TestTime();
431 promo_test
.TestIncrement();
434 // Test that payload.promo_message_short is used if present.
435 TEST_F(PromoResourceServiceTest
, NotificationPromoCompatNoStringsTest
) {
436 // Check that prefs are set correctly.
437 NotificationPromoTest
promo_test(g_browser_process
->local_state());
439 // Set up start date and promo line in a Dictionary as if parsed from the
440 // service. date[0].end is replaced with a date 1 year in the future.
442 " \"ntp_notification_promo\": ["
447 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
455 " \"increment\":100,"
456 " \"increment_frequency\":3600,"
457 " \"increment_max\":400"
461 " \"promo_message_short\":"
462 " \"What do you think of Chrome?\","
463 " \"days_active\":7,"
464 " \"install_age_days\":21"
470 "What do you think of Chrome?",
471 // The starting date is in 1999 to make tests pass
472 // on Android devices with incorrect or unset date/time.
473 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
474 1000, 200, 100, 3600, 400, 30);
476 promo_test
.InitPromoFromJson(true);
477 // Second time should not trigger a notification.
478 promo_test
.InitPromoFromJson(false);
479 promo_test
.TestInitFromPrefs();
482 // Test that strings.|payload.promo_message_short| is used if present.
483 TEST_F(PromoResourceServiceTest
, NotificationPromoCompatPayloadStringsTest
) {
484 // Check that prefs are set correctly.
485 NotificationPromoTest
promo_test(g_browser_process
->local_state());
487 // Set up start date and promo line in a Dictionary as if parsed from the
488 // service. date[0].end is replaced with a date 1 year in the future.
490 " \"ntp_notification_promo\": ["
495 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
503 " \"increment\":100,"
504 " \"increment_frequency\":3600,"
505 " \"increment_max\":400"
509 " \"bogus\":\"string\","
511 " \"What do you think of Chrome?\""
515 " \"promo_message_short\":"
517 " \"days_active\":7,"
518 " \"install_age_days\":21"
524 "What do you think of Chrome?",
525 // The starting date is in 1999 to make tests pass
526 // on Android devices with incorrect or unset date/time.
527 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
528 1000, 200, 100, 3600, 400, 30);
530 promo_test
.InitPromoFromJson(true);
531 // Second time should not trigger a notification.
532 promo_test
.InitPromoFromJson(false);
533 promo_test
.TestInitFromPrefs();
536 TEST_F(PromoResourceServiceTest
, PromoServerURLTest
) {
537 GURL promo_server_url
= NotificationPromo::PromoServerURL(kChannel
);
538 EXPECT_FALSE(promo_server_url
.is_empty());
539 EXPECT_TRUE(promo_server_url
.is_valid());
540 EXPECT_TRUE(promo_server_url
.SchemeIs(url::kHttpsScheme
));
541 // TODO(achuith): Test this better.
544 #if defined(ENABLE_APP_LIST)
545 TEST_F(PromoResourceServiceTest
, AppLauncherPromoTest
) {
546 // Check that prefs are set correctly.
547 NotificationPromoTest
promo_test(g_browser_process
->local_state());
549 // Set up start date and promo line in a Dictionary as if parsed from the
550 // service. date[0].end is replaced with a date 1 year in the future.
552 " \"ntp_notification_promo\": ["
557 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
565 " \"increment\":100,"
566 " \"increment_frequency\":3600,"
567 " \"increment_max\":400"
571 " \"promo_message_short\":"
572 " \"What do you think of Chrome?\","
573 " \"days_active\":7,"
574 " \"install_age_days\":21,"
575 " \"is_app_launcher_promo\":true"
581 "What do you think of Chrome?",
582 // The starting date is in 1999 to make tests pass
583 // on Android devices with incorrect or unset date/time.
584 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT.
585 1000, 200, 100, 3600, 400, 30);
586 promo_test
.InitPromoFromJson(true);
587 local_state_
.Get()->SetBoolean(prefs::kAppLauncherHasBeenEnabled
, true);
588 EXPECT_FALSE(promo_test
.promo().CanShow());