1 // Copyright 2015 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/feature_list.h" 6 7 #include <stddef.h> 8 9 #include <algorithm> 10 #include <utility> 11 12 #include "base/format_macros.h" 13 #include "base/macros.h" 14 #include "base/memory/ptr_util.h" 15 #include "base/metrics/field_trial.h" 16 #include "base/strings/string_util.h" 17 #include "base/strings/stringprintf.h" 18 #include "testing/gtest/include/gtest/gtest.h" 19 20 namespace base { 21 22 namespace { 23 24 const char kFeatureOnByDefaultName[] = "OnByDefault"; 25 struct Feature kFeatureOnByDefault { 26 kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT 27 }; 28 29 const char kFeatureOffByDefaultName[] = "OffByDefault"; 30 struct Feature kFeatureOffByDefault { 31 kFeatureOffByDefaultName, FEATURE_DISABLED_BY_DEFAULT 32 }; 33 34 std::string SortFeatureListString(const std::string& feature_list) { 35 std::vector<std::string> features = 36 FeatureList::SplitFeatureListString(feature_list); 37 std::sort(features.begin(), features.end()); 38 return JoinString(features, ","); 39 } 40 41 } // namespace 42 43 class FeatureListTest : public testing::Test { 44 public: 45 FeatureListTest() : feature_list_(nullptr) { 46 RegisterFeatureListInstance(WrapUnique(new FeatureList)); 47 } 48 ~FeatureListTest() override { ClearFeatureListInstance(); } 49 50 void RegisterFeatureListInstance(std::unique_ptr<FeatureList> feature_list) { 51 FeatureList::ClearInstanceForTesting(); 52 feature_list_ = feature_list.get(); 53 FeatureList::SetInstance(std::move(feature_list)); 54 } 55 void ClearFeatureListInstance() { 56 FeatureList::ClearInstanceForTesting(); 57 feature_list_ = nullptr; 58 } 59 60 FeatureList* feature_list() { return feature_list_; } 61 62 private: 63 // Weak. Owned by the FeatureList::SetInstance(). 64 FeatureList* feature_list_; 65 66 DISALLOW_COPY_AND_ASSIGN(FeatureListTest); 67 }; 68 69 TEST_F(FeatureListTest, DefaultStates) { 70 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); 71 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); 72 } 73 74 TEST_F(FeatureListTest, InitializeFromCommandLine) { 75 struct { 76 const char* enable_features; 77 const char* disable_features; 78 bool expected_feature_on_state; 79 bool expected_feature_off_state; 80 } test_cases[] = { 81 {"", "", true, false}, 82 {"OffByDefault", "", true, true}, 83 {"OffByDefault", "OnByDefault", false, true}, 84 {"OnByDefault,OffByDefault", "", true, true}, 85 {"", "OnByDefault,OffByDefault", false, false}, 86 // In the case an entry is both, disable takes precedence. 87 {"OnByDefault", "OnByDefault,OffByDefault", false, false}, 88 }; 89 90 for (size_t i = 0; i < arraysize(test_cases); ++i) { 91 const auto& test_case = test_cases[i]; 92 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i, 93 test_case.enable_features, 94 test_case.disable_features)); 95 96 ClearFeatureListInstance(); 97 std::unique_ptr<FeatureList> feature_list(new FeatureList); 98 feature_list->InitializeFromCommandLine(test_case.enable_features, 99 test_case.disable_features); 100 RegisterFeatureListInstance(std::move(feature_list)); 101 102 EXPECT_EQ(test_case.expected_feature_on_state, 103 FeatureList::IsEnabled(kFeatureOnByDefault)) 104 << i; 105 EXPECT_EQ(test_case.expected_feature_off_state, 106 FeatureList::IsEnabled(kFeatureOffByDefault)) 107 << i; 108 } 109 } 110 111 TEST_F(FeatureListTest, CheckFeatureIdentity) { 112 // Tests that CheckFeatureIdentity() correctly detects when two different 113 // structs with the same feature name are passed to it. 114 115 // Call it twice for each feature at the top of the file, since the first call 116 // makes it remember the entry and the second call will verify it. 117 EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault)); 118 EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault)); 119 EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault)); 120 EXPECT_TRUE(feature_list()->CheckFeatureIdentity(kFeatureOffByDefault)); 121 122 // Now, call it with a distinct struct for |kFeatureOnByDefaultName|, which 123 // should return false. 124 struct Feature kFeatureOnByDefault2 { 125 kFeatureOnByDefaultName, FEATURE_ENABLED_BY_DEFAULT 126 }; 127 EXPECT_FALSE(feature_list()->CheckFeatureIdentity(kFeatureOnByDefault2)); 128 } 129 130 TEST_F(FeatureListTest, FieldTrialOverrides) { 131 struct { 132 FeatureList::OverrideState trial1_state; 133 FeatureList::OverrideState trial2_state; 134 } test_cases[] = { 135 {FeatureList::OVERRIDE_DISABLE_FEATURE, 136 FeatureList::OVERRIDE_DISABLE_FEATURE}, 137 {FeatureList::OVERRIDE_DISABLE_FEATURE, 138 FeatureList::OVERRIDE_ENABLE_FEATURE}, 139 {FeatureList::OVERRIDE_ENABLE_FEATURE, 140 FeatureList::OVERRIDE_DISABLE_FEATURE}, 141 {FeatureList::OVERRIDE_ENABLE_FEATURE, 142 FeatureList::OVERRIDE_ENABLE_FEATURE}, 143 }; 144 145 FieldTrial::ActiveGroup active_group; 146 for (size_t i = 0; i < arraysize(test_cases); ++i) { 147 const auto& test_case = test_cases[i]; 148 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]", i)); 149 150 ClearFeatureListInstance(); 151 152 FieldTrialList field_trial_list(nullptr); 153 std::unique_ptr<FeatureList> feature_list(new FeatureList); 154 155 FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A"); 156 FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B"); 157 feature_list->RegisterFieldTrialOverride(kFeatureOnByDefaultName, 158 test_case.trial1_state, trial1); 159 feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName, 160 test_case.trial2_state, trial2); 161 RegisterFeatureListInstance(std::move(feature_list)); 162 163 // Initially, neither trial should be active. 164 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name())); 165 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name())); 166 167 const bool expected_enabled_1 = 168 (test_case.trial1_state == FeatureList::OVERRIDE_ENABLE_FEATURE); 169 EXPECT_EQ(expected_enabled_1, FeatureList::IsEnabled(kFeatureOnByDefault)); 170 // The above should have activated |trial1|. 171 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name())); 172 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name())); 173 174 const bool expected_enabled_2 = 175 (test_case.trial2_state == FeatureList::OVERRIDE_ENABLE_FEATURE); 176 EXPECT_EQ(expected_enabled_2, FeatureList::IsEnabled(kFeatureOffByDefault)); 177 // The above should have activated |trial2|. 178 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name())); 179 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial2->trial_name())); 180 } 181 } 182 183 TEST_F(FeatureListTest, FieldTrialAssociateUseDefault) { 184 FieldTrialList field_trial_list(nullptr); 185 std::unique_ptr<FeatureList> feature_list(new FeatureList); 186 187 FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("TrialExample1", "A"); 188 FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("TrialExample2", "B"); 189 feature_list->RegisterFieldTrialOverride( 190 kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial1); 191 feature_list->RegisterFieldTrialOverride( 192 kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial2); 193 RegisterFeatureListInstance(std::move(feature_list)); 194 195 // Initially, neither trial should be active. 196 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial1->trial_name())); 197 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name())); 198 199 // Check the feature enabled state is its default. 200 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); 201 // The above should have activated |trial1|. 202 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name())); 203 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial2->trial_name())); 204 205 // Check the feature enabled state is its default. 206 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); 207 // The above should have activated |trial2|. 208 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial1->trial_name())); 209 EXPECT_TRUE(FieldTrialList::IsTrialActive(trial2->trial_name())); 210 } 211 212 TEST_F(FeatureListTest, CommandLineTakesPrecedenceOverFieldTrial) { 213 ClearFeatureListInstance(); 214 215 FieldTrialList field_trial_list(nullptr); 216 std::unique_ptr<FeatureList> feature_list(new FeatureList); 217 218 // The feature is explicitly enabled on the command-line. 219 feature_list->InitializeFromCommandLine(kFeatureOffByDefaultName, ""); 220 221 // But the FieldTrial would set the feature to disabled. 222 FieldTrial* trial = FieldTrialList::CreateFieldTrial("TrialExample2", "A"); 223 feature_list->RegisterFieldTrialOverride( 224 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, trial); 225 RegisterFeatureListInstance(std::move(feature_list)); 226 227 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name())); 228 // Command-line should take precedence. 229 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault)); 230 // Since the feature is on due to the command-line, and not as a result of the 231 // field trial, the field trial should not be activated (since the Associate* 232 // API wasn't used.) 233 EXPECT_FALSE(FieldTrialList::IsTrialActive(trial->trial_name())); 234 } 235 236 TEST_F(FeatureListTest, IsFeatureOverriddenFromCommandLine) { 237 ClearFeatureListInstance(); 238 239 FieldTrialList field_trial_list(nullptr); 240 std::unique_ptr<FeatureList> feature_list(new FeatureList); 241 242 // No features are overridden from the command line yet 243 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( 244 kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); 245 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( 246 kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); 247 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( 248 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); 249 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( 250 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); 251 252 // Now, enable |kFeatureOffByDefaultName| via the command-line. 253 feature_list->InitializeFromCommandLine(kFeatureOffByDefaultName, ""); 254 255 // It should now be overridden for the enabled group. 256 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( 257 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); 258 EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine( 259 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); 260 261 // Register a field trial to associate with the feature and ensure that the 262 // results are still the same. 263 feature_list->AssociateReportingFieldTrial( 264 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE, 265 FieldTrialList::CreateFieldTrial("Trial1", "A")); 266 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( 267 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); 268 EXPECT_TRUE(feature_list->IsFeatureOverriddenFromCommandLine( 269 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); 270 271 // Now, register a field trial to override |kFeatureOnByDefaultName| state 272 // and check that the function still returns false for that feature. 273 feature_list->RegisterFieldTrialOverride( 274 kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, 275 FieldTrialList::CreateFieldTrial("Trial2", "A")); 276 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( 277 kFeatureOnByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)); 278 EXPECT_FALSE(feature_list->IsFeatureOverriddenFromCommandLine( 279 kFeatureOnByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)); 280 RegisterFeatureListInstance(std::move(feature_list)); 281 282 // Check the expected feature states for good measure. 283 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault)); 284 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault)); 285 } 286 287 TEST_F(FeatureListTest, AssociateReportingFieldTrial) { 288 struct { 289 const char* enable_features; 290 const char* disable_features; 291 bool expected_enable_trial_created; 292 bool expected_disable_trial_created; 293 } test_cases[] = { 294 // If no enable/disable flags are specified, no trials should be created. 295 {"", "", false, false}, 296 // Enabling the feature should result in the enable trial created. 297 {kFeatureOffByDefaultName, "", true, false}, 298 // Disabling the feature should result in the disable trial created. 299 {"", kFeatureOffByDefaultName, false, true}, 300 }; 301 302 const char kTrialName[] = "ForcingTrial"; 303 const char kForcedOnGroupName[] = "ForcedOn"; 304 const char kForcedOffGroupName[] = "ForcedOff"; 305 306 for (size_t i = 0; i < arraysize(test_cases); ++i) { 307 const auto& test_case = test_cases[i]; 308 SCOPED_TRACE(base::StringPrintf("Test[%" PRIuS "]: [%s] [%s]", i, 309 test_case.enable_features, 310 test_case.disable_features)); 311 312 ClearFeatureListInstance(); 313 314 FieldTrialList field_trial_list(nullptr); 315 std::unique_ptr<FeatureList> feature_list(new FeatureList); 316 feature_list->InitializeFromCommandLine(test_case.enable_features, 317 test_case.disable_features); 318 319 FieldTrial* enable_trial = nullptr; 320 if (feature_list->IsFeatureOverriddenFromCommandLine( 321 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE)) { 322 enable_trial = base::FieldTrialList::CreateFieldTrial(kTrialName, 323 kForcedOnGroupName); 324 feature_list->AssociateReportingFieldTrial( 325 kFeatureOffByDefaultName, FeatureList::OVERRIDE_ENABLE_FEATURE, 326 enable_trial); 327 } 328 FieldTrial* disable_trial = nullptr; 329 if (feature_list->IsFeatureOverriddenFromCommandLine( 330 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE)) { 331 disable_trial = base::FieldTrialList::CreateFieldTrial( 332 kTrialName, kForcedOffGroupName); 333 feature_list->AssociateReportingFieldTrial( 334 kFeatureOffByDefaultName, FeatureList::OVERRIDE_DISABLE_FEATURE, 335 disable_trial); 336 } 337 EXPECT_EQ(test_case.expected_enable_trial_created, enable_trial != nullptr); 338 EXPECT_EQ(test_case.expected_disable_trial_created, 339 disable_trial != nullptr); 340 RegisterFeatureListInstance(std::move(feature_list)); 341 342 EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); 343 if (disable_trial) { 344 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); 345 EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName)); 346 EXPECT_EQ(kForcedOffGroupName, disable_trial->group_name()); 347 } else if (enable_trial) { 348 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault)); 349 EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName)); 350 EXPECT_EQ(kForcedOnGroupName, enable_trial->group_name()); 351 } 352 } 353 } 354 355 TEST_F(FeatureListTest, GetFeatureOverrides) { 356 ClearFeatureListInstance(); 357 FieldTrialList field_trial_list(nullptr); 358 std::unique_ptr<FeatureList> feature_list(new FeatureList); 359 feature_list->InitializeFromCommandLine("A,X", "D"); 360 361 FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group"); 362 feature_list->RegisterFieldTrialOverride(kFeatureOffByDefaultName, 363 FeatureList::OVERRIDE_ENABLE_FEATURE, 364 trial); 365 366 RegisterFeatureListInstance(std::move(feature_list)); 367 368 std::string enable_features; 369 std::string disable_features; 370 FeatureList::GetInstance()->GetFeatureOverrides(&enable_features, 371 &disable_features); 372 EXPECT_EQ("A,OffByDefault<Trial,X", SortFeatureListString(enable_features)); 373 EXPECT_EQ("D", SortFeatureListString(disable_features)); 374 } 375 376 TEST_F(FeatureListTest, GetFeatureOverrides_UseDefault) { 377 ClearFeatureListInstance(); 378 FieldTrialList field_trial_list(nullptr); 379 std::unique_ptr<FeatureList> feature_list(new FeatureList); 380 feature_list->InitializeFromCommandLine("A,X", "D"); 381 382 FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group"); 383 feature_list->RegisterFieldTrialOverride( 384 kFeatureOffByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial); 385 386 RegisterFeatureListInstance(std::move(feature_list)); 387 388 std::string enable_features; 389 std::string disable_features; 390 FeatureList::GetInstance()->GetFeatureOverrides(&enable_features, 391 &disable_features); 392 EXPECT_EQ("*OffByDefault<Trial,A,X", SortFeatureListString(enable_features)); 393 EXPECT_EQ("D", SortFeatureListString(disable_features)); 394 } 395 396 TEST_F(FeatureListTest, GetFieldTrial) { 397 ClearFeatureListInstance(); 398 FieldTrialList field_trial_list(nullptr); 399 FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial", "Group"); 400 std::unique_ptr<FeatureList> feature_list(new FeatureList); 401 feature_list->RegisterFieldTrialOverride( 402 kFeatureOnByDefaultName, FeatureList::OVERRIDE_USE_DEFAULT, trial); 403 RegisterFeatureListInstance(std::move(feature_list)); 404 405 EXPECT_EQ(trial, FeatureList::GetFieldTrial(kFeatureOnByDefault)); 406 EXPECT_EQ(nullptr, FeatureList::GetFieldTrial(kFeatureOffByDefault)); 407 } 408 409 TEST_F(FeatureListTest, InitializeFromCommandLine_WithFieldTrials) { 410 ClearFeatureListInstance(); 411 FieldTrialList field_trial_list(nullptr); 412 FieldTrialList::CreateFieldTrial("Trial", "Group"); 413 std::unique_ptr<FeatureList> feature_list(new FeatureList); 414 feature_list->InitializeFromCommandLine("A,OffByDefault<Trial,X", "D"); 415 RegisterFeatureListInstance(std::move(feature_list)); 416 417 EXPECT_FALSE(FieldTrialList::IsTrialActive("Trial")); 418 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOffByDefault)); 419 EXPECT_TRUE(FieldTrialList::IsTrialActive("Trial")); 420 } 421 422 TEST_F(FeatureListTest, InitializeFromCommandLine_UseDefault) { 423 ClearFeatureListInstance(); 424 FieldTrialList field_trial_list(nullptr); 425 FieldTrialList::CreateFieldTrial("T1", "Group"); 426 FieldTrialList::CreateFieldTrial("T2", "Group"); 427 std::unique_ptr<FeatureList> feature_list(new FeatureList); 428 feature_list->InitializeFromCommandLine( 429 "A,*OffByDefault<T1,*OnByDefault<T2,X", "D"); 430 RegisterFeatureListInstance(std::move(feature_list)); 431 432 EXPECT_FALSE(FieldTrialList::IsTrialActive("T1")); 433 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); 434 EXPECT_TRUE(FieldTrialList::IsTrialActive("T1")); 435 436 EXPECT_FALSE(FieldTrialList::IsTrialActive("T2")); 437 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); 438 EXPECT_TRUE(FieldTrialList::IsTrialActive("T2")); 439 } 440 441 TEST_F(FeatureListTest, InitializeInstance) { 442 ClearFeatureListInstance(); 443 444 std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList); 445 FeatureList::SetInstance(std::move(feature_list)); 446 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); 447 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); 448 449 // Initialize from command line if we haven't yet. 450 FeatureList::InitializeInstance("", kFeatureOnByDefaultName); 451 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault)); 452 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); 453 454 // Do not initialize from commandline if we have already. 455 FeatureList::InitializeInstance(kFeatureOffByDefaultName, ""); 456 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOnByDefault)); 457 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); 458 } 459 460 TEST_F(FeatureListTest, UninitializedInstance_IsEnabledReturnsFalse) { 461 ClearFeatureListInstance(); 462 // This test case simulates the calling pattern found in code which does not 463 // explicitly initialize the features list. 464 // All IsEnabled() calls should return the default value in this scenario. 465 EXPECT_EQ(nullptr, FeatureList::GetInstance()); 466 EXPECT_TRUE(FeatureList::IsEnabled(kFeatureOnByDefault)); 467 EXPECT_EQ(nullptr, FeatureList::GetInstance()); 468 EXPECT_FALSE(FeatureList::IsEnabled(kFeatureOffByDefault)); 469 } 470 471 } // namespace base 472