Home | History | Annotate | Download | only in web_resource
      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.
      4 
      5 #include <utility>
      6 #include <vector>
      7 
      8 #include "apps/pref_names.h"
      9 #include "base/json/json_reader.h"
     10 #include "base/message_loop/message_loop.h"
     11 #include "base/prefs/pref_service.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/strings/string_util.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "base/time/time.h"
     16 #include "base/values.h"
     17 #include "chrome/browser/browser_process.h"
     18 #include "chrome/browser/chrome_notification_types.h"
     19 #include "chrome/browser/prefs/browser_prefs.h"
     20 #include "chrome/browser/web_resource/notification_promo.h"
     21 #include "chrome/browser/web_resource/promo_resource_service.h"
     22 #include "chrome/common/pref_names.h"
     23 #include "chrome/common/url_constants.h"
     24 #include "chrome/test/base/scoped_testing_local_state.h"
     25 #include "chrome/test/base/testing_browser_process.h"
     26 #include "content/public/browser/notification_registrar.h"
     27 #include "content/public/browser/notification_service.h"
     28 #include "net/url_request/test_url_fetcher_factory.h"
     29 #include "testing/gtest/include/gtest/gtest.h"
     30 #include "third_party/icu/source/i18n/unicode/smpdtfmt.h"
     31 
     32 namespace {
     33 
     34 const char kDateFormat[] = "dd MMM yyyy HH:mm:ss zzz";
     35 
     36 bool YearFromNow(double* date_epoch, std::string* date_string) {
     37   *date_epoch = (base::Time::Now() + base::TimeDelta::FromDays(365)).ToTimeT();
     38 
     39   UErrorCode status = U_ZERO_ERROR;
     40   icu::SimpleDateFormat simple_formatter(icu::UnicodeString(kDateFormat),
     41                                          icu::Locale("en_US"),
     42                                          status);
     43   if (!U_SUCCESS(status))
     44     return false;
     45 
     46   icu::UnicodeString date_unicode_string;
     47   simple_formatter.format(static_cast<UDate>(*date_epoch * 1000),
     48                           date_unicode_string,
     49                           status);
     50   if (!U_SUCCESS(status))
     51     return false;
     52 
     53   return UTF16ToUTF8(date_unicode_string.getBuffer(),
     54                      static_cast<size_t>(date_unicode_string.length()),
     55                      date_string);
     56 }
     57 
     58 }  // namespace
     59 
     60 class PromoResourceServiceTest : public testing::Test {
     61  public:
     62   // |promo_resource_service_| must be created after |local_state_|.
     63   PromoResourceServiceTest()
     64       : local_state_(TestingBrowserProcess::GetGlobal()),
     65         promo_resource_service_(new PromoResourceService) {}
     66 
     67  protected:
     68   ScopedTestingLocalState local_state_;
     69   scoped_refptr<PromoResourceService> promo_resource_service_;
     70   base::MessageLoop loop_;
     71 };
     72 
     73 class NotificationPromoTest {
     74  public:
     75   NotificationPromoTest()
     76       : received_notification_(false),
     77         start_(0.0),
     78         end_(0.0),
     79         num_groups_(0),
     80         initial_segment_(0),
     81         increment_(1),
     82         time_slice_(0),
     83         max_group_(0),
     84         max_views_(0),
     85         closed_(false) {}
     86 
     87   void Init(const std::string& json,
     88             const std::string& promo_text,
     89             double start,
     90             int num_groups, int initial_segment, int increment,
     91             int time_slice, int max_group, int max_views) {
     92     double year_from_now_epoch;
     93     std::string year_from_now_string;
     94     ASSERT_TRUE(YearFromNow(&year_from_now_epoch, &year_from_now_string));
     95 
     96     std::vector<std::string> replacements;
     97     replacements.push_back(year_from_now_string);
     98 
     99     std::string json_with_end_date(
    100         ReplaceStringPlaceholders(json, replacements, NULL));
    101     Value* value(base::JSONReader::Read(json_with_end_date));
    102     ASSERT_TRUE(value);
    103 
    104     DictionaryValue* dict = NULL;
    105     value->GetAsDictionary(&dict);
    106     ASSERT_TRUE(dict);
    107     test_json_.reset(dict);
    108 
    109     promo_type_ = NotificationPromo::NTP_NOTIFICATION_PROMO;
    110     promo_text_ = promo_text;
    111 
    112     start_ = start;
    113     end_ = year_from_now_epoch;
    114 
    115     num_groups_ = num_groups;
    116     initial_segment_ = initial_segment;
    117     increment_ = increment;
    118     time_slice_ = time_slice;
    119     max_group_ = max_group;
    120 
    121     max_views_ = max_views;
    122 
    123     closed_ = false;
    124     received_notification_ = false;
    125   }
    126 
    127   void InitPromoFromJson(bool should_receive_notification) {
    128     notification_promo_.InitFromJson(*test_json_, promo_type_);
    129     EXPECT_EQ(should_receive_notification,
    130               notification_promo_.new_notification());
    131 
    132     // Test the fields.
    133     TestNotification();
    134   }
    135 
    136   void TestNotification() {
    137     // Check values.
    138     EXPECT_EQ(notification_promo_.promo_text_, promo_text_);
    139 
    140     EXPECT_EQ(notification_promo_.start_, start_);
    141     EXPECT_EQ(notification_promo_.end_, end_);
    142 
    143     EXPECT_EQ(notification_promo_.num_groups_, num_groups_);
    144     EXPECT_EQ(notification_promo_.initial_segment_, initial_segment_);
    145     EXPECT_EQ(notification_promo_.increment_, increment_);
    146     EXPECT_EQ(notification_promo_.time_slice_, time_slice_);
    147     EXPECT_EQ(notification_promo_.max_group_, max_group_);
    148 
    149     EXPECT_EQ(notification_promo_.max_views_, max_views_);
    150     EXPECT_EQ(notification_promo_.closed_, closed_);
    151 
    152     // Check group within bounds.
    153     EXPECT_GE(notification_promo_.group_, 0);
    154     EXPECT_LT(notification_promo_.group_, num_groups_);
    155 
    156     // Views should be 0 for now.
    157     EXPECT_EQ(notification_promo_.views_, 0);
    158   }
    159 
    160   // Create a new NotificationPromo from prefs and compare to current
    161   // notification.
    162   void TestInitFromPrefs() {
    163     NotificationPromo prefs_notification_promo;
    164     prefs_notification_promo.InitFromPrefs(promo_type_);
    165 
    166     EXPECT_EQ(notification_promo_.prefs_,
    167               prefs_notification_promo.prefs_);
    168     EXPECT_EQ(notification_promo_.promo_text_,
    169               prefs_notification_promo.promo_text_);
    170     EXPECT_EQ(notification_promo_.start_,
    171               prefs_notification_promo.start_);
    172     EXPECT_EQ(notification_promo_.end_,
    173               prefs_notification_promo.end_);
    174     EXPECT_EQ(notification_promo_.num_groups_,
    175               prefs_notification_promo.num_groups_);
    176     EXPECT_EQ(notification_promo_.initial_segment_,
    177               prefs_notification_promo.initial_segment_);
    178     EXPECT_EQ(notification_promo_.increment_,
    179               prefs_notification_promo.increment_);
    180     EXPECT_EQ(notification_promo_.time_slice_,
    181               prefs_notification_promo.time_slice_);
    182     EXPECT_EQ(notification_promo_.max_group_,
    183               prefs_notification_promo.max_group_);
    184     EXPECT_EQ(notification_promo_.max_views_,
    185               prefs_notification_promo.max_views_);
    186     EXPECT_EQ(notification_promo_.group_,
    187               prefs_notification_promo.group_);
    188     EXPECT_EQ(notification_promo_.views_,
    189               prefs_notification_promo.views_);
    190     EXPECT_EQ(notification_promo_.closed_,
    191               prefs_notification_promo.closed_);
    192   }
    193 
    194   void TestGroup() {
    195     // Test out of range groups.
    196     const int incr = num_groups_ / 20;
    197     for (int i = max_group_; i < num_groups_; i += incr) {
    198       notification_promo_.group_ = i;
    199       EXPECT_FALSE(notification_promo_.CanShow());
    200     }
    201 
    202     // Test in-range groups.
    203     for (int i = 0; i < max_group_; i += incr) {
    204       notification_promo_.group_ = i;
    205       EXPECT_TRUE(notification_promo_.CanShow());
    206     }
    207 
    208     // When max_group_ is 0, all groups pass.
    209     notification_promo_.max_group_ = 0;
    210     for (int i = 0; i < num_groups_; i += incr) {
    211       notification_promo_.group_ = i;
    212       EXPECT_TRUE(notification_promo_.CanShow());
    213     }
    214     notification_promo_.WritePrefs();
    215   }
    216 
    217   void TestViews() {
    218     notification_promo_.views_ = notification_promo_.max_views_ - 2;
    219     notification_promo_.WritePrefs();
    220 
    221     NotificationPromo::HandleViewed(promo_type_);
    222     NotificationPromo new_promo;
    223     new_promo.InitFromPrefs(promo_type_);
    224     EXPECT_EQ(new_promo.max_views_ - 1, new_promo.views_);
    225     EXPECT_TRUE(new_promo.CanShow());
    226     NotificationPromo::HandleViewed(promo_type_);
    227     new_promo.InitFromPrefs(promo_type_);
    228     EXPECT_EQ(new_promo.max_views_, new_promo.views_);
    229     EXPECT_FALSE(new_promo.CanShow());
    230 
    231     // Test out of range views.
    232     for (int i = max_views_; i < max_views_ * 2; ++i) {
    233       new_promo.views_ = i;
    234       EXPECT_FALSE(new_promo.CanShow());
    235     }
    236 
    237     // Test in range views.
    238     for (int i = 0; i < max_views_; ++i) {
    239       new_promo.views_ = i;
    240       EXPECT_TRUE(new_promo.CanShow());
    241     }
    242     new_promo.WritePrefs();
    243   }
    244 
    245   void TestClosed() {
    246     NotificationPromo new_promo;
    247     new_promo.InitFromPrefs(promo_type_);
    248     EXPECT_FALSE(new_promo.closed_);
    249     EXPECT_TRUE(new_promo.CanShow());
    250 
    251     NotificationPromo::HandleClosed(promo_type_);
    252     new_promo.InitFromPrefs(promo_type_);
    253     EXPECT_TRUE(new_promo.closed_);
    254     EXPECT_FALSE(new_promo.CanShow());
    255 
    256     new_promo.closed_ = false;
    257     EXPECT_TRUE(new_promo.CanShow());
    258     new_promo.WritePrefs();
    259   }
    260 
    261   void TestPromoText() {
    262     notification_promo_.promo_text_.clear();
    263     EXPECT_FALSE(notification_promo_.CanShow());
    264 
    265     notification_promo_.promo_text_ = promo_text_;
    266     EXPECT_TRUE(notification_promo_.CanShow());
    267   }
    268 
    269   void TestTime() {
    270     const double now = base::Time::Now().ToDoubleT();
    271     const double qhour = 15 * 60;
    272 
    273     notification_promo_.group_ = 0;  // For simplicity.
    274 
    275     notification_promo_.start_ = now - qhour;
    276     notification_promo_.end_ = now + qhour;
    277     EXPECT_TRUE(notification_promo_.CanShow());
    278 
    279     // Start time has not arrived.
    280     notification_promo_.start_ = now + qhour;
    281     notification_promo_.end_ = now + qhour;
    282     EXPECT_FALSE(notification_promo_.CanShow());
    283 
    284     // End time has past.
    285     notification_promo_.start_ = now - qhour;
    286     notification_promo_.end_ = now - qhour;
    287     EXPECT_FALSE(notification_promo_.CanShow());
    288 
    289     notification_promo_.start_ = start_;
    290     notification_promo_.end_ = end_;
    291     EXPECT_TRUE(notification_promo_.CanShow());
    292   }
    293 
    294   void TestIncrement() {
    295     const double now = base::Time::Now().ToDoubleT();
    296     const double slice = 60;
    297 
    298     notification_promo_.num_groups_ = 18;
    299     notification_promo_.initial_segment_ = 5;
    300     notification_promo_.increment_ = 3;
    301     notification_promo_.time_slice_ = slice;
    302 
    303     notification_promo_.start_ = now - 1;
    304     notification_promo_.end_ = now + slice;
    305 
    306     // Test initial segment.
    307     notification_promo_.group_ = 4;
    308     EXPECT_TRUE(notification_promo_.CanShow());
    309     notification_promo_.group_ = 5;
    310     EXPECT_FALSE(notification_promo_.CanShow());
    311 
    312     // Test first increment.
    313     notification_promo_.start_ -= slice;
    314     notification_promo_.group_ = 7;
    315     EXPECT_TRUE(notification_promo_.CanShow());
    316     notification_promo_.group_ = 8;
    317     EXPECT_FALSE(notification_promo_.CanShow());
    318 
    319     // Test second increment.
    320     notification_promo_.start_ -= slice;
    321     notification_promo_.group_ = 10;
    322     EXPECT_TRUE(notification_promo_.CanShow());
    323     notification_promo_.group_ = 11;
    324     EXPECT_FALSE(notification_promo_.CanShow());
    325 
    326     // Test penultimate increment.
    327     notification_promo_.start_ -= 2 * slice;
    328     notification_promo_.group_ = 16;
    329     EXPECT_TRUE(notification_promo_.CanShow());
    330     notification_promo_.group_ = 17;
    331     EXPECT_FALSE(notification_promo_.CanShow());
    332 
    333     // Test last increment.
    334     notification_promo_.start_ -= slice;
    335     EXPECT_TRUE(notification_promo_.CanShow());
    336   }
    337 
    338   const NotificationPromo& promo() const { return notification_promo_; }
    339 
    340  private:
    341   NotificationPromo notification_promo_;
    342   bool received_notification_;
    343   scoped_ptr<DictionaryValue> test_json_;
    344 
    345   NotificationPromo::PromoType promo_type_;
    346   std::string promo_text_;
    347 
    348   double start_;
    349   double end_;
    350 
    351   int num_groups_;
    352   int initial_segment_;
    353   int increment_;
    354   int time_slice_;
    355   int max_group_;
    356 
    357   int max_views_;
    358 
    359   bool closed_;
    360 };
    361 
    362 // Test that everything gets parsed correctly, notifications are sent,
    363 // and CanShow() is handled correctly under variety of conditions.
    364 // Additionally, test that the first string in |strings| is used if
    365 // no payload.promo_short_message is specified in the JSON response.
    366 TEST_F(PromoResourceServiceTest, NotificationPromoTest) {
    367   // Check that prefs are set correctly.
    368   NotificationPromoTest promo_test;
    369 
    370   // Set up start date and promo line in a Dictionary as if parsed from the
    371   // service. date[0].end is replaced with a date 1 year in the future.
    372   promo_test.Init("{"
    373                   "  \"ntp_notification_promo\": ["
    374                   "    {"
    375                   "      \"date\":"
    376                   "        ["
    377                   "          {"
    378                   "            \"start\":\"3 Aug 1999 9:26:06 GMT\","
    379                   "            \"end\":\"$1\""
    380                   "          }"
    381                   "        ],"
    382                   "      \"strings\":"
    383                   "        {"
    384                   "          \"NTP4_HOW_DO_YOU_FEEL_ABOUT_CHROME\":"
    385                   "              \"What do you think of Chrome?\""
    386                   "        },"
    387                   "      \"grouping\":"
    388                   "        {"
    389                   "          \"buckets\":1000,"
    390                   "          \"segment\":200,"
    391                   "          \"increment\":100,"
    392                   "          \"increment_frequency\":3600,"
    393                   "          \"increment_max\":400"
    394                   "        },"
    395                   "      \"payload\":"
    396                   "        {"
    397                   "          \"days_active\":7,"
    398                   "          \"install_age_days\":21"
    399                   "        },"
    400                   "      \"max_views\":30"
    401                   "    }"
    402                   "  ]"
    403                   "}",
    404                   "What do you think of Chrome?",
    405                   // The starting date is in 1999 to make tests pass
    406                   // on Android devices with incorrect or unset date/time.
    407                   933672366,  // unix epoch for 3 Aug 1999 9:26:06 GMT.
    408                   1000, 200, 100, 3600, 400, 30);
    409 
    410   promo_test.InitPromoFromJson(true);
    411 
    412   // Second time should not trigger a notification.
    413   promo_test.InitPromoFromJson(false);
    414 
    415   promo_test.TestInitFromPrefs();
    416 
    417   // Test various conditions of CanShow.
    418   // TestGroup Has the side effect of setting us to a passing group.
    419   promo_test.TestGroup();
    420   promo_test.TestViews();
    421   promo_test.TestClosed();
    422   promo_test.TestPromoText();
    423   promo_test.TestTime();
    424   promo_test.TestIncrement();
    425 }
    426 
    427 // Test that payload.promo_message_short is used if present.
    428 TEST_F(PromoResourceServiceTest, NotificationPromoCompatNoStringsTest) {
    429   // Check that prefs are set correctly.
    430   NotificationPromoTest promo_test;
    431 
    432   // Set up start date and promo line in a Dictionary as if parsed from the
    433   // service. date[0].end is replaced with a date 1 year in the future.
    434   promo_test.Init("{"
    435                   "  \"ntp_notification_promo\": ["
    436                   "    {"
    437                   "      \"date\":"
    438                   "        ["
    439                   "          {"
    440                   "            \"start\":\"3 Aug 1999 9:26:06 GMT\","
    441                   "            \"end\":\"$1\""
    442                   "          }"
    443                   "        ],"
    444                   "      \"grouping\":"
    445                   "        {"
    446                   "          \"buckets\":1000,"
    447                   "          \"segment\":200,"
    448                   "          \"increment\":100,"
    449                   "          \"increment_frequency\":3600,"
    450                   "          \"increment_max\":400"
    451                   "        },"
    452                   "      \"payload\":"
    453                   "        {"
    454                   "          \"promo_message_short\":"
    455                   "              \"What do you think of Chrome?\","
    456                   "          \"days_active\":7,"
    457                   "          \"install_age_days\":21"
    458                   "        },"
    459                   "      \"max_views\":30"
    460                   "    }"
    461                   "  ]"
    462                   "}",
    463                   "What do you think of Chrome?",
    464                   // The starting date is in 1999 to make tests pass
    465                   // on Android devices with incorrect or unset date/time.
    466                   933672366,  // unix epoch for 3 Aug 1999 9:26:06 GMT.
    467                   1000, 200, 100, 3600, 400, 30);
    468 
    469   promo_test.InitPromoFromJson(true);
    470   // Second time should not trigger a notification.
    471   promo_test.InitPromoFromJson(false);
    472   promo_test.TestInitFromPrefs();
    473 }
    474 
    475 // Test that strings.|payload.promo_message_short| is used if present.
    476 TEST_F(PromoResourceServiceTest, NotificationPromoCompatPayloadStringsTest) {
    477   // Check that prefs are set correctly.
    478   NotificationPromoTest promo_test;
    479 
    480   // Set up start date and promo line in a Dictionary as if parsed from the
    481   // service. date[0].end is replaced with a date 1 year in the future.
    482   promo_test.Init("{"
    483                   "  \"ntp_notification_promo\": ["
    484                   "    {"
    485                   "      \"date\":"
    486                   "        ["
    487                   "          {"
    488                   "            \"start\":\"3 Aug 1999 9:26:06 GMT\","
    489                   "            \"end\":\"$1\""
    490                   "          }"
    491                   "        ],"
    492                   "      \"grouping\":"
    493                   "        {"
    494                   "          \"buckets\":1000,"
    495                   "          \"segment\":200,"
    496                   "          \"increment\":100,"
    497                   "          \"increment_frequency\":3600,"
    498                   "          \"increment_max\":400"
    499                   "        },"
    500                   "      \"strings\":"
    501                   "        {"
    502                   "          \"bogus\":\"string\","
    503                   "          \"GOOD_STRING\":"
    504                   "              \"What do you think of Chrome?\""
    505                   "        },"
    506                   "      \"payload\":"
    507                   "        {"
    508                   "          \"promo_message_short\":"
    509                   "              \"GOOD_STRING\","
    510                   "          \"days_active\":7,"
    511                   "          \"install_age_days\":21"
    512                   "        },"
    513                   "      \"max_views\":30"
    514                   "    }"
    515                   "  ]"
    516                   "}",
    517                   "What do you think of Chrome?",
    518                   // The starting date is in 1999 to make tests pass
    519                   // on Android devices with incorrect or unset date/time.
    520                   933672366,  // unix epoch for 3 Aug 1999 9:26:06 GMT.
    521                   1000, 200, 100, 3600, 400, 30);
    522 
    523   promo_test.InitPromoFromJson(true);
    524   // Second time should not trigger a notification.
    525   promo_test.InitPromoFromJson(false);
    526   promo_test.TestInitFromPrefs();
    527 }
    528 
    529 TEST_F(PromoResourceServiceTest, PromoServerURLTest) {
    530   GURL promo_server_url = NotificationPromo::PromoServerURL();
    531   EXPECT_FALSE(promo_server_url.is_empty());
    532   EXPECT_TRUE(promo_server_url.is_valid());
    533   EXPECT_TRUE(promo_server_url.SchemeIs(chrome::kHttpsScheme));
    534   // TODO(achuith): Test this better.
    535 }
    536 
    537 TEST_F(PromoResourceServiceTest, AppLauncherPromoTest) {
    538   // Check that prefs are set correctly.
    539   NotificationPromoTest promo_test;
    540 
    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.
    543   promo_test.Init("{"
    544                   "  \"ntp_notification_promo\": ["
    545                   "    {"
    546                   "      \"date\":"
    547                   "        ["
    548                   "          {"
    549                   "            \"start\":\"3 Aug 1999 9:26:06 GMT\","
    550                   "            \"end\":\"$1\""
    551                   "          }"
    552                   "        ],"
    553                   "      \"grouping\":"
    554                   "        {"
    555                   "          \"buckets\":1000,"
    556                   "          \"segment\":200,"
    557                   "          \"increment\":100,"
    558                   "          \"increment_frequency\":3600,"
    559                   "          \"increment_max\":400"
    560                   "        },"
    561                   "      \"payload\":"
    562                   "        {"
    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"
    568                   "        },"
    569                   "      \"max_views\":30"
    570                   "    }"
    571                   "  ]"
    572                   "}",
    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(apps::prefs::kAppLauncherIsEnabled, true);
    580   EXPECT_FALSE(promo_test.promo().CanShow());
    581 }
    582