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 "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"
     30 
     31 namespace {
     32 
     33 const char kDateFormat[] = "dd MMM yyyy HH:mm:ss zzz";
     34 
     35 bool YearFromNow(double* date_epoch, std::string* date_string) {
     36   *date_epoch = (base::Time::Now() + base::TimeDelta::FromDays(365)).ToTimeT();
     37 
     38   UErrorCode status = U_ZERO_ERROR;
     39   icu::SimpleDateFormat simple_formatter(icu::UnicodeString(kDateFormat),
     40                                          icu::Locale("en_US"),
     41                                          status);
     42   if (!U_SUCCESS(status))
     43     return false;
     44 
     45   icu::UnicodeString date_unicode_string;
     46   simple_formatter.format(static_cast<UDate>(*date_epoch * 1000),
     47                           date_unicode_string,
     48                           status);
     49   if (!U_SUCCESS(status))
     50     return false;
     51 
     52   return UTF16ToUTF8(date_unicode_string.getBuffer(),
     53                      static_cast<size_t>(date_unicode_string.length()),
     54                      date_string);
     55 }
     56 
     57 }  // namespace
     58 
     59 class PromoResourceServiceTest : public testing::Test {
     60  public:
     61   // |promo_resource_service_| must be created after |local_state_|.
     62   PromoResourceServiceTest()
     63       : local_state_(TestingBrowserProcess::GetGlobal()),
     64         promo_resource_service_(new PromoResourceService) {}
     65 
     66  protected:
     67   ScopedTestingLocalState local_state_;
     68   scoped_refptr<PromoResourceService> promo_resource_service_;
     69   base::MessageLoop loop_;
     70 };
     71 
     72 class NotificationPromoTest {
     73  public:
     74   NotificationPromoTest()
     75       : received_notification_(false),
     76         start_(0.0),
     77         end_(0.0),
     78         num_groups_(0),
     79         initial_segment_(0),
     80         increment_(1),
     81         time_slice_(0),
     82         max_group_(0),
     83         max_views_(0),
     84         closed_(false) {}
     85 
     86   void Init(const std::string& json,
     87             const std::string& promo_text,
     88             double start,
     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));
     94 
     95     std::vector<std::string> replacements;
     96     replacements.push_back(year_from_now_string);
     97 
     98     std::string json_with_end_date(
     99         ReplaceStringPlaceholders(json, replacements, NULL));
    100     Value* value(base::JSONReader::Read(json_with_end_date));
    101     ASSERT_TRUE(value);
    102 
    103     DictionaryValue* dict = NULL;
    104     value->GetAsDictionary(&dict);
    105     ASSERT_TRUE(dict);
    106     test_json_.reset(dict);
    107 
    108     promo_type_ = NotificationPromo::NTP_NOTIFICATION_PROMO;
    109     promo_text_ = promo_text;
    110 
    111     start_ = start;
    112     end_ = year_from_now_epoch;
    113 
    114     num_groups_ = num_groups;
    115     initial_segment_ = initial_segment;
    116     increment_ = increment;
    117     time_slice_ = time_slice;
    118     max_group_ = max_group;
    119 
    120     max_views_ = max_views;
    121 
    122     closed_ = false;
    123     received_notification_ = false;
    124   }
    125 
    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());
    130 
    131     // Test the fields.
    132     TestNotification();
    133   }
    134 
    135   void TestNotification() {
    136     // Check values.
    137     EXPECT_EQ(notification_promo_.promo_text_, promo_text_);
    138 
    139     EXPECT_EQ(notification_promo_.start_, start_);
    140     EXPECT_EQ(notification_promo_.end_, end_);
    141 
    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_);
    147 
    148     EXPECT_EQ(notification_promo_.max_views_, max_views_);
    149     EXPECT_EQ(notification_promo_.closed_, closed_);
    150 
    151     // Check group within bounds.
    152     EXPECT_GE(notification_promo_.group_, 0);
    153     EXPECT_LT(notification_promo_.group_, num_groups_);
    154 
    155     // Views should be 0 for now.
    156     EXPECT_EQ(notification_promo_.views_, 0);
    157   }
    158 
    159   // Create a new NotificationPromo from prefs and compare to current
    160   // notification.
    161   void TestInitFromPrefs() {
    162     NotificationPromo prefs_notification_promo;
    163     prefs_notification_promo.InitFromPrefs(promo_type_);
    164 
    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_);
    191   }
    192 
    193   void TestGroup() {
    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());
    199     }
    200 
    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());
    205     }
    206 
    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());
    212     }
    213     notification_promo_.WritePrefs();
    214   }
    215 
    216   void TestViews() {
    217     notification_promo_.views_ = notification_promo_.max_views_ - 2;
    218     notification_promo_.WritePrefs();
    219 
    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());
    229 
    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());
    234     }
    235 
    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());
    240     }
    241     new_promo.WritePrefs();
    242   }
    243 
    244   void TestClosed() {
    245     NotificationPromo new_promo;
    246     new_promo.InitFromPrefs(promo_type_);
    247     EXPECT_FALSE(new_promo.closed_);
    248     EXPECT_TRUE(new_promo.CanShow());
    249 
    250     NotificationPromo::HandleClosed(promo_type_);
    251     new_promo.InitFromPrefs(promo_type_);
    252     EXPECT_TRUE(new_promo.closed_);
    253     EXPECT_FALSE(new_promo.CanShow());
    254 
    255     new_promo.closed_ = false;
    256     EXPECT_TRUE(new_promo.CanShow());
    257     new_promo.WritePrefs();
    258   }
    259 
    260   void TestPromoText() {
    261     notification_promo_.promo_text_.clear();
    262     EXPECT_FALSE(notification_promo_.CanShow());
    263 
    264     notification_promo_.promo_text_ = promo_text_;
    265     EXPECT_TRUE(notification_promo_.CanShow());
    266   }
    267 
    268   void TestTime() {
    269     const double now = base::Time::Now().ToDoubleT();
    270     const double qhour = 15 * 60;
    271 
    272     notification_promo_.group_ = 0;  // For simplicity.
    273 
    274     notification_promo_.start_ = now - qhour;
    275     notification_promo_.end_ = now + qhour;
    276     EXPECT_TRUE(notification_promo_.CanShow());
    277 
    278     // Start time has not arrived.
    279     notification_promo_.start_ = now + qhour;
    280     notification_promo_.end_ = now + qhour;
    281     EXPECT_FALSE(notification_promo_.CanShow());
    282 
    283     // End time has past.
    284     notification_promo_.start_ = now - qhour;
    285     notification_promo_.end_ = now - qhour;
    286     EXPECT_FALSE(notification_promo_.CanShow());
    287 
    288     notification_promo_.start_ = start_;
    289     notification_promo_.end_ = end_;
    290     EXPECT_TRUE(notification_promo_.CanShow());
    291   }
    292 
    293   void TestIncrement() {
    294     const double now = base::Time::Now().ToDoubleT();
    295     const double slice = 60;
    296 
    297     notification_promo_.num_groups_ = 18;
    298     notification_promo_.initial_segment_ = 5;
    299     notification_promo_.increment_ = 3;
    300     notification_promo_.time_slice_ = slice;
    301 
    302     notification_promo_.start_ = now - 1;
    303     notification_promo_.end_ = now + slice;
    304 
    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());
    310 
    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());
    317 
    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());
    324 
    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());
    331 
    332     // Test last increment.
    333     notification_promo_.start_ -= slice;
    334     EXPECT_TRUE(notification_promo_.CanShow());
    335   }
    336 
    337   const NotificationPromo& promo() const { return notification_promo_; }
    338 
    339  private:
    340   NotificationPromo notification_promo_;
    341   bool received_notification_;
    342   scoped_ptr<DictionaryValue> test_json_;
    343 
    344   NotificationPromo::PromoType promo_type_;
    345   std::string promo_text_;
    346 
    347   double start_;
    348   double end_;
    349 
    350   int num_groups_;
    351   int initial_segment_;
    352   int increment_;
    353   int time_slice_;
    354   int max_group_;
    355 
    356   int max_views_;
    357 
    358   bool closed_;
    359 };
    360 
    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;
    368 
    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.
    371   promo_test.Init("{"
    372                   "  \"ntp_notification_promo\": ["
    373                   "    {"
    374                   "      \"date\":"
    375                   "        ["
    376                   "          {"
    377                   "            \"start\":\"3 Aug 1999 9:26:06 GMT\","
    378                   "            \"end\":\"$1\""
    379                   "          }"
    380                   "        ],"
    381                   "      \"strings\":"
    382                   "        {"
    383                   "          \"NTP4_HOW_DO_YOU_FEEL_ABOUT_CHROME\":"
    384                   "              \"What do you think of Chrome?\""
    385                   "        },"
    386                   "      \"grouping\":"
    387                   "        {"
    388                   "          \"buckets\":1000,"
    389                   "          \"segment\":200,"
    390                   "          \"increment\":100,"
    391                   "          \"increment_frequency\":3600,"
    392                   "          \"increment_max\":400"
    393                   "        },"
    394                   "      \"payload\":"
    395                   "        {"
    396                   "          \"days_active\":7,"
    397                   "          \"install_age_days\":21"
    398                   "        },"
    399                   "      \"max_views\":30"
    400                   "    }"
    401                   "  ]"
    402                   "}",
    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);
    408 
    409   promo_test.InitPromoFromJson(true);
    410 
    411   // Second time should not trigger a notification.
    412   promo_test.InitPromoFromJson(false);
    413 
    414   promo_test.TestInitFromPrefs();
    415 
    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();
    424 }
    425 
    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;
    430 
    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.
    433   promo_test.Init("{"
    434                   "  \"ntp_notification_promo\": ["
    435                   "    {"
    436                   "      \"date\":"
    437                   "        ["
    438                   "          {"
    439                   "            \"start\":\"3 Aug 1999 9:26:06 GMT\","
    440                   "            \"end\":\"$1\""
    441                   "          }"
    442                   "        ],"
    443                   "      \"grouping\":"
    444                   "        {"
    445                   "          \"buckets\":1000,"
    446                   "          \"segment\":200,"
    447                   "          \"increment\":100,"
    448                   "          \"increment_frequency\":3600,"
    449                   "          \"increment_max\":400"
    450                   "        },"
    451                   "      \"payload\":"
    452                   "        {"
    453                   "          \"promo_message_short\":"
    454                   "              \"What do you think of Chrome?\","
    455                   "          \"days_active\":7,"
    456                   "          \"install_age_days\":21"
    457                   "        },"
    458                   "      \"max_views\":30"
    459                   "    }"
    460                   "  ]"
    461                   "}",
    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);
    467 
    468   promo_test.InitPromoFromJson(true);
    469   // Second time should not trigger a notification.
    470   promo_test.InitPromoFromJson(false);
    471   promo_test.TestInitFromPrefs();
    472 }
    473 
    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;
    478 
    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.
    481   promo_test.Init("{"
    482                   "  \"ntp_notification_promo\": ["
    483                   "    {"
    484                   "      \"date\":"
    485                   "        ["
    486                   "          {"
    487                   "            \"start\":\"3 Aug 1999 9:26:06 GMT\","
    488                   "            \"end\":\"$1\""
    489                   "          }"
    490                   "        ],"
    491                   "      \"grouping\":"
    492                   "        {"
    493                   "          \"buckets\":1000,"
    494                   "          \"segment\":200,"
    495                   "          \"increment\":100,"
    496                   "          \"increment_frequency\":3600,"
    497                   "          \"increment_max\":400"
    498                   "        },"
    499                   "      \"strings\":"
    500                   "        {"
    501                   "          \"bogus\":\"string\","
    502                   "          \"GOOD_STRING\":"
    503                   "              \"What do you think of Chrome?\""
    504                   "        },"
    505                   "      \"payload\":"
    506                   "        {"
    507                   "          \"promo_message_short\":"
    508                   "              \"GOOD_STRING\","
    509                   "          \"days_active\":7,"
    510                   "          \"install_age_days\":21"
    511                   "        },"
    512                   "      \"max_views\":30"
    513                   "    }"
    514                   "  ]"
    515                   "}",
    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);
    521 
    522   promo_test.InitPromoFromJson(true);
    523   // Second time should not trigger a notification.
    524   promo_test.InitPromoFromJson(false);
    525   promo_test.TestInitFromPrefs();
    526 }
    527 
    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.
    534 }
    535 
    536 #if defined(ENABLE_APP_LIST)
    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(prefs::kAppLauncherIsEnabled, true);
    580   EXPECT_FALSE(promo_test.promo().CanShow());
    581 }
    582 #endif
    583