ExtensionSyncService: listen for relevant changes instead of being explicitly called...
[chromium-blink-merge.git] / chrome / browser / web_resource / promo_resource_service_unittest.cc
blob6d7c42643768c4d72fcda655d9e349819a8253e4
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/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"
28 namespace {
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),
39 icu::Locale("en_US"),
40 status);
41 if (!U_SUCCESS(status))
42 return false;
44 icu::UnicodeString date_unicode_string;
45 simple_formatter.format(static_cast<UDate>(*date_epoch * 1000),
46 date_unicode_string,
47 status);
48 if (!U_SUCCESS(status))
49 return false;
51 return base::UTF16ToUTF8(date_unicode_string.getBuffer(),
52 static_cast<size_t>(date_unicode_string.length()),
53 date_string);
56 } // namespace
58 class PromoResourceServiceTest : public testing::Test {
59 public:
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(),
65 kChannel,
66 "en",
67 nullptr,
68 nullptr,
69 web_resource::WebResourceService::ParseJSONCallback())) {}
71 protected:
72 ScopedTestingLocalState local_state_;
73 scoped_ptr<PromoResourceService> promo_resource_service_;
74 base::MessageLoop loop_;
77 class NotificationPromoTest {
78 public:
79 explicit NotificationPromoTest(PrefService* local_state)
80 : local_state_(local_state),
81 notification_promo_(local_state_),
82 received_notification_(false),
83 start_(0.0),
84 end_(0.0),
85 num_groups_(0),
86 initial_segment_(0),
87 increment_(1),
88 time_slice_(0),
89 max_group_(0),
90 max_views_(0),
91 closed_(false) {}
93 void Init(const std::string& json,
94 const std::string& promo_text,
95 double start,
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));
108 ASSERT_TRUE(value);
110 base::DictionaryValue* dict = NULL;
111 value->GetAsDictionary(&dict);
112 ASSERT_TRUE(dict);
113 test_json_.reset(dict);
115 promo_type_ = NotificationPromo::NTP_NOTIFICATION_PROMO;
116 promo_text_ = promo_text;
118 start_ = start;
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;
129 closed_ = false;
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());
138 // Test the fields.
139 TestNotification();
142 void TestNotification() {
143 // Check values.
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
167 // notification.
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_);
200 void TestGroup() {
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();
223 void TestViews() {
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();
251 void TestClosed() {
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());
275 void TestTime() {
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_; }
346 private:
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_;
355 double start_;
356 double end_;
358 int num_groups_;
359 int initial_segment_;
360 int increment_;
361 int time_slice_;
362 int max_group_;
364 int max_views_;
366 bool closed_;
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.
379 promo_test.Init("{"
380 " \"ntp_notification_promo\": ["
381 " {"
382 " \"date\":"
383 " ["
384 " {"
385 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
386 " \"end\":\"$1\""
387 " }"
388 " ],"
389 " \"strings\":"
390 " {"
391 " \"NTP4_HOW_DO_YOU_FEEL_ABOUT_CHROME\":"
392 " \"What do you think of Chrome?\""
393 " },"
394 " \"grouping\":"
395 " {"
396 " \"buckets\":1000,"
397 " \"segment\":200,"
398 " \"increment\":100,"
399 " \"increment_frequency\":3600,"
400 " \"increment_max\":400"
401 " },"
402 " \"payload\":"
403 " {"
404 " \"days_active\":7,"
405 " \"install_age_days\":21"
406 " },"
407 " \"max_views\":30"
408 " }"
409 " ]"
410 "}",
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.
441 promo_test.Init("{"
442 " \"ntp_notification_promo\": ["
443 " {"
444 " \"date\":"
445 " ["
446 " {"
447 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
448 " \"end\":\"$1\""
449 " }"
450 " ],"
451 " \"grouping\":"
452 " {"
453 " \"buckets\":1000,"
454 " \"segment\":200,"
455 " \"increment\":100,"
456 " \"increment_frequency\":3600,"
457 " \"increment_max\":400"
458 " },"
459 " \"payload\":"
460 " {"
461 " \"promo_message_short\":"
462 " \"What do you think of Chrome?\","
463 " \"days_active\":7,"
464 " \"install_age_days\":21"
465 " },"
466 " \"max_views\":30"
467 " }"
468 " ]"
469 "}",
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.
489 promo_test.Init("{"
490 " \"ntp_notification_promo\": ["
491 " {"
492 " \"date\":"
493 " ["
494 " {"
495 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
496 " \"end\":\"$1\""
497 " }"
498 " ],"
499 " \"grouping\":"
500 " {"
501 " \"buckets\":1000,"
502 " \"segment\":200,"
503 " \"increment\":100,"
504 " \"increment_frequency\":3600,"
505 " \"increment_max\":400"
506 " },"
507 " \"strings\":"
508 " {"
509 " \"bogus\":\"string\","
510 " \"GOOD_STRING\":"
511 " \"What do you think of Chrome?\""
512 " },"
513 " \"payload\":"
514 " {"
515 " \"promo_message_short\":"
516 " \"GOOD_STRING\","
517 " \"days_active\":7,"
518 " \"install_age_days\":21"
519 " },"
520 " \"max_views\":30"
521 " }"
522 " ]"
523 "}",
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.
551 promo_test.Init("{"
552 " \"ntp_notification_promo\": ["
553 " {"
554 " \"date\":"
555 " ["
556 " {"
557 " \"start\":\"3 Aug 1999 9:26:06 GMT\","
558 " \"end\":\"$1\""
559 " }"
560 " ],"
561 " \"grouping\":"
562 " {"
563 " \"buckets\":1000,"
564 " \"segment\":200,"
565 " \"increment\":100,"
566 " \"increment_frequency\":3600,"
567 " \"increment_max\":400"
568 " },"
569 " \"payload\":"
570 " {"
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"
576 " },"
577 " \"max_views\":30"
578 " }"
579 " ]"
580 "}",
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());
590 #endif