1 // Copyright (c) 2012 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/metrics/field_trial.h" 6 7 #include <stddef.h> 8 9 #include "base/base_switches.h" 10 #include "base/build_time.h" 11 #include "base/feature_list.h" 12 #include "base/macros.h" 13 #include "base/memory/ptr_util.h" 14 #include "base/message_loop/message_loop.h" 15 #include "base/metrics/field_trial_param_associator.h" 16 #include "base/rand_util.h" 17 #include "base/run_loop.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/stringprintf.h" 20 #include "base/test/gtest_util.h" 21 #include "base/test/mock_entropy_provider.h" 22 #include "base/test/scoped_feature_list.h" 23 #include "base/test/test_shared_memory_util.h" 24 #include "build/build_config.h" 25 #include "testing/gtest/include/gtest/gtest.h" 26 27 namespace base { 28 29 namespace { 30 31 // Default group name used by several tests. 32 const char kDefaultGroupName[] = "DefaultGroup"; 33 34 // Call FieldTrialList::FactoryGetFieldTrial() with a future expiry date. 35 scoped_refptr<FieldTrial> CreateFieldTrial( 36 const std::string& trial_name, 37 int total_probability, 38 const std::string& default_group_name, 39 int* default_group_number) { 40 return FieldTrialList::FactoryGetFieldTrial( 41 trial_name, total_probability, default_group_name, 42 FieldTrialList::kNoExpirationYear, 1, 1, FieldTrial::SESSION_RANDOMIZED, 43 default_group_number); 44 } 45 46 int OneYearBeforeBuildTime() { 47 Time one_year_before_build_time = GetBuildTime() - TimeDelta::FromDays(365); 48 Time::Exploded exploded; 49 one_year_before_build_time.LocalExplode(&exploded); 50 return exploded.year; 51 } 52 53 // FieldTrialList::Observer implementation for testing. 54 class TestFieldTrialObserver : public FieldTrialList::Observer { 55 public: 56 enum Type { 57 ASYNCHRONOUS, 58 SYNCHRONOUS, 59 }; 60 61 TestFieldTrialObserver(Type type) : type_(type) { 62 if (type == SYNCHRONOUS) 63 FieldTrialList::SetSynchronousObserver(this); 64 else 65 FieldTrialList::AddObserver(this); 66 } 67 68 ~TestFieldTrialObserver() override { 69 if (type_ == SYNCHRONOUS) 70 FieldTrialList::RemoveSynchronousObserver(this); 71 else 72 FieldTrialList::RemoveObserver(this); 73 } 74 75 void OnFieldTrialGroupFinalized(const std::string& trial, 76 const std::string& group) override { 77 trial_name_ = trial; 78 group_name_ = group; 79 } 80 81 const std::string& trial_name() const { return trial_name_; } 82 const std::string& group_name() const { return group_name_; } 83 84 private: 85 const Type type_; 86 std::string trial_name_; 87 std::string group_name_; 88 89 DISALLOW_COPY_AND_ASSIGN(TestFieldTrialObserver); 90 }; 91 92 std::string MockEscapeQueryParamValue(const std::string& input) { 93 return input; 94 } 95 96 } // namespace 97 98 class FieldTrialTest : public ::testing::Test { 99 public: 100 FieldTrialTest() : trial_list_(nullptr) {} 101 102 private: 103 MessageLoop message_loop_; 104 FieldTrialList trial_list_; 105 106 DISALLOW_COPY_AND_ASSIGN(FieldTrialTest); 107 }; 108 109 // Test registration, and also check that destructors are called for trials. 110 TEST_F(FieldTrialTest, Registration) { 111 const char name1[] = "name 1 test"; 112 const char name2[] = "name 2 test"; 113 EXPECT_FALSE(FieldTrialList::Find(name1)); 114 EXPECT_FALSE(FieldTrialList::Find(name2)); 115 116 scoped_refptr<FieldTrial> trial1 = 117 CreateFieldTrial(name1, 10, "default name 1 test", nullptr); 118 EXPECT_EQ(FieldTrial::kNotFinalized, trial1->group_); 119 EXPECT_EQ(name1, trial1->trial_name()); 120 EXPECT_EQ("", trial1->group_name_internal()); 121 122 trial1->AppendGroup(std::string(), 7); 123 124 EXPECT_EQ(trial1.get(), FieldTrialList::Find(name1)); 125 EXPECT_FALSE(FieldTrialList::Find(name2)); 126 127 scoped_refptr<FieldTrial> trial2 = 128 CreateFieldTrial(name2, 10, "default name 2 test", nullptr); 129 EXPECT_EQ(FieldTrial::kNotFinalized, trial2->group_); 130 EXPECT_EQ(name2, trial2->trial_name()); 131 EXPECT_EQ("", trial2->group_name_internal()); 132 133 trial2->AppendGroup("a first group", 7); 134 135 EXPECT_EQ(trial1.get(), FieldTrialList::Find(name1)); 136 EXPECT_EQ(trial2.get(), FieldTrialList::Find(name2)); 137 // Note: FieldTrialList should delete the objects at shutdown. 138 } 139 140 TEST_F(FieldTrialTest, AbsoluteProbabilities) { 141 char always_true[] = " always true"; 142 char default_always_true[] = " default always true"; 143 char always_false[] = " always false"; 144 char default_always_false[] = " default always false"; 145 for (int i = 1; i < 250; ++i) { 146 // Try lots of names, by changing the first character of the name. 147 char c = static_cast<char>(i); 148 always_true[0] = c; 149 default_always_true[0] = c; 150 always_false[0] = c; 151 default_always_false[0] = c; 152 153 scoped_refptr<FieldTrial> trial_true = 154 CreateFieldTrial(always_true, 10, default_always_true, nullptr); 155 const std::string winner = "TheWinner"; 156 int winner_group = trial_true->AppendGroup(winner, 10); 157 158 EXPECT_EQ(winner_group, trial_true->group()); 159 EXPECT_EQ(winner, trial_true->group_name()); 160 161 scoped_refptr<FieldTrial> trial_false = 162 CreateFieldTrial(always_false, 10, default_always_false, nullptr); 163 int loser_group = trial_false->AppendGroup("ALoser", 0); 164 165 EXPECT_NE(loser_group, trial_false->group()); 166 } 167 } 168 169 TEST_F(FieldTrialTest, RemainingProbability) { 170 // First create a test that hasn't had a winner yet. 171 const std::string winner = "Winner"; 172 const std::string loser = "Loser"; 173 scoped_refptr<FieldTrial> trial; 174 int counter = 0; 175 int default_group_number = -1; 176 do { 177 std::string name = StringPrintf("trial%d", ++counter); 178 trial = CreateFieldTrial(name, 10, winner, &default_group_number); 179 trial->AppendGroup(loser, 5); // 50% chance of not being chosen. 180 // If a group is not assigned, group_ will be kNotFinalized. 181 } while (trial->group_ != FieldTrial::kNotFinalized); 182 183 // And that 'default' group (winner) should always win. 184 EXPECT_EQ(default_group_number, trial->group()); 185 186 // And that winner should ALWAYS win. 187 EXPECT_EQ(winner, trial->group_name()); 188 } 189 190 TEST_F(FieldTrialTest, FiftyFiftyProbability) { 191 // Check that even with small divisors, we have the proper probabilities, and 192 // all outcomes are possible. Since this is a 50-50 test, it should get both 193 // outcomes in a few tries, but we'll try no more than 100 times (and be flaky 194 // with probability around 1 in 2^99). 195 bool first_winner = false; 196 bool second_winner = false; 197 int counter = 0; 198 do { 199 std::string name = StringPrintf("FiftyFifty%d", ++counter); 200 std::string default_group_name = 201 StringPrintf("Default FiftyFifty%d", ++counter); 202 scoped_refptr<FieldTrial> trial = 203 CreateFieldTrial(name, 2, default_group_name, nullptr); 204 trial->AppendGroup("first", 1); // 50% chance of being chosen. 205 // If group_ is kNotFinalized, then a group assignement hasn't been done. 206 if (trial->group_ != FieldTrial::kNotFinalized) { 207 first_winner = true; 208 continue; 209 } 210 trial->AppendGroup("second", 1); // Always chosen at this point. 211 EXPECT_NE(FieldTrial::kNotFinalized, trial->group()); 212 second_winner = true; 213 } while ((!second_winner || !first_winner) && counter < 100); 214 EXPECT_TRUE(second_winner); 215 EXPECT_TRUE(first_winner); 216 } 217 218 TEST_F(FieldTrialTest, MiddleProbabilities) { 219 char name[] = " same name"; 220 char default_group_name[] = " default same name"; 221 bool false_event_seen = false; 222 bool true_event_seen = false; 223 for (int i = 1; i < 250; ++i) { 224 char c = static_cast<char>(i); 225 name[0] = c; 226 default_group_name[0] = c; 227 scoped_refptr<FieldTrial> trial = 228 CreateFieldTrial(name, 10, default_group_name, nullptr); 229 int might_win = trial->AppendGroup("MightWin", 5); 230 231 if (trial->group() == might_win) { 232 true_event_seen = true; 233 } else { 234 false_event_seen = true; 235 } 236 if (false_event_seen && true_event_seen) 237 return; // Successful test!!! 238 } 239 // Very surprising to get here. Probability should be around 1 in 2 ** 250. 240 // One of the following will fail. 241 EXPECT_TRUE(false_event_seen); 242 EXPECT_TRUE(true_event_seen); 243 } 244 245 TEST_F(FieldTrialTest, OneWinner) { 246 char name[] = "Some name"; 247 char default_group_name[] = "Default some name"; 248 int group_count(10); 249 250 int default_group_number = -1; 251 scoped_refptr<FieldTrial> trial = 252 CreateFieldTrial(name, group_count, default_group_name, nullptr); 253 int winner_index(-2); 254 std::string winner_name; 255 256 for (int i = 1; i <= group_count; ++i) { 257 int might_win = trial->AppendGroup(std::string(), 1); 258 259 // Because we keep appending groups, we want to see if the last group that 260 // was added has been assigned or not. 261 if (trial->group_ == might_win) { 262 EXPECT_EQ(-2, winner_index); 263 winner_index = might_win; 264 StringAppendF(&winner_name, "%d", might_win); 265 EXPECT_EQ(winner_name, trial->group_name()); 266 } 267 } 268 EXPECT_GE(winner_index, 0); 269 // Since all groups cover the total probability, we should not have 270 // chosen the default group. 271 EXPECT_NE(trial->group(), default_group_number); 272 EXPECT_EQ(trial->group(), winner_index); 273 EXPECT_EQ(trial->group_name(), winner_name); 274 } 275 276 TEST_F(FieldTrialTest, DisableProbability) { 277 const std::string default_group_name = "Default group"; 278 const std::string loser = "Loser"; 279 const std::string name = "Trial"; 280 281 // Create a field trail that has expired. 282 int default_group_number = -1; 283 FieldTrial* trial = FieldTrialList::FactoryGetFieldTrial( 284 name, 1000000000, default_group_name, OneYearBeforeBuildTime(), 1, 1, 285 FieldTrial::SESSION_RANDOMIZED, 286 &default_group_number); 287 trial->AppendGroup(loser, 999999999); // 99.9999999% chance of being chosen. 288 289 // Because trial has expired, we should always be in the default group. 290 EXPECT_EQ(default_group_number, trial->group()); 291 292 // And that default_group_name should ALWAYS win. 293 EXPECT_EQ(default_group_name, trial->group_name()); 294 } 295 296 TEST_F(FieldTrialTest, ActiveGroups) { 297 std::string no_group("No Group"); 298 scoped_refptr<FieldTrial> trial = 299 CreateFieldTrial(no_group, 10, "Default", nullptr); 300 301 // There is no winner yet, so no NameGroupId should be returned. 302 FieldTrial::ActiveGroup active_group; 303 EXPECT_FALSE(trial->GetActiveGroup(&active_group)); 304 305 // Create a single winning group. 306 std::string one_winner("One Winner"); 307 trial = CreateFieldTrial(one_winner, 10, "Default", nullptr); 308 std::string winner("Winner"); 309 trial->AppendGroup(winner, 10); 310 EXPECT_FALSE(trial->GetActiveGroup(&active_group)); 311 // Finalize the group selection by accessing the selected group. 312 trial->group(); 313 EXPECT_TRUE(trial->GetActiveGroup(&active_group)); 314 EXPECT_EQ(one_winner, active_group.trial_name); 315 EXPECT_EQ(winner, active_group.group_name); 316 317 std::string multi_group("MultiGroup"); 318 scoped_refptr<FieldTrial> multi_group_trial = 319 CreateFieldTrial(multi_group, 9, "Default", nullptr); 320 321 multi_group_trial->AppendGroup("Me", 3); 322 multi_group_trial->AppendGroup("You", 3); 323 multi_group_trial->AppendGroup("Them", 3); 324 EXPECT_FALSE(multi_group_trial->GetActiveGroup(&active_group)); 325 // Finalize the group selection by accessing the selected group. 326 multi_group_trial->group(); 327 EXPECT_TRUE(multi_group_trial->GetActiveGroup(&active_group)); 328 EXPECT_EQ(multi_group, active_group.trial_name); 329 EXPECT_EQ(multi_group_trial->group_name(), active_group.group_name); 330 331 // Now check if the list is built properly... 332 FieldTrial::ActiveGroups active_groups; 333 FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 334 EXPECT_EQ(2U, active_groups.size()); 335 for (size_t i = 0; i < active_groups.size(); ++i) { 336 // Order is not guaranteed, so check all values. 337 EXPECT_NE(no_group, active_groups[i].trial_name); 338 EXPECT_TRUE(one_winner != active_groups[i].trial_name || 339 winner == active_groups[i].group_name); 340 EXPECT_TRUE(multi_group != active_groups[i].trial_name || 341 multi_group_trial->group_name() == active_groups[i].group_name); 342 } 343 } 344 345 TEST_F(FieldTrialTest, GetActiveFieldTrialGroupsFromString) { 346 FieldTrial::ActiveGroups active_groups; 347 FieldTrialList::GetActiveFieldTrialGroupsFromString("*A/X/B/Y/*C/Z", 348 &active_groups); 349 ASSERT_EQ(2U, active_groups.size()); 350 EXPECT_EQ("A", active_groups[0].trial_name); 351 EXPECT_EQ("X", active_groups[0].group_name); 352 EXPECT_EQ("C", active_groups[1].trial_name); 353 EXPECT_EQ("Z", active_groups[1].group_name); 354 } 355 356 TEST_F(FieldTrialTest, ActiveGroupsNotFinalized) { 357 const char kTrialName[] = "TestTrial"; 358 const char kSecondaryGroupName[] = "SecondaryGroup"; 359 360 int default_group = -1; 361 scoped_refptr<FieldTrial> trial = 362 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); 363 const int secondary_group = trial->AppendGroup(kSecondaryGroupName, 50); 364 365 // Before |group()| is called, |GetActiveGroup()| should return false. 366 FieldTrial::ActiveGroup active_group; 367 EXPECT_FALSE(trial->GetActiveGroup(&active_group)); 368 369 // |GetActiveFieldTrialGroups()| should also not include the trial. 370 FieldTrial::ActiveGroups active_groups; 371 FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 372 EXPECT_TRUE(active_groups.empty()); 373 374 // After |group()| has been called, both APIs should succeed. 375 const int chosen_group = trial->group(); 376 EXPECT_TRUE(chosen_group == default_group || chosen_group == secondary_group); 377 378 EXPECT_TRUE(trial->GetActiveGroup(&active_group)); 379 EXPECT_EQ(kTrialName, active_group.trial_name); 380 if (chosen_group == default_group) 381 EXPECT_EQ(kDefaultGroupName, active_group.group_name); 382 else 383 EXPECT_EQ(kSecondaryGroupName, active_group.group_name); 384 385 FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 386 ASSERT_EQ(1U, active_groups.size()); 387 EXPECT_EQ(kTrialName, active_groups[0].trial_name); 388 EXPECT_EQ(active_group.group_name, active_groups[0].group_name); 389 } 390 391 TEST_F(FieldTrialTest, GetGroupNameWithoutActivation) { 392 const char kTrialName[] = "TestTrial"; 393 const char kSecondaryGroupName[] = "SecondaryGroup"; 394 395 int default_group = -1; 396 scoped_refptr<FieldTrial> trial = 397 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); 398 trial->AppendGroup(kSecondaryGroupName, 50); 399 400 // The trial should start inactive. 401 EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); 402 403 // Calling |GetGroupNameWithoutActivation()| should not activate the trial. 404 std::string group_name = trial->GetGroupNameWithoutActivation(); 405 EXPECT_FALSE(group_name.empty()); 406 EXPECT_FALSE(FieldTrialList::IsTrialActive(kTrialName)); 407 408 // Calling |group_name()| should activate it and return the same group name. 409 EXPECT_EQ(group_name, trial->group_name()); 410 EXPECT_TRUE(FieldTrialList::IsTrialActive(kTrialName)); 411 } 412 413 TEST_F(FieldTrialTest, Save) { 414 std::string save_string; 415 416 scoped_refptr<FieldTrial> trial = 417 CreateFieldTrial("Some name", 10, "Default some name", nullptr); 418 // There is no winner yet, so no textual group name is associated with trial. 419 // In this case, the trial should not be included. 420 EXPECT_EQ("", trial->group_name_internal()); 421 FieldTrialList::StatesToString(&save_string); 422 EXPECT_EQ("", save_string); 423 save_string.clear(); 424 425 // Create a winning group. 426 trial->AppendGroup("Winner", 10); 427 // Finalize the group selection by accessing the selected group. 428 trial->group(); 429 FieldTrialList::StatesToString(&save_string); 430 EXPECT_EQ("Some name/Winner/", save_string); 431 save_string.clear(); 432 433 // Create a second trial and winning group. 434 scoped_refptr<FieldTrial> trial2 = 435 CreateFieldTrial("xxx", 10, "Default xxx", nullptr); 436 trial2->AppendGroup("yyyy", 10); 437 // Finalize the group selection by accessing the selected group. 438 trial2->group(); 439 440 FieldTrialList::StatesToString(&save_string); 441 // We assume names are alphabetized... though this is not critical. 442 EXPECT_EQ("Some name/Winner/xxx/yyyy/", save_string); 443 save_string.clear(); 444 445 // Create a third trial with only the default group. 446 scoped_refptr<FieldTrial> trial3 = 447 CreateFieldTrial("zzz", 10, "default", nullptr); 448 // Finalize the group selection by accessing the selected group. 449 trial3->group(); 450 451 FieldTrialList::StatesToString(&save_string); 452 EXPECT_EQ("Some name/Winner/xxx/yyyy/zzz/default/", save_string); 453 } 454 455 TEST_F(FieldTrialTest, SaveAll) { 456 std::string save_string; 457 458 scoped_refptr<FieldTrial> trial = 459 CreateFieldTrial("Some name", 10, "Default some name", nullptr); 460 EXPECT_EQ("", trial->group_name_internal()); 461 FieldTrialList::AllStatesToString(&save_string, false); 462 EXPECT_EQ("Some name/Default some name/", save_string); 463 // Getting all states should have finalized the trial. 464 EXPECT_EQ("Default some name", trial->group_name_internal()); 465 save_string.clear(); 466 467 // Create a winning group. 468 trial = CreateFieldTrial("trial2", 10, "Default some name", nullptr); 469 trial->AppendGroup("Winner", 10); 470 // Finalize the group selection by accessing the selected group. 471 trial->group(); 472 FieldTrialList::AllStatesToString(&save_string, false); 473 EXPECT_EQ("Some name/Default some name/*trial2/Winner/", save_string); 474 save_string.clear(); 475 476 // Create a second trial and winning group. 477 scoped_refptr<FieldTrial> trial2 = 478 CreateFieldTrial("xxx", 10, "Default xxx", nullptr); 479 trial2->AppendGroup("yyyy", 10); 480 // Finalize the group selection by accessing the selected group. 481 trial2->group(); 482 483 FieldTrialList::AllStatesToString(&save_string, false); 484 // We assume names are alphabetized... though this is not critical. 485 EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/", 486 save_string); 487 save_string.clear(); 488 489 // Create a third trial with only the default group. 490 scoped_refptr<FieldTrial> trial3 = 491 CreateFieldTrial("zzz", 10, "default", nullptr); 492 493 FieldTrialList::AllStatesToString(&save_string, false); 494 EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default/", 495 save_string); 496 497 // Create expired study. 498 int default_group_number = -1; 499 scoped_refptr<FieldTrial> expired_trial = 500 FieldTrialList::FactoryGetFieldTrial( 501 "Expired trial name", 1000000000, "Default group", 502 OneYearBeforeBuildTime(), 1, 1, FieldTrial::SESSION_RANDOMIZED, 503 &default_group_number); 504 expired_trial->AppendGroup("Expired trial group name", 999999999); 505 506 save_string.clear(); 507 FieldTrialList::AllStatesToString(&save_string, false); 508 EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default/", 509 save_string); 510 save_string.clear(); 511 FieldTrialList::AllStatesToString(&save_string, true); 512 EXPECT_EQ( 513 "Expired trial name/Default group/" 514 "Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default/", 515 save_string); 516 } 517 518 TEST_F(FieldTrialTest, Restore) { 519 ASSERT_FALSE(FieldTrialList::TrialExists("Some_name")); 520 ASSERT_FALSE(FieldTrialList::TrialExists("xxx")); 521 522 FieldTrialList::CreateTrialsFromString("Some_name/Winner/xxx/yyyy/", 523 std::set<std::string>()); 524 525 FieldTrial* trial = FieldTrialList::Find("Some_name"); 526 ASSERT_NE(static_cast<FieldTrial*>(nullptr), trial); 527 EXPECT_EQ("Winner", trial->group_name()); 528 EXPECT_EQ("Some_name", trial->trial_name()); 529 530 trial = FieldTrialList::Find("xxx"); 531 ASSERT_NE(static_cast<FieldTrial*>(nullptr), trial); 532 EXPECT_EQ("yyyy", trial->group_name()); 533 EXPECT_EQ("xxx", trial->trial_name()); 534 } 535 536 TEST_F(FieldTrialTest, RestoreNotEndingWithSlash) { 537 EXPECT_TRUE(FieldTrialList::CreateTrialsFromString("tname/gname", 538 std::set<std::string>())); 539 540 FieldTrial* trial = FieldTrialList::Find("tname"); 541 ASSERT_NE(static_cast<FieldTrial*>(nullptr), trial); 542 EXPECT_EQ("gname", trial->group_name()); 543 EXPECT_EQ("tname", trial->trial_name()); 544 } 545 546 TEST_F(FieldTrialTest, BogusRestore) { 547 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("MissingSlash", 548 std::set<std::string>())); 549 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("MissingGroupName/", 550 std::set<std::string>())); 551 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("noname, only group/", 552 std::set<std::string>())); 553 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("/emptyname", 554 std::set<std::string>())); 555 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("*/emptyname", 556 std::set<std::string>())); 557 } 558 559 TEST_F(FieldTrialTest, DuplicateRestore) { 560 scoped_refptr<FieldTrial> trial = 561 CreateFieldTrial("Some name", 10, "Default", nullptr); 562 trial->AppendGroup("Winner", 10); 563 // Finalize the group selection by accessing the selected group. 564 trial->group(); 565 std::string save_string; 566 FieldTrialList::StatesToString(&save_string); 567 EXPECT_EQ("Some name/Winner/", save_string); 568 569 // It is OK if we redundantly specify a winner. 570 EXPECT_TRUE(FieldTrialList::CreateTrialsFromString(save_string, 571 std::set<std::string>())); 572 573 // But it is an error to try to change to a different winner. 574 EXPECT_FALSE(FieldTrialList::CreateTrialsFromString("Some name/Loser/", 575 std::set<std::string>())); 576 } 577 578 TEST_F(FieldTrialTest, CreateTrialsFromStringNotActive) { 579 ASSERT_FALSE(FieldTrialList::TrialExists("Abc")); 580 ASSERT_FALSE(FieldTrialList::TrialExists("Xyz")); 581 ASSERT_TRUE(FieldTrialList::CreateTrialsFromString("Abc/def/Xyz/zyx/", 582 std::set<std::string>())); 583 584 FieldTrial::ActiveGroups active_groups; 585 FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 586 ASSERT_TRUE(active_groups.empty()); 587 588 // Check that the values still get returned and querying them activates them. 589 EXPECT_EQ("def", FieldTrialList::FindFullName("Abc")); 590 EXPECT_EQ("zyx", FieldTrialList::FindFullName("Xyz")); 591 592 FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 593 ASSERT_EQ(2U, active_groups.size()); 594 EXPECT_EQ("Abc", active_groups[0].trial_name); 595 EXPECT_EQ("def", active_groups[0].group_name); 596 EXPECT_EQ("Xyz", active_groups[1].trial_name); 597 EXPECT_EQ("zyx", active_groups[1].group_name); 598 } 599 600 TEST_F(FieldTrialTest, CreateTrialsFromStringForceActivation) { 601 ASSERT_FALSE(FieldTrialList::TrialExists("Abc")); 602 ASSERT_FALSE(FieldTrialList::TrialExists("def")); 603 ASSERT_FALSE(FieldTrialList::TrialExists("Xyz")); 604 ASSERT_TRUE(FieldTrialList::CreateTrialsFromString( 605 "*Abc/cba/def/fed/*Xyz/zyx/", std::set<std::string>())); 606 607 FieldTrial::ActiveGroups active_groups; 608 FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 609 ASSERT_EQ(2U, active_groups.size()); 610 EXPECT_EQ("Abc", active_groups[0].trial_name); 611 EXPECT_EQ("cba", active_groups[0].group_name); 612 EXPECT_EQ("Xyz", active_groups[1].trial_name); 613 EXPECT_EQ("zyx", active_groups[1].group_name); 614 } 615 616 TEST_F(FieldTrialTest, CreateTrialsFromStringNotActiveObserver) { 617 ASSERT_FALSE(FieldTrialList::TrialExists("Abc")); 618 619 TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS); 620 ASSERT_TRUE(FieldTrialList::CreateTrialsFromString("Abc/def/", 621 std::set<std::string>())); 622 RunLoop().RunUntilIdle(); 623 // Observer shouldn't be notified. 624 EXPECT_TRUE(observer.trial_name().empty()); 625 626 // Check that the values still get returned and querying them activates them. 627 EXPECT_EQ("def", FieldTrialList::FindFullName("Abc")); 628 629 RunLoop().RunUntilIdle(); 630 EXPECT_EQ("Abc", observer.trial_name()); 631 EXPECT_EQ("def", observer.group_name()); 632 } 633 634 TEST_F(FieldTrialTest, CreateTrialsFromStringWithIgnoredFieldTrials) { 635 ASSERT_FALSE(FieldTrialList::TrialExists("Unaccepted1")); 636 ASSERT_FALSE(FieldTrialList::TrialExists("Foo")); 637 ASSERT_FALSE(FieldTrialList::TrialExists("Unaccepted2")); 638 ASSERT_FALSE(FieldTrialList::TrialExists("Bar")); 639 ASSERT_FALSE(FieldTrialList::TrialExists("Unaccepted3")); 640 641 std::set<std::string> ignored_trial_names; 642 ignored_trial_names.insert("Unaccepted1"); 643 ignored_trial_names.insert("Unaccepted2"); 644 ignored_trial_names.insert("Unaccepted3"); 645 646 FieldTrialList::CreateTrialsFromString( 647 "Unaccepted1/Unaccepted1_name/" 648 "Foo/Foo_name/" 649 "Unaccepted2/Unaccepted2_name/" 650 "Bar/Bar_name/" 651 "Unaccepted3/Unaccepted3_name/", 652 ignored_trial_names); 653 654 EXPECT_FALSE(FieldTrialList::TrialExists("Unaccepted1")); 655 EXPECT_TRUE(FieldTrialList::TrialExists("Foo")); 656 EXPECT_FALSE(FieldTrialList::TrialExists("Unaccepted2")); 657 EXPECT_TRUE(FieldTrialList::TrialExists("Bar")); 658 EXPECT_FALSE(FieldTrialList::TrialExists("Unaccepted3")); 659 660 FieldTrial::ActiveGroups active_groups; 661 FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 662 EXPECT_TRUE(active_groups.empty()); 663 664 FieldTrial* trial = FieldTrialList::Find("Foo"); 665 ASSERT_NE(static_cast<FieldTrial*>(nullptr), trial); 666 EXPECT_EQ("Foo", trial->trial_name()); 667 EXPECT_EQ("Foo_name", trial->group_name()); 668 669 trial = FieldTrialList::Find("Bar"); 670 ASSERT_NE(static_cast<FieldTrial*>(nullptr), trial); 671 EXPECT_EQ("Bar", trial->trial_name()); 672 EXPECT_EQ("Bar_name", trial->group_name()); 673 } 674 675 TEST_F(FieldTrialTest, CreateFieldTrial) { 676 ASSERT_FALSE(FieldTrialList::TrialExists("Some_name")); 677 678 FieldTrialList::CreateFieldTrial("Some_name", "Winner"); 679 680 FieldTrial* trial = FieldTrialList::Find("Some_name"); 681 ASSERT_NE(static_cast<FieldTrial*>(nullptr), trial); 682 EXPECT_EQ("Winner", trial->group_name()); 683 EXPECT_EQ("Some_name", trial->trial_name()); 684 } 685 686 TEST_F(FieldTrialTest, CreateFieldTrialIsNotActive) { 687 const char kTrialName[] = "CreateFieldTrialIsActiveTrial"; 688 const char kWinnerGroup[] = "Winner"; 689 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); 690 FieldTrialList::CreateFieldTrial(kTrialName, kWinnerGroup); 691 692 FieldTrial::ActiveGroups active_groups; 693 FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 694 EXPECT_TRUE(active_groups.empty()); 695 } 696 697 TEST_F(FieldTrialTest, DuplicateFieldTrial) { 698 scoped_refptr<FieldTrial> trial = 699 CreateFieldTrial("Some_name", 10, "Default", nullptr); 700 trial->AppendGroup("Winner", 10); 701 702 // It is OK if we redundantly specify a winner. 703 FieldTrial* trial1 = FieldTrialList::CreateFieldTrial("Some_name", "Winner"); 704 EXPECT_TRUE(trial1 != nullptr); 705 706 // But it is an error to try to change to a different winner. 707 FieldTrial* trial2 = FieldTrialList::CreateFieldTrial("Some_name", "Loser"); 708 EXPECT_TRUE(trial2 == nullptr); 709 } 710 711 TEST_F(FieldTrialTest, DisableImmediately) { 712 int default_group_number = -1; 713 scoped_refptr<FieldTrial> trial = 714 CreateFieldTrial("trial", 100, "default", &default_group_number); 715 trial->Disable(); 716 ASSERT_EQ("default", trial->group_name()); 717 ASSERT_EQ(default_group_number, trial->group()); 718 } 719 720 TEST_F(FieldTrialTest, DisableAfterInitialization) { 721 scoped_refptr<FieldTrial> trial = 722 CreateFieldTrial("trial", 100, "default", nullptr); 723 trial->AppendGroup("non_default", 100); 724 trial->Disable(); 725 ASSERT_EQ("default", trial->group_name()); 726 } 727 728 TEST_F(FieldTrialTest, ForcedFieldTrials) { 729 // Validate we keep the forced choice. 730 FieldTrial* forced_trial = FieldTrialList::CreateFieldTrial("Use the", 731 "Force"); 732 EXPECT_STREQ("Force", forced_trial->group_name().c_str()); 733 734 int default_group_number = -1; 735 scoped_refptr<FieldTrial> factory_trial = 736 CreateFieldTrial("Use the", 1000, "default", &default_group_number); 737 EXPECT_EQ(factory_trial.get(), forced_trial); 738 739 int chosen_group = factory_trial->AppendGroup("Force", 100); 740 EXPECT_EQ(chosen_group, factory_trial->group()); 741 int not_chosen_group = factory_trial->AppendGroup("Dark Side", 100); 742 EXPECT_NE(chosen_group, not_chosen_group); 743 744 // Since we didn't force the default group, we should not be returned the 745 // chosen group as the default group. 746 EXPECT_NE(default_group_number, chosen_group); 747 int new_group = factory_trial->AppendGroup("Duck Tape", 800); 748 EXPECT_NE(chosen_group, new_group); 749 // The new group should not be the default group either. 750 EXPECT_NE(default_group_number, new_group); 751 } 752 753 TEST_F(FieldTrialTest, ForcedFieldTrialsDefaultGroup) { 754 // Forcing the default should use the proper group ID. 755 FieldTrial* forced_trial = FieldTrialList::CreateFieldTrial("Trial Name", 756 "Default"); 757 int default_group_number = -1; 758 scoped_refptr<FieldTrial> factory_trial = 759 CreateFieldTrial("Trial Name", 1000, "Default", &default_group_number); 760 EXPECT_EQ(forced_trial, factory_trial.get()); 761 762 int other_group = factory_trial->AppendGroup("Not Default", 100); 763 EXPECT_STREQ("Default", factory_trial->group_name().c_str()); 764 EXPECT_EQ(default_group_number, factory_trial->group()); 765 EXPECT_NE(other_group, factory_trial->group()); 766 767 int new_other_group = factory_trial->AppendGroup("Not Default Either", 800); 768 EXPECT_NE(new_other_group, factory_trial->group()); 769 } 770 771 TEST_F(FieldTrialTest, SetForced) { 772 // Start by setting a trial for which we ensure a winner... 773 int default_group_number = -1; 774 scoped_refptr<FieldTrial> forced_trial = 775 CreateFieldTrial("Use the", 1, "default", &default_group_number); 776 EXPECT_EQ(forced_trial, forced_trial); 777 778 int forced_group = forced_trial->AppendGroup("Force", 1); 779 EXPECT_EQ(forced_group, forced_trial->group()); 780 781 // Now force it. 782 forced_trial->SetForced(); 783 784 // Now try to set it up differently as a hard coded registration would. 785 scoped_refptr<FieldTrial> hard_coded_trial = 786 CreateFieldTrial("Use the", 1, "default", &default_group_number); 787 EXPECT_EQ(hard_coded_trial, forced_trial); 788 789 int would_lose_group = hard_coded_trial->AppendGroup("Force", 0); 790 EXPECT_EQ(forced_group, hard_coded_trial->group()); 791 EXPECT_EQ(forced_group, would_lose_group); 792 793 // Same thing if we would have done it to win again. 794 scoped_refptr<FieldTrial> other_hard_coded_trial = 795 CreateFieldTrial("Use the", 1, "default", &default_group_number); 796 EXPECT_EQ(other_hard_coded_trial, forced_trial); 797 798 int would_win_group = other_hard_coded_trial->AppendGroup("Force", 1); 799 EXPECT_EQ(forced_group, other_hard_coded_trial->group()); 800 EXPECT_EQ(forced_group, would_win_group); 801 } 802 803 TEST_F(FieldTrialTest, SetForcedDefaultOnly) { 804 const char kTrialName[] = "SetForcedDefaultOnly"; 805 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); 806 807 int default_group = -1; 808 scoped_refptr<FieldTrial> trial = 809 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); 810 trial->SetForced(); 811 812 trial = CreateFieldTrial(kTrialName, 100, kDefaultGroupName, nullptr); 813 EXPECT_EQ(default_group, trial->group()); 814 EXPECT_EQ(kDefaultGroupName, trial->group_name()); 815 } 816 817 TEST_F(FieldTrialTest, SetForcedDefaultWithExtraGroup) { 818 const char kTrialName[] = "SetForcedDefaultWithExtraGroup"; 819 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); 820 821 int default_group = -1; 822 scoped_refptr<FieldTrial> trial = 823 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); 824 trial->SetForced(); 825 826 trial = CreateFieldTrial(kTrialName, 100, kDefaultGroupName, nullptr); 827 const int extra_group = trial->AppendGroup("Extra", 100); 828 EXPECT_EQ(default_group, trial->group()); 829 EXPECT_NE(extra_group, trial->group()); 830 EXPECT_EQ(kDefaultGroupName, trial->group_name()); 831 } 832 833 TEST_F(FieldTrialTest, SetForcedTurnFeatureOn) { 834 const char kTrialName[] = "SetForcedTurnFeatureOn"; 835 const char kExtraGroupName[] = "Extra"; 836 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); 837 838 // Simulate a server-side (forced) config that turns the feature on when the 839 // original hard-coded config had it disabled. 840 scoped_refptr<FieldTrial> forced_trial = 841 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, nullptr); 842 forced_trial->AppendGroup(kExtraGroupName, 100); 843 forced_trial->SetForced(); 844 845 int default_group = -1; 846 scoped_refptr<FieldTrial> client_trial = 847 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); 848 const int extra_group = client_trial->AppendGroup(kExtraGroupName, 0); 849 EXPECT_NE(default_group, extra_group); 850 851 EXPECT_FALSE(client_trial->group_reported_); 852 EXPECT_EQ(extra_group, client_trial->group()); 853 EXPECT_TRUE(client_trial->group_reported_); 854 EXPECT_EQ(kExtraGroupName, client_trial->group_name()); 855 } 856 857 TEST_F(FieldTrialTest, SetForcedTurnFeatureOff) { 858 const char kTrialName[] = "SetForcedTurnFeatureOff"; 859 const char kExtraGroupName[] = "Extra"; 860 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); 861 862 // Simulate a server-side (forced) config that turns the feature off when the 863 // original hard-coded config had it enabled. 864 scoped_refptr<FieldTrial> forced_trial = 865 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, nullptr); 866 forced_trial->AppendGroup(kExtraGroupName, 0); 867 forced_trial->SetForced(); 868 869 int default_group = -1; 870 scoped_refptr<FieldTrial> client_trial = 871 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); 872 const int extra_group = client_trial->AppendGroup(kExtraGroupName, 100); 873 EXPECT_NE(default_group, extra_group); 874 875 EXPECT_FALSE(client_trial->group_reported_); 876 EXPECT_EQ(default_group, client_trial->group()); 877 EXPECT_TRUE(client_trial->group_reported_); 878 EXPECT_EQ(kDefaultGroupName, client_trial->group_name()); 879 } 880 881 TEST_F(FieldTrialTest, SetForcedChangeDefault_Default) { 882 const char kTrialName[] = "SetForcedDefaultGroupChange"; 883 const char kGroupAName[] = "A"; 884 const char kGroupBName[] = "B"; 885 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); 886 887 // Simulate a server-side (forced) config that switches which group is default 888 // and ensures that the non-forced code receives the correct group numbers. 889 scoped_refptr<FieldTrial> forced_trial = 890 CreateFieldTrial(kTrialName, 100, kGroupAName, nullptr); 891 forced_trial->AppendGroup(kGroupBName, 100); 892 forced_trial->SetForced(); 893 894 int default_group = -1; 895 scoped_refptr<FieldTrial> client_trial = 896 CreateFieldTrial(kTrialName, 100, kGroupBName, &default_group); 897 const int extra_group = client_trial->AppendGroup(kGroupAName, 50); 898 EXPECT_NE(default_group, extra_group); 899 900 EXPECT_FALSE(client_trial->group_reported_); 901 EXPECT_EQ(default_group, client_trial->group()); 902 EXPECT_TRUE(client_trial->group_reported_); 903 EXPECT_EQ(kGroupBName, client_trial->group_name()); 904 } 905 906 TEST_F(FieldTrialTest, SetForcedChangeDefault_NonDefault) { 907 const char kTrialName[] = "SetForcedDefaultGroupChange"; 908 const char kGroupAName[] = "A"; 909 const char kGroupBName[] = "B"; 910 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); 911 912 // Simulate a server-side (forced) config that switches which group is default 913 // and ensures that the non-forced code receives the correct group numbers. 914 scoped_refptr<FieldTrial> forced_trial = 915 CreateFieldTrial(kTrialName, 100, kGroupAName, nullptr); 916 forced_trial->AppendGroup(kGroupBName, 0); 917 forced_trial->SetForced(); 918 919 int default_group = -1; 920 scoped_refptr<FieldTrial> client_trial = 921 CreateFieldTrial(kTrialName, 100, kGroupBName, &default_group); 922 const int extra_group = client_trial->AppendGroup(kGroupAName, 50); 923 EXPECT_NE(default_group, extra_group); 924 925 EXPECT_FALSE(client_trial->group_reported_); 926 EXPECT_EQ(extra_group, client_trial->group()); 927 EXPECT_TRUE(client_trial->group_reported_); 928 EXPECT_EQ(kGroupAName, client_trial->group_name()); 929 } 930 931 TEST_F(FieldTrialTest, Observe) { 932 const char kTrialName[] = "TrialToObserve1"; 933 const char kSecondaryGroupName[] = "SecondaryGroup"; 934 935 TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS); 936 int default_group = -1; 937 scoped_refptr<FieldTrial> trial = 938 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); 939 const int secondary_group = trial->AppendGroup(kSecondaryGroupName, 50); 940 const int chosen_group = trial->group(); 941 EXPECT_TRUE(chosen_group == default_group || chosen_group == secondary_group); 942 943 // Observers are called asynchronously. 944 EXPECT_TRUE(observer.trial_name().empty()); 945 EXPECT_TRUE(observer.group_name().empty()); 946 RunLoop().RunUntilIdle(); 947 948 EXPECT_EQ(kTrialName, observer.trial_name()); 949 if (chosen_group == default_group) 950 EXPECT_EQ(kDefaultGroupName, observer.group_name()); 951 else 952 EXPECT_EQ(kSecondaryGroupName, observer.group_name()); 953 } 954 955 TEST_F(FieldTrialTest, SynchronousObserver) { 956 const char kTrialName[] = "TrialToObserve1"; 957 const char kSecondaryGroupName[] = "SecondaryGroup"; 958 959 TestFieldTrialObserver observer(TestFieldTrialObserver::SYNCHRONOUS); 960 int default_group = -1; 961 scoped_refptr<FieldTrial> trial = 962 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); 963 const int secondary_group = trial->AppendGroup(kSecondaryGroupName, 50); 964 const int chosen_group = trial->group(); 965 EXPECT_TRUE(chosen_group == default_group || chosen_group == secondary_group); 966 967 // The observer should be notified synchronously by the group() call. 968 EXPECT_EQ(kTrialName, observer.trial_name()); 969 if (chosen_group == default_group) 970 EXPECT_EQ(kDefaultGroupName, observer.group_name()); 971 else 972 EXPECT_EQ(kSecondaryGroupName, observer.group_name()); 973 } 974 975 TEST_F(FieldTrialTest, ObserveDisabled) { 976 const char kTrialName[] = "TrialToObserve2"; 977 978 TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS); 979 int default_group = -1; 980 scoped_refptr<FieldTrial> trial = 981 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); 982 trial->AppendGroup("A", 25); 983 trial->AppendGroup("B", 25); 984 trial->AppendGroup("C", 25); 985 trial->Disable(); 986 987 // Observer shouldn't be notified of a disabled trial. 988 RunLoop().RunUntilIdle(); 989 EXPECT_TRUE(observer.trial_name().empty()); 990 EXPECT_TRUE(observer.group_name().empty()); 991 992 // Observer shouldn't be notified even after a |group()| call. 993 EXPECT_EQ(default_group, trial->group()); 994 RunLoop().RunUntilIdle(); 995 EXPECT_TRUE(observer.trial_name().empty()); 996 EXPECT_TRUE(observer.group_name().empty()); 997 } 998 999 TEST_F(FieldTrialTest, ObserveForcedDisabled) { 1000 const char kTrialName[] = "TrialToObserve3"; 1001 1002 TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS); 1003 int default_group = -1; 1004 scoped_refptr<FieldTrial> trial = 1005 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, &default_group); 1006 trial->AppendGroup("A", 25); 1007 trial->AppendGroup("B", 25); 1008 trial->AppendGroup("C", 25); 1009 trial->SetForced(); 1010 trial->Disable(); 1011 1012 // Observer shouldn't be notified of a disabled trial, even when forced. 1013 RunLoop().RunUntilIdle(); 1014 EXPECT_TRUE(observer.trial_name().empty()); 1015 EXPECT_TRUE(observer.group_name().empty()); 1016 1017 // Observer shouldn't be notified even after a |group()| call. 1018 EXPECT_EQ(default_group, trial->group()); 1019 RunLoop().RunUntilIdle(); 1020 EXPECT_TRUE(observer.trial_name().empty()); 1021 EXPECT_TRUE(observer.group_name().empty()); 1022 } 1023 1024 TEST_F(FieldTrialTest, DisabledTrialNotActive) { 1025 const char kTrialName[] = "DisabledTrial"; 1026 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); 1027 1028 scoped_refptr<FieldTrial> trial = 1029 CreateFieldTrial(kTrialName, 100, kDefaultGroupName, nullptr); 1030 trial->AppendGroup("X", 50); 1031 trial->Disable(); 1032 1033 // Ensure the trial is not listed as active. 1034 FieldTrial::ActiveGroups active_groups; 1035 FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 1036 EXPECT_TRUE(active_groups.empty()); 1037 1038 // Ensure the trial is not listed in the |StatesToString()| result. 1039 std::string states; 1040 FieldTrialList::StatesToString(&states); 1041 EXPECT_TRUE(states.empty()); 1042 } 1043 1044 TEST_F(FieldTrialTest, ExpirationYearNotExpired) { 1045 const char kTrialName[] = "NotExpired"; 1046 const char kGroupName[] = "Group2"; 1047 const int kProbability = 100; 1048 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); 1049 1050 scoped_refptr<FieldTrial> trial = 1051 CreateFieldTrial(kTrialName, kProbability, kDefaultGroupName, nullptr); 1052 trial->AppendGroup(kGroupName, kProbability); 1053 EXPECT_EQ(kGroupName, trial->group_name()); 1054 } 1055 1056 TEST_F(FieldTrialTest, FloatBoundariesGiveEqualGroupSizes) { 1057 const int kBucketCount = 100; 1058 1059 // Try each boundary value |i / 100.0| as the entropy value. 1060 for (int i = 0; i < kBucketCount; ++i) { 1061 const double entropy = i / static_cast<double>(kBucketCount); 1062 1063 scoped_refptr<FieldTrial> trial( 1064 new FieldTrial("test", kBucketCount, "default", entropy)); 1065 for (int j = 0; j < kBucketCount; ++j) 1066 trial->AppendGroup(IntToString(j), 1); 1067 1068 EXPECT_EQ(IntToString(i), trial->group_name()); 1069 } 1070 } 1071 1072 TEST_F(FieldTrialTest, DoesNotSurpassTotalProbability) { 1073 const double kEntropyValue = 1.0 - 1e-9; 1074 ASSERT_LT(kEntropyValue, 1.0); 1075 1076 scoped_refptr<FieldTrial> trial( 1077 new FieldTrial("test", 2, "default", kEntropyValue)); 1078 trial->AppendGroup("1", 1); 1079 trial->AppendGroup("2", 1); 1080 1081 EXPECT_EQ("2", trial->group_name()); 1082 } 1083 1084 TEST_F(FieldTrialTest, CreateSimulatedFieldTrial) { 1085 const char kTrialName[] = "CreateSimulatedFieldTrial"; 1086 ASSERT_FALSE(FieldTrialList::TrialExists(kTrialName)); 1087 1088 // Different cases to test, e.g. default vs. non default group being chosen. 1089 struct { 1090 double entropy_value; 1091 const char* expected_group; 1092 } test_cases[] = { 1093 { 0.4, "A" }, 1094 { 0.85, "B" }, 1095 { 0.95, kDefaultGroupName }, 1096 }; 1097 1098 for (size_t i = 0; i < arraysize(test_cases); ++i) { 1099 TestFieldTrialObserver observer(TestFieldTrialObserver::ASYNCHRONOUS); 1100 scoped_refptr<FieldTrial> trial( 1101 FieldTrial::CreateSimulatedFieldTrial(kTrialName, 100, kDefaultGroupName, 1102 test_cases[i].entropy_value)); 1103 trial->AppendGroup("A", 80); 1104 trial->AppendGroup("B", 10); 1105 EXPECT_EQ(test_cases[i].expected_group, trial->group_name()); 1106 1107 // Field trial shouldn't have been registered with the list. 1108 EXPECT_FALSE(FieldTrialList::TrialExists(kTrialName)); 1109 EXPECT_EQ(0u, FieldTrialList::GetFieldTrialCount()); 1110 1111 // Observer shouldn't have been notified. 1112 RunLoop().RunUntilIdle(); 1113 EXPECT_TRUE(observer.trial_name().empty()); 1114 1115 // The trial shouldn't be in the active set of trials. 1116 FieldTrial::ActiveGroups active_groups; 1117 FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 1118 EXPECT_TRUE(active_groups.empty()); 1119 1120 // The trial shouldn't be listed in the |StatesToString()| result. 1121 std::string states; 1122 FieldTrialList::StatesToString(&states); 1123 EXPECT_TRUE(states.empty()); 1124 } 1125 } 1126 1127 TEST(FieldTrialTestWithoutList, StatesStringFormat) { 1128 std::string save_string; 1129 1130 // Scoping the first FieldTrialList, as we need another one to test the 1131 // importing function. 1132 { 1133 FieldTrialList field_trial_list(nullptr); 1134 scoped_refptr<FieldTrial> trial = 1135 CreateFieldTrial("Abc", 10, "Default some name", nullptr); 1136 trial->AppendGroup("cba", 10); 1137 trial->group(); 1138 scoped_refptr<FieldTrial> trial2 = 1139 CreateFieldTrial("Xyz", 10, "Default xxx", nullptr); 1140 trial2->AppendGroup("zyx", 10); 1141 trial2->group(); 1142 scoped_refptr<FieldTrial> trial3 = 1143 CreateFieldTrial("zzz", 10, "default", nullptr); 1144 1145 FieldTrialList::AllStatesToString(&save_string, false); 1146 } 1147 1148 // Starting with a new blank FieldTrialList. 1149 FieldTrialList field_trial_list(nullptr); 1150 ASSERT_TRUE(field_trial_list.CreateTrialsFromString(save_string, 1151 std::set<std::string>())); 1152 1153 FieldTrial::ActiveGroups active_groups; 1154 field_trial_list.GetActiveFieldTrialGroups(&active_groups); 1155 ASSERT_EQ(2U, active_groups.size()); 1156 EXPECT_EQ("Abc", active_groups[0].trial_name); 1157 EXPECT_EQ("cba", active_groups[0].group_name); 1158 EXPECT_EQ("Xyz", active_groups[1].trial_name); 1159 EXPECT_EQ("zyx", active_groups[1].group_name); 1160 EXPECT_TRUE(field_trial_list.TrialExists("zzz")); 1161 } 1162 1163 TEST(FieldTrialDeathTest, OneTimeRandomizedTrialWithoutFieldTrialList) { 1164 // Trying to instantiate a one-time randomized field trial before the 1165 // FieldTrialList is created should crash. 1166 EXPECT_DEATH_IF_SUPPORTED( 1167 FieldTrialList::FactoryGetFieldTrial( 1168 "OneTimeRandomizedTrialWithoutFieldTrialList", 100, kDefaultGroupName, 1169 FieldTrialList::kNoExpirationYear, 1, 1, 1170 FieldTrial::ONE_TIME_RANDOMIZED, nullptr), 1171 ""); 1172 } 1173 1174 #if defined(OS_FUCHSIA) 1175 // TODO(crbug.com/752368): This is flaky on Fuchsia. 1176 #define MAYBE_TestCopyFieldTrialStateToFlags \ 1177 DISABLED_TestCopyFieldTrialStateToFlags 1178 #else 1179 #define MAYBE_TestCopyFieldTrialStateToFlags TestCopyFieldTrialStateToFlags 1180 #endif 1181 TEST(FieldTrialListTest, MAYBE_TestCopyFieldTrialStateToFlags) { 1182 constexpr char kFieldTrialHandleSwitch[] = "test-field-trial-handle"; 1183 constexpr char kEnableFeaturesSwitch[] = "test-enable-features"; 1184 constexpr char kDisableFeaturesSwitch[] = "test-disable-features"; 1185 1186 FieldTrialList field_trial_list(std::make_unique<MockEntropyProvider>()); 1187 1188 std::unique_ptr<FeatureList> feature_list(new FeatureList); 1189 feature_list->InitializeFromCommandLine("A,B", "C"); 1190 1191 FieldTrial* trial = FieldTrialList::CreateFieldTrial("Trial1", "Group1"); 1192 feature_list->RegisterFieldTrialOverride( 1193 "MyFeature", FeatureList::OVERRIDE_ENABLE_FEATURE, trial); 1194 1195 test::ScopedFeatureList scoped_feature_list; 1196 scoped_feature_list.InitWithFeatureList(std::move(feature_list)); 1197 1198 FilePath test_file_path = FilePath(FILE_PATH_LITERAL("Program")); 1199 CommandLine command_line = CommandLine(test_file_path); 1200 1201 FieldTrialList::CopyFieldTrialStateToFlags( 1202 kFieldTrialHandleSwitch, kEnableFeaturesSwitch, kDisableFeaturesSwitch, 1203 &command_line); 1204 EXPECT_TRUE(command_line.HasSwitch(kFieldTrialHandleSwitch)); 1205 1206 // Explictly specified enabled/disabled features should be specified. 1207 EXPECT_EQ("A,B", command_line.GetSwitchValueASCII(kEnableFeaturesSwitch)); 1208 EXPECT_EQ("C", command_line.GetSwitchValueASCII(kDisableFeaturesSwitch)); 1209 } 1210 1211 TEST(FieldTrialListTest, InstantiateAllocator) { 1212 test::ScopedFeatureList scoped_feature_list; 1213 scoped_feature_list.Init(); 1214 1215 FieldTrialList field_trial_list(nullptr); 1216 FieldTrialList::CreateFieldTrial("Trial1", "Group1"); 1217 1218 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); 1219 void* memory = field_trial_list.field_trial_allocator_->shared_memory(); 1220 size_t used = field_trial_list.field_trial_allocator_->used(); 1221 1222 // Ensure that the function is idempotent. 1223 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); 1224 void* new_memory = field_trial_list.field_trial_allocator_->shared_memory(); 1225 size_t new_used = field_trial_list.field_trial_allocator_->used(); 1226 EXPECT_EQ(memory, new_memory); 1227 EXPECT_EQ(used, new_used); 1228 } 1229 1230 TEST(FieldTrialListTest, AddTrialsToAllocator) { 1231 std::string save_string; 1232 SharedMemoryHandle handle; 1233 1234 // Scoping the first FieldTrialList, as we need another one to test that it 1235 // matches. 1236 { 1237 test::ScopedFeatureList scoped_feature_list; 1238 scoped_feature_list.Init(); 1239 1240 FieldTrialList field_trial_list(nullptr); 1241 FieldTrialList::CreateFieldTrial("Trial1", "Group1"); 1242 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); 1243 FieldTrialList::AllStatesToString(&save_string, false); 1244 handle = SharedMemory::DuplicateHandle( 1245 field_trial_list.field_trial_allocator_->shared_memory()->handle()); 1246 } 1247 1248 FieldTrialList field_trial_list2(nullptr); 1249 std::unique_ptr<SharedMemory> shm(new SharedMemory(handle, true)); 1250 // 4 KiB is enough to hold the trials only created for this test. 1251 shm.get()->Map(4 << 10); 1252 FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); 1253 std::string check_string; 1254 FieldTrialList::AllStatesToString(&check_string, false); 1255 EXPECT_EQ(save_string, check_string); 1256 } 1257 1258 TEST(FieldTrialListTest, DoNotAddSimulatedFieldTrialsToAllocator) { 1259 constexpr char kTrialName[] = "trial"; 1260 SharedMemoryHandle handle; 1261 { 1262 test::ScopedFeatureList scoped_feature_list; 1263 scoped_feature_list.Init(); 1264 1265 // Create a simulated trial and a real trial and call group() on them, which 1266 // should only add the real trial to the field trial allocator. 1267 FieldTrialList field_trial_list(nullptr); 1268 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); 1269 1270 // This shouldn't add to the allocator. 1271 scoped_refptr<FieldTrial> simulated_trial = 1272 FieldTrial::CreateSimulatedFieldTrial(kTrialName, 100, "Simulated", 1273 0.95); 1274 simulated_trial->group(); 1275 1276 // This should add to the allocator. 1277 FieldTrial* real_trial = 1278 FieldTrialList::CreateFieldTrial(kTrialName, "Real"); 1279 real_trial->group(); 1280 1281 handle = SharedMemory::DuplicateHandle( 1282 field_trial_list.field_trial_allocator_->shared_memory()->handle()); 1283 } 1284 1285 // Check that there's only one entry in the allocator. 1286 FieldTrialList field_trial_list2(nullptr); 1287 std::unique_ptr<SharedMemory> shm(new SharedMemory(handle, true)); 1288 // 4 KiB is enough to hold the trials only created for this test. 1289 shm.get()->Map(4 << 10); 1290 FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); 1291 std::string check_string; 1292 FieldTrialList::AllStatesToString(&check_string, false); 1293 ASSERT_EQ(check_string.find("Simulated"), std::string::npos); 1294 } 1295 1296 TEST(FieldTrialListTest, AssociateFieldTrialParams) { 1297 test::ScopedFeatureList scoped_feature_list; 1298 scoped_feature_list.Init(); 1299 1300 std::string trial_name("Trial1"); 1301 std::string group_name("Group1"); 1302 1303 // Create a field trial with some params. 1304 FieldTrialList field_trial_list(nullptr); 1305 FieldTrialList::CreateFieldTrial(trial_name, group_name); 1306 std::map<std::string, std::string> params; 1307 params["key1"] = "value1"; 1308 params["key2"] = "value2"; 1309 FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( 1310 trial_name, group_name, params); 1311 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); 1312 1313 // Clear all cached params from the associator. 1314 FieldTrialParamAssociator::GetInstance()->ClearAllCachedParamsForTesting(); 1315 // Check that the params have been cleared from the cache. 1316 std::map<std::string, std::string> cached_params; 1317 FieldTrialParamAssociator::GetInstance()->GetFieldTrialParamsWithoutFallback( 1318 trial_name, group_name, &cached_params); 1319 EXPECT_EQ(0U, cached_params.size()); 1320 1321 // Check that we fetch the param from shared memory properly. 1322 std::map<std::string, std::string> new_params; 1323 FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial_name, 1324 &new_params); 1325 EXPECT_EQ("value1", new_params["key1"]); 1326 EXPECT_EQ("value2", new_params["key2"]); 1327 EXPECT_EQ(2U, new_params.size()); 1328 } 1329 1330 #if defined(OS_FUCHSIA) 1331 // TODO(crbug.com/752368): This is flaky on Fuchsia. 1332 #define MAYBE_ClearParamsFromSharedMemory DISABLED_ClearParamsFromSharedMemory 1333 #else 1334 #define MAYBE_ClearParamsFromSharedMemory ClearParamsFromSharedMemory 1335 #endif 1336 TEST(FieldTrialListTest, MAYBE_ClearParamsFromSharedMemory) { 1337 std::string trial_name("Trial1"); 1338 std::string group_name("Group1"); 1339 1340 SharedMemoryHandle handle; 1341 { 1342 test::ScopedFeatureList scoped_feature_list; 1343 scoped_feature_list.Init(); 1344 1345 // Create a field trial with some params. 1346 FieldTrialList field_trial_list(nullptr); 1347 FieldTrial* trial = 1348 FieldTrialList::CreateFieldTrial(trial_name, group_name); 1349 std::map<std::string, std::string> params; 1350 params["key1"] = "value1"; 1351 params["key2"] = "value2"; 1352 FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( 1353 trial_name, group_name, params); 1354 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); 1355 1356 // Clear all params from the associator AND shared memory. The allocated 1357 // segments should be different. 1358 FieldTrial::FieldTrialRef old_ref = trial->ref_; 1359 FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting(); 1360 FieldTrial::FieldTrialRef new_ref = trial->ref_; 1361 EXPECT_NE(old_ref, new_ref); 1362 1363 // Check that there are no params associated with the field trial anymore. 1364 std::map<std::string, std::string> new_params; 1365 FieldTrialParamAssociator::GetInstance()->GetFieldTrialParams(trial_name, 1366 &new_params); 1367 EXPECT_EQ(0U, new_params.size()); 1368 1369 // Now duplicate the handle so we can easily check that the trial is still 1370 // in shared memory via AllStatesToString. 1371 handle = SharedMemory::DuplicateHandle( 1372 field_trial_list.field_trial_allocator_->shared_memory()->handle()); 1373 } 1374 1375 // Check that we have the trial. 1376 FieldTrialList field_trial_list2(nullptr); 1377 std::unique_ptr<SharedMemory> shm(new SharedMemory(handle, true)); 1378 // 4 KiB is enough to hold the trials only created for this test. 1379 shm.get()->Map(4 << 10); 1380 FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); 1381 std::string check_string; 1382 FieldTrialList::AllStatesToString(&check_string, false); 1383 EXPECT_EQ("*Trial1/Group1/", check_string); 1384 } 1385 1386 TEST(FieldTrialListTest, DumpAndFetchFromSharedMemory) { 1387 std::string trial_name("Trial1"); 1388 std::string group_name("Group1"); 1389 1390 // Create a field trial with some params. 1391 FieldTrialList field_trial_list(nullptr); 1392 FieldTrialList::CreateFieldTrial(trial_name, group_name); 1393 std::map<std::string, std::string> params; 1394 params["key1"] = "value1"; 1395 params["key2"] = "value2"; 1396 FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( 1397 trial_name, group_name, params); 1398 1399 std::unique_ptr<SharedMemory> shm(new SharedMemory()); 1400 // 4 KiB is enough to hold the trials only created for this test. 1401 shm.get()->CreateAndMapAnonymous(4 << 10); 1402 // We _could_ use PersistentMemoryAllocator, this just has less params. 1403 SharedPersistentMemoryAllocator allocator(std::move(shm), 1, "", false); 1404 1405 // Dump and subsequently retrieve the field trial to |allocator|. 1406 FieldTrialList::DumpAllFieldTrialsToPersistentAllocator(&allocator); 1407 std::vector<const FieldTrial::FieldTrialEntry*> entries = 1408 FieldTrialList::GetAllFieldTrialsFromPersistentAllocator(allocator); 1409 1410 // Check that we have the entry we put in. 1411 EXPECT_EQ(1u, entries.size()); 1412 const FieldTrial::FieldTrialEntry* entry = entries[0]; 1413 1414 // Check that the trial and group names match. 1415 StringPiece shm_trial_name; 1416 StringPiece shm_group_name; 1417 entry->GetTrialAndGroupName(&shm_trial_name, &shm_group_name); 1418 EXPECT_EQ(trial_name, shm_trial_name); 1419 EXPECT_EQ(group_name, shm_group_name); 1420 1421 // Check that the params match. 1422 std::map<std::string, std::string> shm_params; 1423 entry->GetParams(&shm_params); 1424 EXPECT_EQ(2u, shm_params.size()); 1425 EXPECT_EQ("value1", shm_params["key1"]); 1426 EXPECT_EQ("value2", shm_params["key2"]); 1427 } 1428 1429 #if !defined(OS_NACL) 1430 TEST(FieldTrialListTest, SerializeSharedMemoryHandleMetadata) { 1431 std::unique_ptr<SharedMemory> shm(new SharedMemory()); 1432 shm->CreateAndMapAnonymous(4 << 10); 1433 1434 std::string serialized = 1435 FieldTrialList::SerializeSharedMemoryHandleMetadata(shm->handle()); 1436 #if defined(OS_WIN) || defined(OS_FUCHSIA) 1437 SharedMemoryHandle deserialized = 1438 FieldTrialList::DeserializeSharedMemoryHandleMetadata(serialized); 1439 #else 1440 // Use a valid-looking arbitrary number for the file descriptor. It's not 1441 // being used in this unittest, but needs to pass sanity checks in the 1442 // handle's constructor. 1443 SharedMemoryHandle deserialized = 1444 FieldTrialList::DeserializeSharedMemoryHandleMetadata(42, serialized); 1445 #endif 1446 EXPECT_EQ(deserialized.GetGUID(), shm->handle().GetGUID()); 1447 EXPECT_FALSE(deserialized.GetGUID().is_empty()); 1448 } 1449 #endif // !defined(OS_NACL) 1450 1451 // Verify that the field trial shared memory handle is really read-only, and 1452 // does not allow writable mappings. Test disabled on NaCl, Windows and Fuchsia 1453 // which don't support/implement GetFieldTrialHandle(). For Fuchsia, see 1454 // crbug.com/752368 1455 #if !defined(OS_NACL) && !defined(OS_WIN) && !defined(OS_FUCHSIA) 1456 TEST(FieldTrialListTest, CheckReadOnlySharedMemoryHandle) { 1457 FieldTrialList field_trial_list(nullptr); 1458 FieldTrialList::CreateFieldTrial("Trial1", "Group1"); 1459 1460 test::ScopedFeatureList scoped_feature_list; 1461 scoped_feature_list.Init(); 1462 1463 FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); 1464 1465 SharedMemoryHandle handle = FieldTrialList::GetFieldTrialHandle(); 1466 ASSERT_TRUE(handle.IsValid()); 1467 1468 ASSERT_TRUE(CheckReadOnlySharedMemoryHandleForTesting(handle)); 1469 } 1470 #endif // !OS_NACL && !OS_WIN && !OS_FUCHSIA 1471 1472 TEST_F(FieldTrialTest, TestAllParamsToString) { 1473 std::string exptected_output = "t1.g1:p1/v1/p2/v2"; 1474 1475 // Create study with one group and two params. 1476 std::map<std::string, std::string> params; 1477 params["p1"] = "v1"; 1478 params["p2"] = "v2"; 1479 FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( 1480 "t1", "g1", params); 1481 EXPECT_EQ( 1482 "", FieldTrialList::AllParamsToString(false, &MockEscapeQueryParamValue)); 1483 1484 scoped_refptr<FieldTrial> trial1 = 1485 CreateFieldTrial("t1", 100, "Default", nullptr); 1486 trial1->AppendGroup("g1", 100); 1487 trial1->group(); 1488 EXPECT_EQ(exptected_output, FieldTrialList::AllParamsToString( 1489 false, &MockEscapeQueryParamValue)); 1490 1491 // Create study with two groups and params that don't belog to the assigned 1492 // group. This should be in the output. 1493 FieldTrialParamAssociator::GetInstance()->AssociateFieldTrialParams( 1494 "t2", "g2", params); 1495 scoped_refptr<FieldTrial> trial2 = 1496 CreateFieldTrial("t2", 100, "Default", nullptr); 1497 trial2->AppendGroup("g1", 100); 1498 trial2->AppendGroup("g2", 0); 1499 trial2->group(); 1500 EXPECT_EQ(exptected_output, FieldTrialList::AllParamsToString( 1501 false, &MockEscapeQueryParamValue)); 1502 } 1503 1504 } // namespace base 1505