Home | History | Annotate | Download | only in history
      1 // Copyright 2013 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 "chrome/browser/history/most_visited_tiles_experiment.h"
      6 
      7 #include <algorithm>
      8 #include <sstream>
      9 
     10 #include "base/metrics/field_trial.h"
     11 #include "base/metrics/histogram.h"
     12 #include "base/metrics/statistics_recorder.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "base/values.h"
     15 #include "chrome/browser/history/history_types.h"
     16 #include "chrome/common/instant_types.h"
     17 #include "chrome/common/metrics/entropy_provider.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 #include "url/gurl.h"
     20 
     21 namespace history {
     22 
     23 namespace {
     24 
     25 // Constants for the most visited tile placement field trial.
     26 // See field trial config (MostVisitedTilePlacement.json) for details.
     27 const char kMostVisitedFieldTrialName[] = "MostVisitedTilePlacement";
     28 const char kOneEightAGroupName[] = "OneEight_A_Flipped";
     29 const char kOneFourAGroupName[] = "OneFour_A_Flipped";
     30 const char kDontShowOpenURLsGroupName[] = "DontShowOpenTabs";
     31 const char kGmailURL[] = "http://www.gmail.com/";
     32 // Name of histogram tracking types of actions carried out by the field trial.
     33 const char kMostVisitedExperimentHistogramName[] =
     34     "NewTabPage.MostVisitedTilePlacementExperiment";
     35 // Minimum number of Most Visited suggestions required in order for the Most
     36 // Visited Field Trial to remove a URL already open in the browser.
     37 const size_t kMinUrlSuggestions = 8;
     38 
     39 // The indexes of the tiles that are affected in the experiment.
     40 enum FlippedIndexes {
     41   TILE_ONE = 0,
     42   TILE_FOUR = 3,
     43   TILE_EIGHT = 7
     44 };
     45 
     46 // Creates a DictionaryValue using |url| and appends to |list|.
     47 void AppendURLToListValue(const std::string& url_string,
     48                           base::ListValue* list) {
     49   DictionaryValue* page_value = new DictionaryValue();
     50   page_value->SetString("url", url_string);
     51   list->Append(page_value);
     52 }
     53 
     54 // Creates an InstantMostVisitedItem using |url| and appends to |list|.
     55 void AppendInstantURLToVector(const std::string& url_string,
     56                               std::vector<InstantMostVisitedItem>* list) {
     57   InstantMostVisitedItem item;
     58   item.url = GURL(url_string);
     59   list->push_back(item);
     60 }
     61 
     62 // Creates an MostVisitedURL using |url| and appends to |list|.
     63 void AppendMostVisitedURLToVector(const std::string& url_string,
     64                                   std::vector<history::MostVisitedURL>* list) {
     65   history::MostVisitedURL most_visited;
     66   most_visited.url = GURL(url_string);
     67   list->push_back(most_visited);
     68 }
     69 
     70 void SetUpMaybeShuffle(const int& max_urls,
     71                        MostVisitedURLList* most_visited_urls,
     72                        MostVisitedURLList* test_urls) {
     73   // |most_visited_urls| must have > 8 MostVisitedURLs for any URLs to be
     74   // flipped by experiment.
     75   for (int i = 0; i < max_urls; ++i) {
     76     std::string url;
     77     base::SStringPrintf(&url, "http://www.test%d.com", i);
     78     AppendMostVisitedURLToVector(url, most_visited_urls);
     79     AppendMostVisitedURLToVector(url, test_urls);
     80   }
     81 }
     82 
     83 }  // namespace
     84 
     85 class MostVisitedTilesExperimentTest : public testing::Test {
     86  public:
     87   MostVisitedTilesExperimentTest()
     88       : histogram_(NULL),
     89         field_trial_list_(new metrics::SHA1EntropyProvider("foo")) {}
     90 
     91   virtual ~MostVisitedTilesExperimentTest() {}
     92 
     93  protected:
     94   virtual void SetUp() OVERRIDE {
     95     base::StatisticsRecorder::Initialize();
     96     previous_metrics_count_.resize(NUM_NTP_TILE_EXPERIMENT_ACTIONS, 0);
     97     base::HistogramBase* histogram = GetHistogram();
     98     if (histogram) {
     99       scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples());
    100       if (samples.get()) {
    101         for (int state = NTP_TILE_EXPERIMENT_ACTION_REMOVED_URL;
    102              state < NUM_NTP_TILE_EXPERIMENT_ACTIONS;
    103              ++state) {
    104           previous_metrics_count_[state] = samples->GetCount(state);
    105         }
    106       }
    107     }
    108   }
    109 
    110   void ValidateMetrics(const base::HistogramBase::Sample& value) {
    111     base::HistogramBase* histogram = GetHistogram();
    112     ASSERT_TRUE(histogram != NULL);
    113     scoped_ptr<base::HistogramSamples> samples(histogram->SnapshotSamples());
    114     if (samples.get()) {
    115       for (int state = NTP_TILE_EXPERIMENT_ACTION_REMOVED_URL;
    116            state < NUM_NTP_TILE_EXPERIMENT_ACTIONS;
    117            ++state) {
    118         if (state == value) {
    119           EXPECT_EQ(previous_metrics_count_[state] + 1,
    120                     samples->GetCount(state));
    121         } else {
    122           EXPECT_EQ(previous_metrics_count_[state], samples->GetCount(state));
    123         }
    124       }
    125     }
    126   }
    127 
    128  private:
    129   base::HistogramBase* GetHistogram() {
    130     if (!histogram_) {
    131       histogram_ = base::StatisticsRecorder::FindHistogram(
    132           kMostVisitedExperimentHistogramName);
    133     }
    134     return histogram_;
    135   }
    136 
    137   // Owned by base::StatisticsRecorder
    138   base::HistogramBase* histogram_;
    139   base::FieldTrialList field_trial_list_;
    140   std::vector<int> previous_metrics_count_;
    141 
    142   DISALLOW_COPY_AND_ASSIGN(MostVisitedTilesExperimentTest);
    143 };
    144 
    145 // For pre-instant extended clients.
    146 TEST_F(MostVisitedTilesExperimentTest,
    147        RemovePageValuesMatchingOpenTabsTooFewURLs) {
    148   base::FieldTrialList::CreateFieldTrial(kMostVisitedFieldTrialName,
    149                                          kDontShowOpenURLsGroupName);
    150 
    151   // Ensure the field trial is created with the correct group.
    152   EXPECT_TRUE(MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled());
    153 
    154   std::set<std::string> open_urls;
    155   open_urls.insert(kGmailURL);
    156 
    157   base::ListValue pages_value;
    158   AppendURLToListValue(kGmailURL, &pages_value);
    159 
    160   // Test the method when there are not enough URLs to force removal.
    161   MostVisitedTilesExperiment::RemovePageValuesMatchingOpenTabs(
    162       open_urls, &pages_value);
    163   DictionaryValue gmail_value;
    164   gmail_value.SetString("url", kGmailURL);
    165   // Ensure the open url has not been removed from |pages_value|.
    166   EXPECT_NE(pages_value.end(), pages_value.Find(gmail_value));
    167 
    168   // Ensure counts have been incremented correctly.
    169   EXPECT_NO_FATAL_FAILURE(
    170       ValidateMetrics(NTP_TILE_EXPERIMENT_ACTION_DID_NOT_REMOVE_URL));
    171 }
    172 
    173 // For pre-instant extended clients.
    174 TEST_F(MostVisitedTilesExperimentTest, RemovePageValuesMatchingOpenTabs) {
    175   base::FieldTrialList::CreateFieldTrial(kMostVisitedFieldTrialName,
    176                                          kDontShowOpenURLsGroupName);
    177 
    178   // Ensure the field trial is created with the correct group.
    179   EXPECT_TRUE(MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled());
    180 
    181   std::set<std::string> open_urls;
    182   open_urls.insert(kGmailURL);
    183 
    184   base::ListValue pages_value;
    185   AppendURLToListValue(kGmailURL, &pages_value);
    186 
    187   // |pages_value| must have > 8 page values for any URLs to be removed by
    188   // experiment.
    189   for (size_t i = 0; i < kMinUrlSuggestions; ++i) {
    190     std::string url;
    191     base::SStringPrintf(&url, "http://www.test%d.com", static_cast<int>(i));
    192     AppendURLToListValue(url, &pages_value);
    193   }
    194 
    195   // Call method with enough URLs to force removal.
    196   MostVisitedTilesExperiment::RemovePageValuesMatchingOpenTabs(
    197       open_urls, &pages_value);
    198   // Ensure the open url has been removed from |pages_value|.
    199   DictionaryValue gmail_value;
    200   gmail_value.SetString("url", kGmailURL);
    201   EXPECT_EQ(pages_value.end(), pages_value.Find(gmail_value));
    202 
    203   // Ensure counts have been incremented correctly.
    204   EXPECT_NO_FATAL_FAILURE(
    205       ValidateMetrics(NTP_TILE_EXPERIMENT_ACTION_REMOVED_URL));
    206 }
    207 
    208 // For instant extended clients.
    209 TEST_F(MostVisitedTilesExperimentTest, RemoveItemsMatchingOpenTabsTooFewURLs) {
    210   base::FieldTrialList::CreateFieldTrial(kMostVisitedFieldTrialName,
    211                                          kDontShowOpenURLsGroupName);
    212 
    213   // Ensure the field trial is created with the correct group.
    214   EXPECT_TRUE(MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled());
    215 
    216   std::set<std::string> open_urls;
    217   open_urls.insert(kGmailURL);
    218   std::vector<InstantMostVisitedItem> items;
    219   AppendInstantURLToVector(kGmailURL, &items);
    220 
    221   // Call the method when there are not enough URLs to force removal.
    222   MostVisitedTilesExperiment::RemoveItemsMatchingOpenTabs(open_urls, &items);
    223 
    224   // Ensure the open url has not been removed from |items|.
    225   for (size_t i = 0; i < items.size(); i++) {
    226     const std::string& item_url = items[i].url.spec();
    227     EXPECT_NE(0u, open_urls.count(item_url));
    228   }
    229 
    230   // Ensure counts have been incremented correctly.
    231   EXPECT_NO_FATAL_FAILURE(
    232       ValidateMetrics(NTP_TILE_EXPERIMENT_ACTION_DID_NOT_REMOVE_URL));
    233 }
    234 
    235 // For instant extended clients.
    236 TEST_F(MostVisitedTilesExperimentTest, RemoveItemsMatchingOpenTabs) {
    237   base::FieldTrialList::CreateFieldTrial(
    238       kMostVisitedFieldTrialName,
    239       kDontShowOpenURLsGroupName);
    240 
    241   // Ensure the field trial is created with the correct group.
    242   EXPECT_TRUE(MostVisitedTilesExperiment::IsDontShowOpenURLsEnabled());
    243 
    244   std::set<std::string> open_urls;
    245   open_urls.insert(kGmailURL);
    246   std::vector<InstantMostVisitedItem> items;
    247   AppendInstantURLToVector(kGmailURL, &items);
    248 
    249   // |items| must have > 8 InstantMostVisitedItems for any URLs to be removed by
    250   // experiment.
    251   for (size_t i = 0; i < kMinUrlSuggestions; ++i) {
    252     std::string url;
    253     base::SStringPrintf(&url, "http://www.test%d.com", static_cast<int>(i));
    254     AppendInstantURLToVector(url, &items);
    255   }
    256 
    257   // Call method with enough URLs to force removal.
    258   MostVisitedTilesExperiment::RemoveItemsMatchingOpenTabs(open_urls, &items);
    259 
    260   // Ensure the open URL has been removed from |items|.
    261   for (size_t i = 0; i < items.size(); i++) {
    262     const std::string& item_url = items[i].url.spec();
    263     EXPECT_EQ(0u, open_urls.count(item_url));
    264   }
    265 
    266   // Ensure counts have been incremented correctly.
    267   EXPECT_NO_FATAL_FAILURE(
    268       ValidateMetrics(NTP_TILE_EXPERIMENT_ACTION_REMOVED_URL));
    269 }
    270 
    271 TEST_F(MostVisitedTilesExperimentTest, MaybeShuffleOneEight) {
    272   base::FieldTrialList::CreateFieldTrial(kMostVisitedFieldTrialName,
    273                                          kOneEightAGroupName);
    274 
    275   // Ensure the field trial is created with the correct group.
    276   EXPECT_EQ(kOneEightAGroupName,
    277             base::FieldTrialList::FindFullName(kMostVisitedFieldTrialName));
    278 
    279   MostVisitedURLList most_visited_urls;
    280   MostVisitedURLList test_urls;
    281   SetUpMaybeShuffle(kMinUrlSuggestions, &most_visited_urls, &test_urls);
    282 
    283   history::MostVisitedTilesExperiment::MaybeShuffle(&most_visited_urls);
    284   // Ensure the 1st and 8th URLs have been switched.
    285   EXPECT_EQ(most_visited_urls[TILE_ONE].url.spec(),
    286             test_urls[TILE_EIGHT].url.spec());
    287 }
    288 
    289 TEST_F(MostVisitedTilesExperimentTest, MaybeShuffleOneEightTooFewURLs) {
    290   base::FieldTrialList::CreateFieldTrial(kMostVisitedFieldTrialName,
    291                                          kOneEightAGroupName);
    292 
    293   // Ensure the field trial is created with the correct group.
    294   EXPECT_EQ(kOneEightAGroupName,
    295             base::FieldTrialList::FindFullName(kMostVisitedFieldTrialName));
    296 
    297   MostVisitedURLList most_visited_urls;
    298   MostVisitedURLList test_urls;
    299   // If |most_visited_urls| has < 8 URLs, experiment will not flip any tiles.
    300   SetUpMaybeShuffle(kMinUrlSuggestions - 1, &most_visited_urls, &test_urls);
    301 
    302   history::MostVisitedTilesExperiment::MaybeShuffle(&most_visited_urls);
    303   // Ensure no URLs have been switched.
    304   EXPECT_EQ(most_visited_urls[TILE_ONE].url.spec(),
    305             test_urls[TILE_ONE].url.spec());
    306   EXPECT_EQ(most_visited_urls[TILE_EIGHT - 1].url.spec(),
    307             test_urls[TILE_EIGHT - 1].url.spec());
    308 
    309   // Ensure counts are correct.
    310   EXPECT_NO_FATAL_FAILURE(
    311       ValidateMetrics(NTP_TILE_EXPERIMENT_ACTION_TOO_FEW_URLS_TILES_1_8));
    312 }
    313 
    314 TEST_F(MostVisitedTilesExperimentTest, MaybeShuffleOneFour) {
    315   base::FieldTrialList::CreateFieldTrial(kMostVisitedFieldTrialName,
    316                                          kOneFourAGroupName);
    317 
    318   // Ensure the field trial is created with the correct group.
    319   EXPECT_EQ(kOneFourAGroupName,
    320             base::FieldTrialList::FindFullName(kMostVisitedFieldTrialName));
    321 
    322   MostVisitedURLList most_visited_urls;
    323   MostVisitedURLList test_urls;
    324   SetUpMaybeShuffle(kMinUrlSuggestions, &most_visited_urls, &test_urls);
    325 
    326   history::MostVisitedTilesExperiment::MaybeShuffle(&most_visited_urls);
    327   // Ensure the 1st and 4th URLs have been switched.
    328   EXPECT_EQ(most_visited_urls[TILE_ONE].url.spec(),
    329             test_urls[TILE_FOUR].url.spec());
    330 }
    331 
    332 TEST_F(MostVisitedTilesExperimentTest, MaybeShuffleOneFourTooFewURLs) {
    333   base::FieldTrialList::CreateFieldTrial(
    334       kMostVisitedFieldTrialName,
    335       kOneFourAGroupName);
    336 
    337   // Ensure the field trial is created with the correct group.
    338   EXPECT_EQ(kOneFourAGroupName,
    339             base::FieldTrialList::FindFullName(kMostVisitedFieldTrialName));
    340 
    341   MostVisitedURLList most_visited_urls;
    342   MostVisitedURLList test_urls;
    343   // If |most_visited_urls| has < 4 URLs, experiment will not flip any tiles.
    344   SetUpMaybeShuffle(kMinUrlSuggestions - 5, &most_visited_urls, &test_urls);
    345 
    346   history::MostVisitedTilesExperiment::MaybeShuffle(&most_visited_urls);
    347   // Ensure no URLs have been switched.
    348   EXPECT_EQ(most_visited_urls[TILE_ONE].url.spec(),
    349            test_urls[TILE_ONE].url.spec());
    350   EXPECT_EQ(most_visited_urls[TILE_FOUR-1].url.spec(),
    351             test_urls[TILE_FOUR-1].url.spec());
    352 
    353   // Ensure counts are correct.
    354   EXPECT_NO_FATAL_FAILURE(
    355       ValidateMetrics(NTP_TILE_EXPERIMENT_ACTION_TOO_FEW_URLS_TILES_1_4));
    356 }
    357 
    358 }  // namespace history
    359