Home | History | Annotate | Download | only in variations
      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 "chrome/browser/metrics/variations/variations_service.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/base64.h"
     10 #include "base/prefs/testing_pref_service.h"
     11 #include "base/sha1.h"
     12 #include "base/strings/string_number_conversions.h"
     13 #include "base/strings/string_util.h"
     14 #include "chrome/browser/web_resource/resource_request_allowed_notifier_test_util.h"
     15 #include "chrome/common/pref_names.h"
     16 #include "chrome/test/base/testing_browser_process.h"
     17 #include "chrome/test/base/testing_pref_service_syncable.h"
     18 #include "components/variations/proto/study.pb.h"
     19 #include "components/variations/proto/variations_seed.pb.h"
     20 #include "content/public/test/test_browser_thread.h"
     21 #include "net/base/url_util.h"
     22 #include "net/http/http_response_headers.h"
     23 #include "net/http/http_status_code.h"
     24 #include "net/url_request/test_url_fetcher_factory.h"
     25 #include "testing/gtest/include/gtest/gtest.h"
     26 
     27 #if defined(OS_CHROMEOS)
     28 #include "chrome/browser/chromeos/settings/cros_settings.h"
     29 #include "chrome/browser/chromeos/settings/device_settings_service.h"
     30 #include "chrome/browser/chromeos/settings/stub_cros_settings_provider.h"
     31 #endif
     32 
     33 namespace chrome_variations {
     34 
     35 namespace {
     36 
     37 // A test class used to validate expected functionality in VariationsService.
     38 class TestVariationsService : public VariationsService {
     39  public:
     40   TestVariationsService(TestRequestAllowedNotifier* test_notifier,
     41                         PrefService* local_state)
     42       : VariationsService(test_notifier, local_state, NULL),
     43         intercepts_fetch_(true),
     44         fetch_attempted_(false),
     45         seed_stored_(false) {
     46     // Set this so StartRepeatedVariationsSeedFetch can be called in tests.
     47     SetCreateTrialsFromSeedCalledForTesting(true);
     48   }
     49 
     50   virtual ~TestVariationsService() {
     51   }
     52 
     53   void set_intercepts_fetch(bool value) {
     54     intercepts_fetch_ = value;
     55   }
     56 
     57   bool fetch_attempted() const { return fetch_attempted_; }
     58 
     59   bool seed_stored() const { return seed_stored_; }
     60 
     61   virtual void DoActualFetch() OVERRIDE {
     62     if (intercepts_fetch_) {
     63       fetch_attempted_ = true;
     64       return;
     65     }
     66 
     67     VariationsService::DoActualFetch();
     68   }
     69 
     70  protected:
     71   virtual void StoreSeed(const std::string& seed_data,
     72                          const std::string& seed_signature,
     73                          const base::Time& date_fetched) OVERRIDE {
     74     seed_stored_ = true;
     75   }
     76 
     77  private:
     78   bool intercepts_fetch_;
     79   bool fetch_attempted_;
     80   bool seed_stored_;
     81 
     82   DISALLOW_COPY_AND_ASSIGN(TestVariationsService);
     83 };
     84 
     85 class TestVariationsServiceObserver : public VariationsService::Observer {
     86  public:
     87   TestVariationsServiceObserver()
     88       : best_effort_changes_notified_(0),
     89         crticial_changes_notified_(0) {
     90   }
     91   virtual ~TestVariationsServiceObserver() {
     92   }
     93 
     94   virtual void OnExperimentChangesDetected(Severity severity) OVERRIDE {
     95     switch (severity) {
     96       case BEST_EFFORT:
     97         ++best_effort_changes_notified_;
     98         break;
     99       case CRITICAL:
    100         ++crticial_changes_notified_;
    101         break;
    102     }
    103   }
    104 
    105   int best_effort_changes_notified() const {
    106     return best_effort_changes_notified_;
    107   }
    108 
    109   int crticial_changes_notified() const {
    110     return crticial_changes_notified_;
    111   }
    112 
    113  private:
    114   // Number of notification received with BEST_EFFORT severity.
    115   int best_effort_changes_notified_;
    116 
    117   // Number of notification received with CRITICAL severity.
    118   int crticial_changes_notified_;
    119 
    120   DISALLOW_COPY_AND_ASSIGN(TestVariationsServiceObserver);
    121 };
    122 
    123 // Populates |seed| with simple test data. The resulting seed will contain one
    124 // study called "test", which contains one experiment called "abc" with
    125 // probability weight 100. |seed|'s study field will be cleared before adding
    126 // the new study.
    127 variations::VariationsSeed CreateTestSeed() {
    128   variations::VariationsSeed seed;
    129   variations::Study* study = seed.add_study();
    130   study->set_name("test");
    131   study->set_default_experiment_name("abc");
    132   variations::Study_Experiment* experiment = study->add_experiment();
    133   experiment->set_name("abc");
    134   experiment->set_probability_weight(100);
    135   seed.set_serial_number("123");
    136   return seed;
    137 }
    138 
    139 // Serializes |seed| to protobuf binary format.
    140 std::string SerializeSeed(const variations::VariationsSeed& seed) {
    141   std::string serialized_seed;
    142   seed.SerializeToString(&serialized_seed);
    143   return serialized_seed;
    144 }
    145 
    146 // Simulates a variations service response by setting a date header and the
    147 // specified HTTP |response_code| on |fetcher|.
    148 void SimulateServerResponse(int response_code, net::TestURLFetcher* fetcher) {
    149   ASSERT_TRUE(fetcher);
    150   scoped_refptr<net::HttpResponseHeaders> headers(
    151       new net::HttpResponseHeaders("date:Wed, 13 Feb 2013 00:25:24 GMT\0\0"));
    152   fetcher->set_response_headers(headers);
    153   fetcher->set_response_code(response_code);
    154 }
    155 
    156 }  // namespace
    157 
    158 class VariationsServiceTest : public ::testing::Test {
    159  protected:
    160   VariationsServiceTest() {}
    161 
    162  private:
    163 #if defined(OS_CHROMEOS)
    164   // Not used directly. Initializes CrosSettings for testing.
    165   chromeos::ScopedTestDeviceSettingsService test_device_settings_service_;
    166   chromeos::ScopedTestCrosSettings test_cros_settings_;
    167 #endif
    168 
    169   DISALLOW_COPY_AND_ASSIGN(VariationsServiceTest);
    170 };
    171 
    172 #if !defined(OS_CHROMEOS)
    173 TEST_F(VariationsServiceTest, VariationsURLIsValid) {
    174 #if defined(OS_ANDROID)
    175   // Android uses profile prefs as the PrefService to generate the URL.
    176   TestingPrefServiceSyncable prefs;
    177   VariationsService::RegisterProfilePrefs(prefs.registry());
    178 #else
    179   TestingPrefServiceSimple prefs;
    180   VariationsService::RegisterPrefs(prefs.registry());
    181 #endif
    182   const std::string default_variations_url =
    183       VariationsService::GetDefaultVariationsServerURLForTesting();
    184 
    185   std::string value;
    186   GURL url = VariationsService::GetVariationsServerURL(&prefs);
    187   EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true));
    188   EXPECT_FALSE(net::GetValueForKeyInQuery(url, "restrict", &value));
    189 
    190   prefs.SetString(prefs::kVariationsRestrictParameter, "restricted");
    191   url = VariationsService::GetVariationsServerURL(&prefs);
    192   EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true));
    193   EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value));
    194   EXPECT_EQ("restricted", value);
    195 }
    196 #else
    197 class VariationsServiceTestChromeOS : public VariationsServiceTest {
    198  protected:
    199   VariationsServiceTestChromeOS() {}
    200 
    201   virtual void SetUp() OVERRIDE {
    202     cros_settings_ = chromeos::CrosSettings::Get();
    203     DCHECK(cros_settings_ != NULL);
    204     // Remove the real DeviceSettingsProvider and replace it with a stub that
    205     // allows modifications in a test.
    206     device_settings_provider_ = cros_settings_->GetProvider(
    207         chromeos::kReportDeviceVersionInfo);
    208     EXPECT_TRUE(device_settings_provider_ != NULL);
    209     EXPECT_TRUE(cros_settings_->RemoveSettingsProvider(
    210         device_settings_provider_));
    211     cros_settings_->AddSettingsProvider(&stub_settings_provider_);
    212   }
    213 
    214   virtual void TearDown() OVERRIDE {
    215     // Restore the real DeviceSettingsProvider.
    216     EXPECT_TRUE(
    217         cros_settings_->RemoveSettingsProvider(&stub_settings_provider_));
    218     cros_settings_->AddSettingsProvider(device_settings_provider_);
    219   }
    220 
    221   void SetVariationsRestrictParameterPolicyValue(std::string value) {
    222     cros_settings_->SetString(chromeos::kVariationsRestrictParameter, value);
    223   }
    224 
    225  private:
    226   chromeos::CrosSettings* cros_settings_;
    227   chromeos::StubCrosSettingsProvider stub_settings_provider_;
    228   chromeos::CrosSettingsProvider* device_settings_provider_;
    229 
    230   DISALLOW_COPY_AND_ASSIGN(VariationsServiceTestChromeOS);
    231 };
    232 
    233 TEST_F(VariationsServiceTestChromeOS, VariationsURLIsValid) {
    234   TestingPrefServiceSimple prefs;
    235   VariationsService::RegisterPrefs(prefs.registry());
    236   const std::string default_variations_url =
    237       VariationsService::GetDefaultVariationsServerURLForTesting();
    238 
    239   std::string value;
    240   GURL url = VariationsService::GetVariationsServerURL(&prefs);
    241   EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true));
    242   EXPECT_FALSE(net::GetValueForKeyInQuery(url, "restrict", &value));
    243 
    244   SetVariationsRestrictParameterPolicyValue("restricted");
    245   url = VariationsService::GetVariationsServerURL(&prefs);
    246   EXPECT_TRUE(StartsWithASCII(url.spec(), default_variations_url, true));
    247   EXPECT_TRUE(net::GetValueForKeyInQuery(url, "restrict", &value));
    248   EXPECT_EQ("restricted", value);
    249 }
    250 #endif
    251 
    252 TEST_F(VariationsServiceTest, VariationsURLHasOSNameParam) {
    253   TestingPrefServiceSimple prefs;
    254   VariationsService::RegisterPrefs(prefs.registry());
    255   const GURL url = VariationsService::GetVariationsServerURL(&prefs);
    256 
    257   std::string value;
    258   EXPECT_TRUE(net::GetValueForKeyInQuery(url, "osname", &value));
    259   EXPECT_FALSE(value.empty());
    260 }
    261 
    262 TEST_F(VariationsServiceTest, RequestsInitiallyNotAllowed) {
    263   base::MessageLoopForUI message_loop;
    264   content::TestBrowserThread ui_thread(content::BrowserThread::UI,
    265                                        &message_loop);
    266   TestingPrefServiceSimple prefs;
    267   VariationsService::RegisterPrefs(prefs.registry());
    268 
    269   // Pass ownership to TestVariationsService, but keep a weak pointer to
    270   // manipulate it for this test.
    271   TestRequestAllowedNotifier* test_notifier = new TestRequestAllowedNotifier;
    272   TestVariationsService test_service(test_notifier, &prefs);
    273 
    274   // Force the notifier to initially disallow requests.
    275   test_notifier->SetRequestsAllowedOverride(false);
    276   test_service.StartRepeatedVariationsSeedFetch();
    277   EXPECT_FALSE(test_service.fetch_attempted());
    278 
    279   test_notifier->NotifyObserver();
    280   EXPECT_TRUE(test_service.fetch_attempted());
    281 }
    282 
    283 TEST_F(VariationsServiceTest, RequestsInitiallyAllowed) {
    284   base::MessageLoopForUI message_loop;
    285   content::TestBrowserThread ui_thread(content::BrowserThread::UI,
    286                                        &message_loop);
    287   TestingPrefServiceSimple prefs;
    288   VariationsService::RegisterPrefs(prefs.registry());
    289 
    290   // Pass ownership to TestVariationsService, but keep a weak pointer to
    291   // manipulate it for this test.
    292   TestRequestAllowedNotifier* test_notifier = new TestRequestAllowedNotifier;
    293   TestVariationsService test_service(test_notifier, &prefs);
    294 
    295   test_notifier->SetRequestsAllowedOverride(true);
    296   test_service.StartRepeatedVariationsSeedFetch();
    297   EXPECT_TRUE(test_service.fetch_attempted());
    298 }
    299 
    300 TEST_F(VariationsServiceTest, SeedStoredWhenOKStatus) {
    301   base::MessageLoop message_loop;
    302   content::TestBrowserThread io_thread(content::BrowserThread::IO,
    303                                        &message_loop);
    304   TestingPrefServiceSimple prefs;
    305   VariationsService::RegisterPrefs(prefs.registry());
    306 
    307   TestVariationsService service(new TestRequestAllowedNotifier, &prefs);
    308   service.set_intercepts_fetch(false);
    309 
    310   net::TestURLFetcherFactory factory;
    311   service.DoActualFetch();
    312 
    313   net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
    314   SimulateServerResponse(net::HTTP_OK, fetcher);
    315   fetcher->SetResponseString(SerializeSeed(CreateTestSeed()));
    316 
    317   EXPECT_FALSE(service.seed_stored());
    318   service.OnURLFetchComplete(fetcher);
    319   EXPECT_TRUE(service.seed_stored());
    320 }
    321 
    322 TEST_F(VariationsServiceTest, SeedNotStoredWhenNonOKStatus) {
    323   const int non_ok_status_codes[] = {
    324     net::HTTP_NO_CONTENT,
    325     net::HTTP_NOT_MODIFIED,
    326     net::HTTP_NOT_FOUND,
    327     net::HTTP_INTERNAL_SERVER_ERROR,
    328     net::HTTP_SERVICE_UNAVAILABLE,
    329   };
    330 
    331   base::MessageLoop message_loop;
    332   content::TestBrowserThread io_thread(content::BrowserThread::IO,
    333                                        &message_loop);
    334   TestingPrefServiceSimple prefs;
    335   VariationsService::RegisterPrefs(prefs.registry());
    336 
    337   VariationsService service(new TestRequestAllowedNotifier, &prefs, NULL);
    338   for (size_t i = 0; i < arraysize(non_ok_status_codes); ++i) {
    339     net::TestURLFetcherFactory factory;
    340     service.DoActualFetch();
    341     EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue());
    342 
    343     net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
    344     SimulateServerResponse(non_ok_status_codes[i], fetcher);
    345     service.OnURLFetchComplete(fetcher);
    346 
    347     EXPECT_TRUE(prefs.FindPreference(prefs::kVariationsSeed)->IsDefaultValue());
    348   }
    349 }
    350 
    351 TEST_F(VariationsServiceTest, SeedDateUpdatedOn304Status) {
    352   base::MessageLoop message_loop;
    353   content::TestBrowserThread io_thread(content::BrowserThread::IO,
    354                                        &message_loop);
    355   TestingPrefServiceSimple prefs;
    356   VariationsService::RegisterPrefs(prefs.registry());
    357 
    358   VariationsService service(new TestRequestAllowedNotifier, &prefs, NULL);
    359   net::TestURLFetcherFactory factory;
    360   service.DoActualFetch();
    361   EXPECT_TRUE(
    362       prefs.FindPreference(prefs::kVariationsSeedDate)->IsDefaultValue());
    363 
    364   net::TestURLFetcher* fetcher = factory.GetFetcherByID(0);
    365   SimulateServerResponse(net::HTTP_NOT_MODIFIED, fetcher);
    366   service.OnURLFetchComplete(fetcher);
    367   EXPECT_FALSE(
    368       prefs.FindPreference(prefs::kVariationsSeedDate)->IsDefaultValue());
    369 }
    370 
    371 TEST_F(VariationsServiceTest, Observer) {
    372   TestingPrefServiceSimple prefs;
    373   VariationsService::RegisterPrefs(prefs.registry());
    374   VariationsService service(new TestRequestAllowedNotifier, &prefs, NULL);
    375 
    376   struct {
    377     int normal_count;
    378     int best_effort_count;
    379     int critical_count;
    380     int expected_best_effort_notifications;
    381     int expected_crtical_notifications;
    382   } cases[] = {
    383       {0, 0, 0, 0, 0},
    384       {1, 0, 0, 0, 0},
    385       {10, 0, 0, 0, 0},
    386       {0, 1, 0, 1, 0},
    387       {0, 10, 0, 1, 0},
    388       {0, 0, 1, 0, 1},
    389       {0, 0, 10, 0, 1},
    390       {0, 1, 1, 0, 1},
    391       {1, 1, 1, 0, 1},
    392       {1, 1, 0, 1, 0},
    393       {1, 0, 1, 0, 1},
    394   };
    395 
    396   for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) {
    397     TestVariationsServiceObserver observer;
    398     service.AddObserver(&observer);
    399 
    400     variations::VariationsSeedSimulator::Result result;
    401     result.normal_group_change_count = cases[i].normal_count;
    402     result.kill_best_effort_group_change_count = cases[i].best_effort_count;
    403     result.kill_critical_group_change_count = cases[i].critical_count;
    404     service.NotifyObservers(result);
    405 
    406     EXPECT_EQ(cases[i].expected_best_effort_notifications,
    407               observer.best_effort_changes_notified()) << i;
    408     EXPECT_EQ(cases[i].expected_crtical_notifications,
    409               observer.crticial_changes_notified()) << i;
    410 
    411     service.RemoveObserver(&observer);
    412   }
    413 }
    414 
    415 }  // namespace chrome_variations
    416