Home | History | Annotate | Download | only in browser
      1 // Copyright (c) 2011 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 "base/prefs/pref_registry_simple.h"
      6 #include "base/prefs/testing_pref_service.h"
      7 #include "base/strings/string_number_conversions.h"
      8 #include "base/strings/utf_string_conversions.h"
      9 #include "base/values.h"
     10 #include "chrome/browser/about_flags.h"
     11 #include "chrome/browser/pref_service_flags_storage.h"
     12 #include "chrome/common/chrome_switches.h"
     13 #include "chrome/common/pref_names.h"
     14 #include "grit/chromium_strings.h"
     15 #include "testing/gtest/include/gtest/gtest.h"
     16 
     17 const char kFlags1[] = "flag1";
     18 const char kFlags2[] = "flag2";
     19 const char kFlags3[] = "flag3";
     20 const char kFlags4[] = "flag4";
     21 const char kFlags5[] = "flag5";
     22 
     23 const char kSwitch1[] = "switch";
     24 const char kSwitch2[] = "switch2";
     25 const char kSwitch3[] = "switch3";
     26 const char kValueForSwitch2[] = "value_for_switch2";
     27 
     28 const char kMultiSwitch1[] = "multi_switch1";
     29 const char kMultiSwitch2[] = "multi_switch2";
     30 const char kValueForMultiSwitch2[] = "value_for_multi_switch2";
     31 
     32 const char kEnableDisableValue1[] = "value1";
     33 const char kEnableDisableValue2[] = "value2";
     34 
     35 namespace about_flags {
     36 
     37 const Experiment::Choice kMultiChoices[] = {
     38   { IDS_PRODUCT_NAME, "", "" },
     39   { IDS_PRODUCT_NAME, kMultiSwitch1, "" },
     40   { IDS_PRODUCT_NAME, kMultiSwitch2, kValueForMultiSwitch2 },
     41 };
     42 
     43 // The experiments that are set for these tests. The 3rd experiment is not
     44 // supported on the current platform, all others are.
     45 static Experiment kExperiments[] = {
     46   {
     47     kFlags1,
     48     IDS_PRODUCT_NAME,
     49     IDS_PRODUCT_NAME,
     50     0,  // Ends up being mapped to the current platform.
     51     Experiment::SINGLE_VALUE,
     52     kSwitch1,
     53     "",
     54     NULL,
     55     NULL,
     56     NULL,
     57     0
     58   },
     59   {
     60     kFlags2,
     61     IDS_PRODUCT_NAME,
     62     IDS_PRODUCT_NAME,
     63     0,  // Ends up being mapped to the current platform.
     64     Experiment::SINGLE_VALUE,
     65     kSwitch2,
     66     kValueForSwitch2,
     67     NULL,
     68     NULL,
     69     NULL,
     70     0
     71   },
     72   {
     73     kFlags3,
     74     IDS_PRODUCT_NAME,
     75     IDS_PRODUCT_NAME,
     76     0,  // This ends up enabling for an OS other than the current.
     77     Experiment::SINGLE_VALUE,
     78     kSwitch3,
     79     "",
     80     NULL,
     81     NULL,
     82     NULL,
     83     0
     84   },
     85   {
     86     kFlags4,
     87     IDS_PRODUCT_NAME,
     88     IDS_PRODUCT_NAME,
     89     0,  // Ends up being mapped to the current platform.
     90     Experiment::MULTI_VALUE,
     91     "",
     92     "",
     93     "",
     94     "",
     95     kMultiChoices,
     96     arraysize(kMultiChoices)
     97   },
     98   {
     99     kFlags5,
    100     IDS_PRODUCT_NAME,
    101     IDS_PRODUCT_NAME,
    102     0,  // Ends up being mapped to the current platform.
    103     Experiment::ENABLE_DISABLE_VALUE,
    104     kSwitch1,
    105     kEnableDisableValue1,
    106     kSwitch2,
    107     kEnableDisableValue2,
    108     NULL,
    109     3
    110   },
    111 };
    112 
    113 class AboutFlagsTest : public ::testing::Test {
    114  protected:
    115   AboutFlagsTest() : flags_storage_(&prefs_) {
    116     prefs_.registry()->RegisterListPref(prefs::kEnabledLabsExperiments);
    117     testing::ClearState();
    118   }
    119 
    120   virtual void SetUp() OVERRIDE {
    121     for (size_t i = 0; i < arraysize(kExperiments); ++i)
    122       kExperiments[i].supported_platforms = GetCurrentPlatform();
    123 
    124     int os_other_than_current = 1;
    125     while (os_other_than_current == GetCurrentPlatform())
    126       os_other_than_current <<= 1;
    127     kExperiments[2].supported_platforms = os_other_than_current;
    128 
    129     testing::SetExperiments(kExperiments, arraysize(kExperiments));
    130   }
    131 
    132   virtual void TearDown() OVERRIDE {
    133     testing::SetExperiments(NULL, 0);
    134   }
    135 
    136   TestingPrefServiceSimple prefs_;
    137   PrefServiceFlagsStorage flags_storage_;
    138 };
    139 
    140 
    141 TEST_F(AboutFlagsTest, NoChangeNoRestart) {
    142   EXPECT_FALSE(IsRestartNeededToCommitChanges());
    143   SetExperimentEnabled(&flags_storage_, kFlags1, false);
    144   EXPECT_FALSE(IsRestartNeededToCommitChanges());
    145 }
    146 
    147 TEST_F(AboutFlagsTest, ChangeNeedsRestart) {
    148   EXPECT_FALSE(IsRestartNeededToCommitChanges());
    149   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    150   EXPECT_TRUE(IsRestartNeededToCommitChanges());
    151 }
    152 
    153 TEST_F(AboutFlagsTest, MultiFlagChangeNeedsRestart) {
    154   const Experiment& experiment = kExperiments[3];
    155   ASSERT_EQ(kFlags4, experiment.internal_name);
    156   EXPECT_FALSE(IsRestartNeededToCommitChanges());
    157   // Enable the 2nd choice of the multi-value.
    158   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
    159   EXPECT_TRUE(IsRestartNeededToCommitChanges());
    160   testing::ClearState();
    161   EXPECT_FALSE(IsRestartNeededToCommitChanges());
    162   // Enable the default choice now.
    163   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
    164   EXPECT_TRUE(IsRestartNeededToCommitChanges());
    165 }
    166 
    167 TEST_F(AboutFlagsTest, AddTwoFlagsRemoveOne) {
    168   // Add two experiments, check they're there.
    169   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    170   SetExperimentEnabled(&flags_storage_, kFlags2, true);
    171 
    172   const ListValue* experiments_list = prefs_.GetList(
    173       prefs::kEnabledLabsExperiments);
    174   ASSERT_TRUE(experiments_list != NULL);
    175 
    176   ASSERT_EQ(2u, experiments_list->GetSize());
    177 
    178   std::string s0;
    179   ASSERT_TRUE(experiments_list->GetString(0, &s0));
    180   std::string s1;
    181   ASSERT_TRUE(experiments_list->GetString(1, &s1));
    182 
    183   EXPECT_TRUE(s0 == kFlags1 || s1 == kFlags1);
    184   EXPECT_TRUE(s0 == kFlags2 || s1 == kFlags2);
    185 
    186   // Remove one experiment, check the other's still around.
    187   SetExperimentEnabled(&flags_storage_, kFlags2, false);
    188 
    189   experiments_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
    190   ASSERT_TRUE(experiments_list != NULL);
    191   ASSERT_EQ(1u, experiments_list->GetSize());
    192   ASSERT_TRUE(experiments_list->GetString(0, &s0));
    193   EXPECT_TRUE(s0 == kFlags1);
    194 }
    195 
    196 TEST_F(AboutFlagsTest, AddTwoFlagsRemoveBoth) {
    197   // Add two experiments, check the pref exists.
    198   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    199   SetExperimentEnabled(&flags_storage_, kFlags2, true);
    200   const ListValue* experiments_list = prefs_.GetList(
    201       prefs::kEnabledLabsExperiments);
    202   ASSERT_TRUE(experiments_list != NULL);
    203 
    204   // Remove both, the pref should have been removed completely.
    205   SetExperimentEnabled(&flags_storage_, kFlags1, false);
    206   SetExperimentEnabled(&flags_storage_, kFlags2, false);
    207   experiments_list = prefs_.GetList(prefs::kEnabledLabsExperiments);
    208   EXPECT_TRUE(experiments_list == NULL || experiments_list->GetSize() == 0);
    209 }
    210 
    211 TEST_F(AboutFlagsTest, ConvertFlagsToSwitches) {
    212   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    213 
    214   CommandLine command_line(CommandLine::NO_PROGRAM);
    215   command_line.AppendSwitch("foo");
    216 
    217   EXPECT_TRUE(command_line.HasSwitch("foo"));
    218   EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
    219 
    220   ConvertFlagsToSwitches(&flags_storage_, &command_line);
    221 
    222   EXPECT_TRUE(command_line.HasSwitch("foo"));
    223   EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
    224 }
    225 
    226 TEST_F(AboutFlagsTest, CompareSwitchesToCurrentCommandLine) {
    227   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    228 
    229   CommandLine command_line(CommandLine::NO_PROGRAM);
    230   command_line.AppendSwitch("foo");
    231 
    232   CommandLine new_command_line(CommandLine::NO_PROGRAM);
    233   ConvertFlagsToSwitches(&flags_storage_, &new_command_line);
    234 
    235   EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(new_command_line,
    236                                                         command_line));
    237 
    238   ConvertFlagsToSwitches(&flags_storage_, &command_line);
    239 
    240   EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(new_command_line,
    241                                                        command_line));
    242 
    243   // Now both have flags but different.
    244   SetExperimentEnabled(&flags_storage_, kFlags1, false);
    245   SetExperimentEnabled(&flags_storage_, kFlags2, true);
    246 
    247   CommandLine another_command_line(CommandLine::NO_PROGRAM);
    248   ConvertFlagsToSwitches(&flags_storage_, &another_command_line);
    249 
    250   EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(new_command_line,
    251                                                         another_command_line));
    252 }
    253 
    254 TEST_F(AboutFlagsTest, RemoveFlagSwitches) {
    255   std::map<std::string, CommandLine::StringType> switch_list;
    256   switch_list[kSwitch1] = CommandLine::StringType();
    257   switch_list[switches::kFlagSwitchesBegin] = CommandLine::StringType();
    258   switch_list[switches::kFlagSwitchesEnd] = CommandLine::StringType();
    259   switch_list["foo"] = CommandLine::StringType();
    260 
    261   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    262 
    263   // This shouldn't do anything before ConvertFlagsToSwitches() wasn't called.
    264   RemoveFlagsSwitches(&switch_list);
    265   ASSERT_EQ(4u, switch_list.size());
    266   EXPECT_TRUE(switch_list.find(kSwitch1) != switch_list.end());
    267   EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesBegin) !=
    268               switch_list.end());
    269   EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesEnd) !=
    270               switch_list.end());
    271   EXPECT_TRUE(switch_list.find("foo") != switch_list.end());
    272 
    273   // Call ConvertFlagsToSwitches(), then RemoveFlagsSwitches() again.
    274   CommandLine command_line(CommandLine::NO_PROGRAM);
    275   command_line.AppendSwitch("foo");
    276   ConvertFlagsToSwitches(&flags_storage_, &command_line);
    277   RemoveFlagsSwitches(&switch_list);
    278 
    279   // Now the about:flags-related switch should have been removed.
    280   ASSERT_EQ(1u, switch_list.size());
    281   EXPECT_TRUE(switch_list.find("foo") != switch_list.end());
    282 }
    283 
    284 // Tests enabling experiments that aren't supported on the current platform.
    285 TEST_F(AboutFlagsTest, PersistAndPrune) {
    286   // Enable experiments 1 and 3.
    287   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    288   SetExperimentEnabled(&flags_storage_, kFlags3, true);
    289   CommandLine command_line(CommandLine::NO_PROGRAM);
    290   EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
    291   EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
    292 
    293   // Convert the flags to switches. Experiment 3 shouldn't be among the switches
    294   // as it is not applicable to the current platform.
    295   ConvertFlagsToSwitches(&flags_storage_, &command_line);
    296   EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
    297   EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
    298 
    299   // Experiment 3 should show still be persisted in preferences though.
    300   const ListValue* experiments_list =
    301       prefs_.GetList(prefs::kEnabledLabsExperiments);
    302   ASSERT_TRUE(experiments_list);
    303   EXPECT_EQ(2U, experiments_list->GetSize());
    304   std::string s0;
    305   ASSERT_TRUE(experiments_list->GetString(0, &s0));
    306   EXPECT_EQ(kFlags1, s0);
    307   std::string s1;
    308   ASSERT_TRUE(experiments_list->GetString(1, &s1));
    309   EXPECT_EQ(kFlags3, s1);
    310 }
    311 
    312 // Tests that switches which should have values get them in the command
    313 // line.
    314 TEST_F(AboutFlagsTest, CheckValues) {
    315   // Enable experiments 1 and 2.
    316   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    317   SetExperimentEnabled(&flags_storage_, kFlags2, true);
    318   CommandLine command_line(CommandLine::NO_PROGRAM);
    319   EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
    320   EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
    321 
    322   // Convert the flags to switches.
    323   ConvertFlagsToSwitches(&flags_storage_, &command_line);
    324   EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
    325   EXPECT_EQ(std::string(), command_line.GetSwitchValueASCII(kSwitch1));
    326   EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
    327   EXPECT_EQ(std::string(kValueForSwitch2),
    328             command_line.GetSwitchValueASCII(kSwitch2));
    329 
    330   // Confirm that there is no '=' in the command line for simple switches.
    331   std::string switch1_with_equals = std::string("--") +
    332                                     std::string(kSwitch1) +
    333                                     std::string("=");
    334 #if defined(OS_WIN)
    335   EXPECT_EQ(std::wstring::npos,
    336             command_line.GetCommandLineString().find(
    337                 ASCIIToWide(switch1_with_equals)));
    338 #else
    339   EXPECT_EQ(std::string::npos,
    340             command_line.GetCommandLineString().find(switch1_with_equals));
    341 #endif
    342 
    343   // And confirm there is a '=' for switches with values.
    344   std::string switch2_with_equals = std::string("--") +
    345                                     std::string(kSwitch2) +
    346                                     std::string("=");
    347 #if defined(OS_WIN)
    348   EXPECT_NE(std::wstring::npos,
    349             command_line.GetCommandLineString().find(
    350                 ASCIIToWide(switch2_with_equals)));
    351 #else
    352   EXPECT_NE(std::string::npos,
    353             command_line.GetCommandLineString().find(switch2_with_equals));
    354 #endif
    355 
    356   // And it should persist.
    357   const ListValue* experiments_list =
    358       prefs_.GetList(prefs::kEnabledLabsExperiments);
    359   ASSERT_TRUE(experiments_list);
    360   EXPECT_EQ(2U, experiments_list->GetSize());
    361   std::string s0;
    362   ASSERT_TRUE(experiments_list->GetString(0, &s0));
    363   EXPECT_EQ(kFlags1, s0);
    364   std::string s1;
    365   ASSERT_TRUE(experiments_list->GetString(1, &s1));
    366   EXPECT_EQ(kFlags2, s1);
    367 }
    368 
    369 // Tests multi-value type experiments.
    370 TEST_F(AboutFlagsTest, MultiValues) {
    371   const Experiment& experiment = kExperiments[3];
    372   ASSERT_EQ(kFlags4, experiment.internal_name);
    373 
    374   // Initially, the first "deactivated" option of the multi experiment should
    375   // be set.
    376   {
    377     CommandLine command_line(CommandLine::NO_PROGRAM);
    378     ConvertFlagsToSwitches(&flags_storage_, &command_line);
    379     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
    380     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
    381   }
    382 
    383   // Enable the 2nd choice of the multi-value.
    384   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
    385   {
    386     CommandLine command_line(CommandLine::NO_PROGRAM);
    387     ConvertFlagsToSwitches(&flags_storage_, &command_line);
    388     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
    389     EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch2));
    390     EXPECT_EQ(std::string(kValueForMultiSwitch2),
    391               command_line.GetSwitchValueASCII(kMultiSwitch2));
    392   }
    393 
    394   // Disable the multi-value experiment.
    395   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
    396   {
    397     CommandLine command_line(CommandLine::NO_PROGRAM);
    398     ConvertFlagsToSwitches(&flags_storage_, &command_line);
    399     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
    400     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
    401   }
    402 }
    403 
    404 TEST_F(AboutFlagsTest, EnableDisableValues) {
    405   const Experiment& experiment = kExperiments[4];
    406   ASSERT_EQ(kFlags5, experiment.internal_name);
    407 
    408   // Nothing selected.
    409   {
    410     CommandLine command_line(CommandLine::NO_PROGRAM);
    411     ConvertFlagsToSwitches(&flags_storage_, &command_line);
    412     EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
    413     EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
    414   }
    415 
    416   // "Enable" option selected.
    417   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(1), true);
    418   {
    419     CommandLine command_line(CommandLine::NO_PROGRAM);
    420     ConvertFlagsToSwitches(&flags_storage_, &command_line);
    421     EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
    422     EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
    423     EXPECT_EQ(kEnableDisableValue1, command_line.GetSwitchValueASCII(kSwitch1));
    424   }
    425 
    426   // "Disable" option selected.
    427   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
    428   {
    429     CommandLine command_line(CommandLine::NO_PROGRAM);
    430     ConvertFlagsToSwitches(&flags_storage_, &command_line);
    431     EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
    432     EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
    433     EXPECT_EQ(kEnableDisableValue2, command_line.GetSwitchValueASCII(kSwitch2));
    434   }
    435 
    436   // "Default" option selected, same as nothing selected.
    437   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
    438   {
    439     CommandLine command_line(CommandLine::NO_PROGRAM);
    440     ConvertFlagsToSwitches(&flags_storage_, &command_line);
    441     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
    442     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
    443   }
    444 }
    445 
    446 // Makes sure there are no separators in any of the experiment names.
    447 TEST_F(AboutFlagsTest, NoSeparators) {
    448   testing::SetExperiments(NULL, 0);
    449   size_t count;
    450   const Experiment* experiments = testing::GetExperiments(&count);
    451     for (size_t i = 0; i < count; ++i) {
    452     std::string name = experiments->internal_name;
    453     EXPECT_EQ(std::string::npos, name.find(testing::kMultiSeparator)) << i;
    454   }
    455 }
    456 
    457 }  // namespace about_flags
    458