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