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 base::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 base::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, kAddSentinels);
    221 
    222   EXPECT_TRUE(command_line.HasSwitch("foo"));
    223   EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
    224   EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesBegin));
    225   EXPECT_TRUE(command_line.HasSwitch(switches::kFlagSwitchesEnd));
    226 
    227   CommandLine command_line2(CommandLine::NO_PROGRAM);
    228 
    229   ConvertFlagsToSwitches(&flags_storage_, &command_line2, kNoSentinels);
    230 
    231   EXPECT_TRUE(command_line2.HasSwitch(kSwitch1));
    232   EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesBegin));
    233   EXPECT_FALSE(command_line2.HasSwitch(switches::kFlagSwitchesEnd));
    234 }
    235 
    236 TEST_F(AboutFlagsTest, CompareSwitchesToCurrentCommandLine) {
    237   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    238 
    239   CommandLine command_line(CommandLine::NO_PROGRAM);
    240   command_line.AppendSwitch("foo");
    241 
    242   CommandLine new_command_line(CommandLine::NO_PROGRAM);
    243   ConvertFlagsToSwitches(&flags_storage_, &new_command_line, kAddSentinels);
    244 
    245   EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(new_command_line,
    246                                                         command_line));
    247 
    248   ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    249 
    250   EXPECT_TRUE(AreSwitchesIdenticalToCurrentCommandLine(new_command_line,
    251                                                        command_line));
    252 
    253   // Now both have flags but different.
    254   SetExperimentEnabled(&flags_storage_, kFlags1, false);
    255   SetExperimentEnabled(&flags_storage_, kFlags2, true);
    256 
    257   CommandLine another_command_line(CommandLine::NO_PROGRAM);
    258   ConvertFlagsToSwitches(&flags_storage_, &another_command_line, kAddSentinels);
    259 
    260   EXPECT_FALSE(AreSwitchesIdenticalToCurrentCommandLine(new_command_line,
    261                                                         another_command_line));
    262 }
    263 
    264 TEST_F(AboutFlagsTest, RemoveFlagSwitches) {
    265   std::map<std::string, CommandLine::StringType> switch_list;
    266   switch_list[kSwitch1] = CommandLine::StringType();
    267   switch_list[switches::kFlagSwitchesBegin] = CommandLine::StringType();
    268   switch_list[switches::kFlagSwitchesEnd] = CommandLine::StringType();
    269   switch_list["foo"] = CommandLine::StringType();
    270 
    271   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    272 
    273   // This shouldn't do anything before ConvertFlagsToSwitches() wasn't called.
    274   RemoveFlagsSwitches(&switch_list);
    275   ASSERT_EQ(4u, switch_list.size());
    276   EXPECT_TRUE(switch_list.find(kSwitch1) != switch_list.end());
    277   EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesBegin) !=
    278               switch_list.end());
    279   EXPECT_TRUE(switch_list.find(switches::kFlagSwitchesEnd) !=
    280               switch_list.end());
    281   EXPECT_TRUE(switch_list.find("foo") != switch_list.end());
    282 
    283   // Call ConvertFlagsToSwitches(), then RemoveFlagsSwitches() again.
    284   CommandLine command_line(CommandLine::NO_PROGRAM);
    285   command_line.AppendSwitch("foo");
    286   ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    287   RemoveFlagsSwitches(&switch_list);
    288 
    289   // Now the about:flags-related switch should have been removed.
    290   ASSERT_EQ(1u, switch_list.size());
    291   EXPECT_TRUE(switch_list.find("foo") != switch_list.end());
    292 }
    293 
    294 // Tests enabling experiments that aren't supported on the current platform.
    295 TEST_F(AboutFlagsTest, PersistAndPrune) {
    296   // Enable experiments 1 and 3.
    297   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    298   SetExperimentEnabled(&flags_storage_, kFlags3, true);
    299   CommandLine command_line(CommandLine::NO_PROGRAM);
    300   EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
    301   EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
    302 
    303   // Convert the flags to switches. Experiment 3 shouldn't be among the switches
    304   // as it is not applicable to the current platform.
    305   ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    306   EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
    307   EXPECT_FALSE(command_line.HasSwitch(kSwitch3));
    308 
    309   // Experiment 3 should show still be persisted in preferences though.
    310   const base::ListValue* experiments_list =
    311       prefs_.GetList(prefs::kEnabledLabsExperiments);
    312   ASSERT_TRUE(experiments_list);
    313   EXPECT_EQ(2U, experiments_list->GetSize());
    314   std::string s0;
    315   ASSERT_TRUE(experiments_list->GetString(0, &s0));
    316   EXPECT_EQ(kFlags1, s0);
    317   std::string s1;
    318   ASSERT_TRUE(experiments_list->GetString(1, &s1));
    319   EXPECT_EQ(kFlags3, s1);
    320 }
    321 
    322 // Tests that switches which should have values get them in the command
    323 // line.
    324 TEST_F(AboutFlagsTest, CheckValues) {
    325   // Enable experiments 1 and 2.
    326   SetExperimentEnabled(&flags_storage_, kFlags1, true);
    327   SetExperimentEnabled(&flags_storage_, kFlags2, true);
    328   CommandLine command_line(CommandLine::NO_PROGRAM);
    329   EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
    330   EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
    331 
    332   // Convert the flags to switches.
    333   ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    334   EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
    335   EXPECT_EQ(std::string(), command_line.GetSwitchValueASCII(kSwitch1));
    336   EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
    337   EXPECT_EQ(std::string(kValueForSwitch2),
    338             command_line.GetSwitchValueASCII(kSwitch2));
    339 
    340   // Confirm that there is no '=' in the command line for simple switches.
    341   std::string switch1_with_equals = std::string("--") +
    342                                     std::string(kSwitch1) +
    343                                     std::string("=");
    344 #if defined(OS_WIN)
    345   EXPECT_EQ(std::wstring::npos,
    346             command_line.GetCommandLineString().find(
    347                 base::ASCIIToWide(switch1_with_equals)));
    348 #else
    349   EXPECT_EQ(std::string::npos,
    350             command_line.GetCommandLineString().find(switch1_with_equals));
    351 #endif
    352 
    353   // And confirm there is a '=' for switches with values.
    354   std::string switch2_with_equals = std::string("--") +
    355                                     std::string(kSwitch2) +
    356                                     std::string("=");
    357 #if defined(OS_WIN)
    358   EXPECT_NE(std::wstring::npos,
    359             command_line.GetCommandLineString().find(
    360                 base::ASCIIToWide(switch2_with_equals)));
    361 #else
    362   EXPECT_NE(std::string::npos,
    363             command_line.GetCommandLineString().find(switch2_with_equals));
    364 #endif
    365 
    366   // And it should persist.
    367   const base::ListValue* experiments_list =
    368       prefs_.GetList(prefs::kEnabledLabsExperiments);
    369   ASSERT_TRUE(experiments_list);
    370   EXPECT_EQ(2U, experiments_list->GetSize());
    371   std::string s0;
    372   ASSERT_TRUE(experiments_list->GetString(0, &s0));
    373   EXPECT_EQ(kFlags1, s0);
    374   std::string s1;
    375   ASSERT_TRUE(experiments_list->GetString(1, &s1));
    376   EXPECT_EQ(kFlags2, s1);
    377 }
    378 
    379 // Tests multi-value type experiments.
    380 TEST_F(AboutFlagsTest, MultiValues) {
    381   const Experiment& experiment = kExperiments[3];
    382   ASSERT_EQ(kFlags4, experiment.internal_name);
    383 
    384   // Initially, the first "deactivated" option of the multi experiment should
    385   // be set.
    386   {
    387     CommandLine command_line(CommandLine::NO_PROGRAM);
    388     ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    389     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
    390     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
    391   }
    392 
    393   // Enable the 2nd choice of the multi-value.
    394   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
    395   {
    396     CommandLine command_line(CommandLine::NO_PROGRAM);
    397     ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    398     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
    399     EXPECT_TRUE(command_line.HasSwitch(kMultiSwitch2));
    400     EXPECT_EQ(std::string(kValueForMultiSwitch2),
    401               command_line.GetSwitchValueASCII(kMultiSwitch2));
    402   }
    403 
    404   // Disable the multi-value experiment.
    405   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
    406   {
    407     CommandLine command_line(CommandLine::NO_PROGRAM);
    408     ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    409     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
    410     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
    411   }
    412 }
    413 
    414 TEST_F(AboutFlagsTest, EnableDisableValues) {
    415   const Experiment& experiment = kExperiments[4];
    416   ASSERT_EQ(kFlags5, experiment.internal_name);
    417 
    418   // Nothing selected.
    419   {
    420     CommandLine command_line(CommandLine::NO_PROGRAM);
    421     ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    422     EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
    423     EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
    424   }
    425 
    426   // "Enable" option selected.
    427   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(1), true);
    428   {
    429     CommandLine command_line(CommandLine::NO_PROGRAM);
    430     ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    431     EXPECT_TRUE(command_line.HasSwitch(kSwitch1));
    432     EXPECT_FALSE(command_line.HasSwitch(kSwitch2));
    433     EXPECT_EQ(kEnableDisableValue1, command_line.GetSwitchValueASCII(kSwitch1));
    434   }
    435 
    436   // "Disable" option selected.
    437   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(2), true);
    438   {
    439     CommandLine command_line(CommandLine::NO_PROGRAM);
    440     ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    441     EXPECT_FALSE(command_line.HasSwitch(kSwitch1));
    442     EXPECT_TRUE(command_line.HasSwitch(kSwitch2));
    443     EXPECT_EQ(kEnableDisableValue2, command_line.GetSwitchValueASCII(kSwitch2));
    444   }
    445 
    446   // "Default" option selected, same as nothing selected.
    447   SetExperimentEnabled(&flags_storage_, experiment.NameForChoice(0), true);
    448   {
    449     CommandLine command_line(CommandLine::NO_PROGRAM);
    450     ConvertFlagsToSwitches(&flags_storage_, &command_line, kAddSentinels);
    451     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch1));
    452     EXPECT_FALSE(command_line.HasSwitch(kMultiSwitch2));
    453   }
    454 }
    455 
    456 // Makes sure there are no separators in any of the experiment names.
    457 TEST_F(AboutFlagsTest, NoSeparators) {
    458   testing::SetExperiments(NULL, 0);
    459   size_t count;
    460   const Experiment* experiments = testing::GetExperiments(&count);
    461     for (size_t i = 0; i < count; ++i) {
    462     std::string name = experiments->internal_name;
    463     EXPECT_EQ(std::string::npos, name.find(testing::kMultiSeparator)) << i;
    464   }
    465 }
    466 
    467 }  // namespace about_flags
    468