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