| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <utility> |
| #include <vector> |
| |
| #include "apps/pref_names.h" |
| #include "base/json/json_reader.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/prefs/pref_service.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/prefs/browser_prefs.h" |
| #include "chrome/browser/web_resource/notification_promo.h" |
| #include "chrome/browser/web_resource/promo_resource_service.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/url_constants.h" |
| #include "chrome/test/base/scoped_testing_local_state.h" |
| #include "chrome/test/base/testing_browser_process.h" |
| #include "content/public/browser/notification_registrar.h" |
| #include "content/public/browser/notification_service.h" |
| #include "net/url_request/test_url_fetcher_factory.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/icu/source/i18n/unicode/smpdtfmt.h" |
| |
| namespace { |
| |
| const char kDateFormat[] = "dd MMM yyyy HH:mm:ss zzz"; |
| |
| bool YearFromNow(double* date_epoch, std::string* date_string) { |
| *date_epoch = (base::Time::Now() + base::TimeDelta::FromDays(365)).ToTimeT(); |
| |
| UErrorCode status = U_ZERO_ERROR; |
| icu::SimpleDateFormat simple_formatter(icu::UnicodeString(kDateFormat), |
| icu::Locale("en_US"), |
| status); |
| if (!U_SUCCESS(status)) |
| return false; |
| |
| icu::UnicodeString date_unicode_string; |
| simple_formatter.format(static_cast<UDate>(*date_epoch * 1000), |
| date_unicode_string, |
| status); |
| if (!U_SUCCESS(status)) |
| return false; |
| |
| return UTF16ToUTF8(date_unicode_string.getBuffer(), |
| static_cast<size_t>(date_unicode_string.length()), |
| date_string); |
| } |
| |
| } // namespace |
| |
| class PromoResourceServiceTest : public testing::Test { |
| public: |
| // |promo_resource_service_| must be created after |local_state_|. |
| PromoResourceServiceTest() |
| : local_state_(TestingBrowserProcess::GetGlobal()), |
| promo_resource_service_(new PromoResourceService) {} |
| |
| protected: |
| ScopedTestingLocalState local_state_; |
| scoped_refptr<PromoResourceService> promo_resource_service_; |
| base::MessageLoop loop_; |
| }; |
| |
| class NotificationPromoTest { |
| public: |
| NotificationPromoTest() |
| : received_notification_(false), |
| start_(0.0), |
| end_(0.0), |
| num_groups_(0), |
| initial_segment_(0), |
| increment_(1), |
| time_slice_(0), |
| max_group_(0), |
| max_views_(0), |
| closed_(false) {} |
| |
| void Init(const std::string& json, |
| const std::string& promo_text, |
| double start, |
| int num_groups, int initial_segment, int increment, |
| int time_slice, int max_group, int max_views) { |
| double year_from_now_epoch; |
| std::string year_from_now_string; |
| ASSERT_TRUE(YearFromNow(&year_from_now_epoch, &year_from_now_string)); |
| |
| std::vector<std::string> replacements; |
| replacements.push_back(year_from_now_string); |
| |
| std::string json_with_end_date( |
| ReplaceStringPlaceholders(json, replacements, NULL)); |
| Value* value(base::JSONReader::Read(json_with_end_date)); |
| ASSERT_TRUE(value); |
| |
| DictionaryValue* dict = NULL; |
| value->GetAsDictionary(&dict); |
| ASSERT_TRUE(dict); |
| test_json_.reset(dict); |
| |
| promo_type_ = NotificationPromo::NTP_NOTIFICATION_PROMO; |
| promo_text_ = promo_text; |
| |
| start_ = start; |
| end_ = year_from_now_epoch; |
| |
| num_groups_ = num_groups; |
| initial_segment_ = initial_segment; |
| increment_ = increment; |
| time_slice_ = time_slice; |
| max_group_ = max_group; |
| |
| max_views_ = max_views; |
| |
| closed_ = false; |
| received_notification_ = false; |
| } |
| |
| void InitPromoFromJson(bool should_receive_notification) { |
| notification_promo_.InitFromJson(*test_json_, promo_type_); |
| EXPECT_EQ(should_receive_notification, |
| notification_promo_.new_notification()); |
| |
| // Test the fields. |
| TestNotification(); |
| } |
| |
| void TestNotification() { |
| // Check values. |
| EXPECT_EQ(notification_promo_.promo_text_, promo_text_); |
| |
| EXPECT_EQ(notification_promo_.start_, start_); |
| EXPECT_EQ(notification_promo_.end_, end_); |
| |
| EXPECT_EQ(notification_promo_.num_groups_, num_groups_); |
| EXPECT_EQ(notification_promo_.initial_segment_, initial_segment_); |
| EXPECT_EQ(notification_promo_.increment_, increment_); |
| EXPECT_EQ(notification_promo_.time_slice_, time_slice_); |
| EXPECT_EQ(notification_promo_.max_group_, max_group_); |
| |
| EXPECT_EQ(notification_promo_.max_views_, max_views_); |
| EXPECT_EQ(notification_promo_.closed_, closed_); |
| |
| // Check group within bounds. |
| EXPECT_GE(notification_promo_.group_, 0); |
| EXPECT_LT(notification_promo_.group_, num_groups_); |
| |
| // Views should be 0 for now. |
| EXPECT_EQ(notification_promo_.views_, 0); |
| } |
| |
| // Create a new NotificationPromo from prefs and compare to current |
| // notification. |
| void TestInitFromPrefs() { |
| NotificationPromo prefs_notification_promo; |
| prefs_notification_promo.InitFromPrefs(promo_type_); |
| |
| EXPECT_EQ(notification_promo_.prefs_, |
| prefs_notification_promo.prefs_); |
| EXPECT_EQ(notification_promo_.promo_text_, |
| prefs_notification_promo.promo_text_); |
| EXPECT_EQ(notification_promo_.start_, |
| prefs_notification_promo.start_); |
| EXPECT_EQ(notification_promo_.end_, |
| prefs_notification_promo.end_); |
| EXPECT_EQ(notification_promo_.num_groups_, |
| prefs_notification_promo.num_groups_); |
| EXPECT_EQ(notification_promo_.initial_segment_, |
| prefs_notification_promo.initial_segment_); |
| EXPECT_EQ(notification_promo_.increment_, |
| prefs_notification_promo.increment_); |
| EXPECT_EQ(notification_promo_.time_slice_, |
| prefs_notification_promo.time_slice_); |
| EXPECT_EQ(notification_promo_.max_group_, |
| prefs_notification_promo.max_group_); |
| EXPECT_EQ(notification_promo_.max_views_, |
| prefs_notification_promo.max_views_); |
| EXPECT_EQ(notification_promo_.group_, |
| prefs_notification_promo.group_); |
| EXPECT_EQ(notification_promo_.views_, |
| prefs_notification_promo.views_); |
| EXPECT_EQ(notification_promo_.closed_, |
| prefs_notification_promo.closed_); |
| } |
| |
| void TestGroup() { |
| // Test out of range groups. |
| const int incr = num_groups_ / 20; |
| for (int i = max_group_; i < num_groups_; i += incr) { |
| notification_promo_.group_ = i; |
| EXPECT_FALSE(notification_promo_.CanShow()); |
| } |
| |
| // Test in-range groups. |
| for (int i = 0; i < max_group_; i += incr) { |
| notification_promo_.group_ = i; |
| EXPECT_TRUE(notification_promo_.CanShow()); |
| } |
| |
| // When max_group_ is 0, all groups pass. |
| notification_promo_.max_group_ = 0; |
| for (int i = 0; i < num_groups_; i += incr) { |
| notification_promo_.group_ = i; |
| EXPECT_TRUE(notification_promo_.CanShow()); |
| } |
| notification_promo_.WritePrefs(); |
| } |
| |
| void TestViews() { |
| notification_promo_.views_ = notification_promo_.max_views_ - 2; |
| notification_promo_.WritePrefs(); |
| |
| NotificationPromo::HandleViewed(promo_type_); |
| NotificationPromo new_promo; |
| new_promo.InitFromPrefs(promo_type_); |
| EXPECT_EQ(new_promo.max_views_ - 1, new_promo.views_); |
| EXPECT_TRUE(new_promo.CanShow()); |
| NotificationPromo::HandleViewed(promo_type_); |
| new_promo.InitFromPrefs(promo_type_); |
| EXPECT_EQ(new_promo.max_views_, new_promo.views_); |
| EXPECT_FALSE(new_promo.CanShow()); |
| |
| // Test out of range views. |
| for (int i = max_views_; i < max_views_ * 2; ++i) { |
| new_promo.views_ = i; |
| EXPECT_FALSE(new_promo.CanShow()); |
| } |
| |
| // Test in range views. |
| for (int i = 0; i < max_views_; ++i) { |
| new_promo.views_ = i; |
| EXPECT_TRUE(new_promo.CanShow()); |
| } |
| new_promo.WritePrefs(); |
| } |
| |
| void TestClosed() { |
| NotificationPromo new_promo; |
| new_promo.InitFromPrefs(promo_type_); |
| EXPECT_FALSE(new_promo.closed_); |
| EXPECT_TRUE(new_promo.CanShow()); |
| |
| NotificationPromo::HandleClosed(promo_type_); |
| new_promo.InitFromPrefs(promo_type_); |
| EXPECT_TRUE(new_promo.closed_); |
| EXPECT_FALSE(new_promo.CanShow()); |
| |
| new_promo.closed_ = false; |
| EXPECT_TRUE(new_promo.CanShow()); |
| new_promo.WritePrefs(); |
| } |
| |
| void TestPromoText() { |
| notification_promo_.promo_text_.clear(); |
| EXPECT_FALSE(notification_promo_.CanShow()); |
| |
| notification_promo_.promo_text_ = promo_text_; |
| EXPECT_TRUE(notification_promo_.CanShow()); |
| } |
| |
| void TestTime() { |
| const double now = base::Time::Now().ToDoubleT(); |
| const double qhour = 15 * 60; |
| |
| notification_promo_.group_ = 0; // For simplicity. |
| |
| notification_promo_.start_ = now - qhour; |
| notification_promo_.end_ = now + qhour; |
| EXPECT_TRUE(notification_promo_.CanShow()); |
| |
| // Start time has not arrived. |
| notification_promo_.start_ = now + qhour; |
| notification_promo_.end_ = now + qhour; |
| EXPECT_FALSE(notification_promo_.CanShow()); |
| |
| // End time has past. |
| notification_promo_.start_ = now - qhour; |
| notification_promo_.end_ = now - qhour; |
| EXPECT_FALSE(notification_promo_.CanShow()); |
| |
| notification_promo_.start_ = start_; |
| notification_promo_.end_ = end_; |
| EXPECT_TRUE(notification_promo_.CanShow()); |
| } |
| |
| void TestIncrement() { |
| const double now = base::Time::Now().ToDoubleT(); |
| const double slice = 60; |
| |
| notification_promo_.num_groups_ = 18; |
| notification_promo_.initial_segment_ = 5; |
| notification_promo_.increment_ = 3; |
| notification_promo_.time_slice_ = slice; |
| |
| notification_promo_.start_ = now - 1; |
| notification_promo_.end_ = now + slice; |
| |
| // Test initial segment. |
| notification_promo_.group_ = 4; |
| EXPECT_TRUE(notification_promo_.CanShow()); |
| notification_promo_.group_ = 5; |
| EXPECT_FALSE(notification_promo_.CanShow()); |
| |
| // Test first increment. |
| notification_promo_.start_ -= slice; |
| notification_promo_.group_ = 7; |
| EXPECT_TRUE(notification_promo_.CanShow()); |
| notification_promo_.group_ = 8; |
| EXPECT_FALSE(notification_promo_.CanShow()); |
| |
| // Test second increment. |
| notification_promo_.start_ -= slice; |
| notification_promo_.group_ = 10; |
| EXPECT_TRUE(notification_promo_.CanShow()); |
| notification_promo_.group_ = 11; |
| EXPECT_FALSE(notification_promo_.CanShow()); |
| |
| // Test penultimate increment. |
| notification_promo_.start_ -= 2 * slice; |
| notification_promo_.group_ = 16; |
| EXPECT_TRUE(notification_promo_.CanShow()); |
| notification_promo_.group_ = 17; |
| EXPECT_FALSE(notification_promo_.CanShow()); |
| |
| // Test last increment. |
| notification_promo_.start_ -= slice; |
| EXPECT_TRUE(notification_promo_.CanShow()); |
| } |
| |
| const NotificationPromo& promo() const { return notification_promo_; } |
| |
| private: |
| NotificationPromo notification_promo_; |
| bool received_notification_; |
| scoped_ptr<DictionaryValue> test_json_; |
| |
| NotificationPromo::PromoType promo_type_; |
| std::string promo_text_; |
| |
| double start_; |
| double end_; |
| |
| int num_groups_; |
| int initial_segment_; |
| int increment_; |
| int time_slice_; |
| int max_group_; |
| |
| int max_views_; |
| |
| bool closed_; |
| }; |
| |
| // Test that everything gets parsed correctly, notifications are sent, |
| // and CanShow() is handled correctly under variety of conditions. |
| // Additionally, test that the first string in |strings| is used if |
| // no payload.promo_short_message is specified in the JSON response. |
| TEST_F(PromoResourceServiceTest, NotificationPromoTest) { |
| // Check that prefs are set correctly. |
| NotificationPromoTest promo_test; |
| |
| // Set up start date and promo line in a Dictionary as if parsed from the |
| // service. date[0].end is replaced with a date 1 year in the future. |
| promo_test.Init("{" |
| " \"ntp_notification_promo\": [" |
| " {" |
| " \"date\":" |
| " [" |
| " {" |
| " \"start\":\"3 Aug 1999 9:26:06 GMT\"," |
| " \"end\":\"$1\"" |
| " }" |
| " ]," |
| " \"strings\":" |
| " {" |
| " \"NTP4_HOW_DO_YOU_FEEL_ABOUT_CHROME\":" |
| " \"What do you think of Chrome?\"" |
| " }," |
| " \"grouping\":" |
| " {" |
| " \"buckets\":1000," |
| " \"segment\":200," |
| " \"increment\":100," |
| " \"increment_frequency\":3600," |
| " \"increment_max\":400" |
| " }," |
| " \"payload\":" |
| " {" |
| " \"days_active\":7," |
| " \"install_age_days\":21" |
| " }," |
| " \"max_views\":30" |
| " }" |
| " ]" |
| "}", |
| "What do you think of Chrome?", |
| // The starting date is in 1999 to make tests pass |
| // on Android devices with incorrect or unset date/time. |
| 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT. |
| 1000, 200, 100, 3600, 400, 30); |
| |
| promo_test.InitPromoFromJson(true); |
| |
| // Second time should not trigger a notification. |
| promo_test.InitPromoFromJson(false); |
| |
| promo_test.TestInitFromPrefs(); |
| |
| // Test various conditions of CanShow. |
| // TestGroup Has the side effect of setting us to a passing group. |
| promo_test.TestGroup(); |
| promo_test.TestViews(); |
| promo_test.TestClosed(); |
| promo_test.TestPromoText(); |
| promo_test.TestTime(); |
| promo_test.TestIncrement(); |
| } |
| |
| // Test that payload.promo_message_short is used if present. |
| TEST_F(PromoResourceServiceTest, NotificationPromoCompatNoStringsTest) { |
| // Check that prefs are set correctly. |
| NotificationPromoTest promo_test; |
| |
| // Set up start date and promo line in a Dictionary as if parsed from the |
| // service. date[0].end is replaced with a date 1 year in the future. |
| promo_test.Init("{" |
| " \"ntp_notification_promo\": [" |
| " {" |
| " \"date\":" |
| " [" |
| " {" |
| " \"start\":\"3 Aug 1999 9:26:06 GMT\"," |
| " \"end\":\"$1\"" |
| " }" |
| " ]," |
| " \"grouping\":" |
| " {" |
| " \"buckets\":1000," |
| " \"segment\":200," |
| " \"increment\":100," |
| " \"increment_frequency\":3600," |
| " \"increment_max\":400" |
| " }," |
| " \"payload\":" |
| " {" |
| " \"promo_message_short\":" |
| " \"What do you think of Chrome?\"," |
| " \"days_active\":7," |
| " \"install_age_days\":21" |
| " }," |
| " \"max_views\":30" |
| " }" |
| " ]" |
| "}", |
| "What do you think of Chrome?", |
| // The starting date is in 1999 to make tests pass |
| // on Android devices with incorrect or unset date/time. |
| 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT. |
| 1000, 200, 100, 3600, 400, 30); |
| |
| promo_test.InitPromoFromJson(true); |
| // Second time should not trigger a notification. |
| promo_test.InitPromoFromJson(false); |
| promo_test.TestInitFromPrefs(); |
| } |
| |
| // Test that strings.|payload.promo_message_short| is used if present. |
| TEST_F(PromoResourceServiceTest, NotificationPromoCompatPayloadStringsTest) { |
| // Check that prefs are set correctly. |
| NotificationPromoTest promo_test; |
| |
| // Set up start date and promo line in a Dictionary as if parsed from the |
| // service. date[0].end is replaced with a date 1 year in the future. |
| promo_test.Init("{" |
| " \"ntp_notification_promo\": [" |
| " {" |
| " \"date\":" |
| " [" |
| " {" |
| " \"start\":\"3 Aug 1999 9:26:06 GMT\"," |
| " \"end\":\"$1\"" |
| " }" |
| " ]," |
| " \"grouping\":" |
| " {" |
| " \"buckets\":1000," |
| " \"segment\":200," |
| " \"increment\":100," |
| " \"increment_frequency\":3600," |
| " \"increment_max\":400" |
| " }," |
| " \"strings\":" |
| " {" |
| " \"bogus\":\"string\"," |
| " \"GOOD_STRING\":" |
| " \"What do you think of Chrome?\"" |
| " }," |
| " \"payload\":" |
| " {" |
| " \"promo_message_short\":" |
| " \"GOOD_STRING\"," |
| " \"days_active\":7," |
| " \"install_age_days\":21" |
| " }," |
| " \"max_views\":30" |
| " }" |
| " ]" |
| "}", |
| "What do you think of Chrome?", |
| // The starting date is in 1999 to make tests pass |
| // on Android devices with incorrect or unset date/time. |
| 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT. |
| 1000, 200, 100, 3600, 400, 30); |
| |
| promo_test.InitPromoFromJson(true); |
| // Second time should not trigger a notification. |
| promo_test.InitPromoFromJson(false); |
| promo_test.TestInitFromPrefs(); |
| } |
| |
| TEST_F(PromoResourceServiceTest, PromoServerURLTest) { |
| GURL promo_server_url = NotificationPromo::PromoServerURL(); |
| EXPECT_FALSE(promo_server_url.is_empty()); |
| EXPECT_TRUE(promo_server_url.is_valid()); |
| EXPECT_TRUE(promo_server_url.SchemeIs(chrome::kHttpsScheme)); |
| // TODO(achuith): Test this better. |
| } |
| |
| TEST_F(PromoResourceServiceTest, AppLauncherPromoTest) { |
| // Check that prefs are set correctly. |
| NotificationPromoTest promo_test; |
| |
| // Set up start date and promo line in a Dictionary as if parsed from the |
| // service. date[0].end is replaced with a date 1 year in the future. |
| promo_test.Init("{" |
| " \"ntp_notification_promo\": [" |
| " {" |
| " \"date\":" |
| " [" |
| " {" |
| " \"start\":\"3 Aug 1999 9:26:06 GMT\"," |
| " \"end\":\"$1\"" |
| " }" |
| " ]," |
| " \"grouping\":" |
| " {" |
| " \"buckets\":1000," |
| " \"segment\":200," |
| " \"increment\":100," |
| " \"increment_frequency\":3600," |
| " \"increment_max\":400" |
| " }," |
| " \"payload\":" |
| " {" |
| " \"promo_message_short\":" |
| " \"What do you think of Chrome?\"," |
| " \"days_active\":7," |
| " \"install_age_days\":21," |
| " \"is_app_launcher_promo\":true" |
| " }," |
| " \"max_views\":30" |
| " }" |
| " ]" |
| "}", |
| "What do you think of Chrome?", |
| // The starting date is in 1999 to make tests pass |
| // on Android devices with incorrect or unset date/time. |
| 933672366, // unix epoch for 3 Aug 1999 9:26:06 GMT. |
| 1000, 200, 100, 3600, 400, 30); |
| promo_test.InitPromoFromJson(true); |
| local_state_.Get()->SetBoolean(apps::prefs::kAppLauncherIsEnabled, true); |
| EXPECT_FALSE(promo_test.promo().CanShow()); |
| } |