1 // Copyright 2013 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 "components/variations/variations_seed_processor.h" 6 7 #include <vector> 8 9 #include "base/command_line.h" 10 #include "base/strings/string_split.h" 11 #include "components/variations/processed_study.h" 12 #include "components/variations/variations_associated_data.h" 13 #include "testing/gtest/include/gtest/gtest.h" 14 15 namespace chrome_variations { 16 17 namespace { 18 19 // Converts |time| to Study proto format. 20 int64 TimeToProtoTime(const base::Time& time) { 21 return (time - base::Time::UnixEpoch()).InSeconds(); 22 } 23 24 // Constants for testing associating command line flags with trial groups. 25 const char kFlagStudyName[] = "flag_test_trial"; 26 const char kFlagGroup1Name[] = "flag_group1"; 27 const char kFlagGroup2Name[] = "flag_group2"; 28 const char kNonFlagGroupName[] = "non_flag_group"; 29 const char kForcingFlag1[] = "flag_test1"; 30 const char kForcingFlag2[] = "flag_test2"; 31 32 const VariationID kExperimentId = 123; 33 34 // Adds an experiment to |study| with the specified |name| and |probability|. 35 Study_Experiment* AddExperiment(const std::string& name, int probability, 36 Study* study) { 37 Study_Experiment* experiment = study->add_experiment(); 38 experiment->set_name(name); 39 experiment->set_probability_weight(probability); 40 return experiment; 41 } 42 43 // Populates |study| with test data used for testing associating command line 44 // flags with trials groups. The study will contain three groups, a default 45 // group that isn't associated with a flag, and two other groups, both 46 // associated with different flags. 47 Study CreateStudyWithFlagGroups(int default_group_probability, 48 int flag_group1_probability, 49 int flag_group2_probability) { 50 DCHECK_GE(default_group_probability, 0); 51 DCHECK_GE(flag_group1_probability, 0); 52 DCHECK_GE(flag_group2_probability, 0); 53 Study study; 54 study.set_name(kFlagStudyName); 55 study.set_default_experiment_name(kNonFlagGroupName); 56 57 AddExperiment(kNonFlagGroupName, default_group_probability, &study); 58 AddExperiment(kFlagGroup1Name, flag_group1_probability, &study) 59 ->set_forcing_flag(kForcingFlag1); 60 AddExperiment(kFlagGroup2Name, flag_group2_probability, &study) 61 ->set_forcing_flag(kForcingFlag2); 62 63 return study; 64 } 65 66 // Tests whether a field trial is active (i.e. group() has been called on it). 67 bool IsFieldTrialActive(const std::string& trial_name) { 68 base::FieldTrial::ActiveGroups active_groups; 69 base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 70 for (size_t i = 0; i < active_groups.size(); ++i) { 71 if (active_groups[i].trial_name == trial_name) 72 return true; 73 } 74 return false; 75 } 76 77 } // namespace 78 79 class VariationsSeedProcessorTest : public ::testing::Test { 80 public: 81 VariationsSeedProcessorTest() { 82 } 83 84 virtual ~VariationsSeedProcessorTest() { 85 // Ensure that the maps are cleared between tests, since they are stored as 86 // process singletons. 87 testing::ClearAllVariationIDs(); 88 testing::ClearAllVariationParams(); 89 } 90 91 bool CreateTrialFromStudy(const Study* study) { 92 ProcessedStudy processed_study; 93 if (processed_study.Init(study, false)) { 94 VariationsSeedProcessor().CreateTrialFromStudy(processed_study); 95 return true; 96 } 97 return false; 98 } 99 100 private: 101 DISALLOW_COPY_AND_ASSIGN(VariationsSeedProcessorTest); 102 }; 103 104 TEST_F(VariationsSeedProcessorTest, AllowForceGroupAndVariationId) { 105 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1); 106 107 base::FieldTrialList field_trial_list(NULL); 108 109 Study study = CreateStudyWithFlagGroups(100, 0, 0); 110 study.mutable_experiment(1)->set_google_web_experiment_id(kExperimentId); 111 study.mutable_filter()->add_channel(Study_Channel_DEV); 112 study.mutable_filter()->add_channel(Study_Channel_CANARY); 113 study.mutable_filter()->add_platform(Study_Platform_PLATFORM_ANDROID); 114 115 EXPECT_TRUE(CreateTrialFromStudy(&study)); 116 EXPECT_EQ(kFlagGroup1Name, 117 base::FieldTrialList::FindFullName(kFlagStudyName)); 118 119 VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName, 120 kFlagGroup1Name); 121 EXPECT_EQ(kExperimentId, id); 122 } 123 124 TEST_F(VariationsSeedProcessorTest, AllowVariationIdWithForcingFlag) { 125 VariationsSeedProcessor seed_processor; 126 Study study = CreateStudyWithFlagGroups(100, 0, 0); 127 EXPECT_FALSE(seed_processor.AllowVariationIdWithForcingFlag(study)); 128 129 study.mutable_filter()->add_channel(Study_Channel_DEV); 130 EXPECT_FALSE(seed_processor.AllowVariationIdWithForcingFlag(study)); 131 132 study.mutable_filter()->add_platform(Study_Platform_PLATFORM_ANDROID); 133 EXPECT_TRUE(seed_processor.AllowVariationIdWithForcingFlag(study)); 134 135 study.mutable_filter()->add_channel(Study_Channel_CANARY); 136 study.mutable_filter()->add_platform(Study_Platform_PLATFORM_IOS); 137 EXPECT_TRUE(seed_processor.AllowVariationIdWithForcingFlag(study)); 138 139 study.mutable_filter()->add_platform(Study_Platform_PLATFORM_WINDOWS); 140 EXPECT_FALSE(seed_processor.AllowVariationIdWithForcingFlag(study)); 141 } 142 143 TEST_F(VariationsSeedProcessorTest, CheckStudyChannel) { 144 VariationsSeedProcessor seed_processor; 145 146 const Study_Channel channels[] = { 147 Study_Channel_CANARY, 148 Study_Channel_DEV, 149 Study_Channel_BETA, 150 Study_Channel_STABLE, 151 }; 152 bool channel_added[arraysize(channels)] = { 0 }; 153 154 Study_Filter filter; 155 156 // Check in the forwarded order. The loop cond is <= arraysize(channels) 157 // instead of < so that the result of adding the last channel gets checked. 158 for (size_t i = 0; i <= arraysize(channels); ++i) { 159 for (size_t j = 0; j < arraysize(channels); ++j) { 160 const bool expected = channel_added[j] || filter.channel_size() == 0; 161 const bool result = seed_processor.CheckStudyChannel(filter, channels[j]); 162 EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!"; 163 } 164 165 if (i < arraysize(channels)) { 166 filter.add_channel(channels[i]); 167 channel_added[i] = true; 168 } 169 } 170 171 // Do the same check in the reverse order. 172 filter.clear_channel(); 173 memset(&channel_added, 0, sizeof(channel_added)); 174 for (size_t i = 0; i <= arraysize(channels); ++i) { 175 for (size_t j = 0; j < arraysize(channels); ++j) { 176 const bool expected = channel_added[j] || filter.channel_size() == 0; 177 const bool result = seed_processor.CheckStudyChannel(filter, channels[j]); 178 EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!"; 179 } 180 181 if (i < arraysize(channels)) { 182 const int index = arraysize(channels) - i - 1; 183 filter.add_channel(channels[index]); 184 channel_added[index] = true; 185 } 186 } 187 } 188 189 TEST_F(VariationsSeedProcessorTest, CheckStudyFormFactor) { 190 VariationsSeedProcessor seed_processor; 191 192 const Study_FormFactor form_factors[] = { 193 Study_FormFactor_DESKTOP, 194 Study_FormFactor_PHONE, 195 Study_FormFactor_TABLET, 196 }; 197 198 ASSERT_EQ(Study_FormFactor_FormFactor_ARRAYSIZE, 199 static_cast<int>(arraysize(form_factors))); 200 201 bool form_factor_added[arraysize(form_factors)] = { 0 }; 202 Study_Filter filter; 203 204 for (size_t i = 0; i <= arraysize(form_factors); ++i) { 205 for (size_t j = 0; j < arraysize(form_factors); ++j) { 206 const bool expected = form_factor_added[j] || 207 filter.form_factor_size() == 0; 208 const bool result = seed_processor.CheckStudyFormFactor(filter, 209 form_factors[j]); 210 EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!"; 211 } 212 213 if (i < arraysize(form_factors)) { 214 filter.add_form_factor(form_factors[i]); 215 form_factor_added[i] = true; 216 } 217 } 218 219 // Do the same check in the reverse order. 220 filter.clear_form_factor(); 221 memset(&form_factor_added, 0, sizeof(form_factor_added)); 222 for (size_t i = 0; i <= arraysize(form_factors); ++i) { 223 for (size_t j = 0; j < arraysize(form_factors); ++j) { 224 const bool expected = form_factor_added[j] || 225 filter.form_factor_size() == 0; 226 const bool result = seed_processor.CheckStudyFormFactor(filter, 227 form_factors[j]); 228 EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!"; 229 } 230 231 if (i < arraysize(form_factors)) { 232 const int index = arraysize(form_factors) - i - 1;; 233 filter.add_form_factor(form_factors[index]); 234 form_factor_added[index] = true; 235 } 236 } 237 } 238 239 TEST_F(VariationsSeedProcessorTest, CheckStudyLocale) { 240 VariationsSeedProcessor seed_processor; 241 242 struct { 243 const char* filter_locales; 244 bool en_us_result; 245 bool en_ca_result; 246 bool fr_result; 247 } test_cases[] = { 248 {"en-US", true, false, false}, 249 {"en-US,en-CA,fr", true, true, true}, 250 {"en-US,en-CA,en-GB", true, true, false}, 251 {"en-GB,en-CA,en-US", true, true, false}, 252 {"ja,kr,vi", false, false, false}, 253 {"fr-CA", false, false, false}, 254 {"", true, true, true}, 255 }; 256 257 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(test_cases); ++i) { 258 std::vector<std::string> filter_locales; 259 Study_Filter filter; 260 base::SplitString(test_cases[i].filter_locales, ',', &filter_locales); 261 for (size_t j = 0; j < filter_locales.size(); ++j) 262 filter.add_locale(filter_locales[j]); 263 EXPECT_EQ(test_cases[i].en_us_result, 264 seed_processor.CheckStudyLocale(filter, "en-US")); 265 EXPECT_EQ(test_cases[i].en_ca_result, 266 seed_processor.CheckStudyLocale(filter, "en-CA")); 267 EXPECT_EQ(test_cases[i].fr_result, 268 seed_processor.CheckStudyLocale(filter, "fr")); 269 } 270 } 271 272 TEST_F(VariationsSeedProcessorTest, CheckStudyPlatform) { 273 VariationsSeedProcessor seed_processor; 274 275 const Study_Platform platforms[] = { 276 Study_Platform_PLATFORM_WINDOWS, 277 Study_Platform_PLATFORM_MAC, 278 Study_Platform_PLATFORM_LINUX, 279 Study_Platform_PLATFORM_CHROMEOS, 280 Study_Platform_PLATFORM_ANDROID, 281 Study_Platform_PLATFORM_IOS, 282 }; 283 ASSERT_EQ(Study_Platform_Platform_ARRAYSIZE, 284 static_cast<int>(arraysize(platforms))); 285 bool platform_added[arraysize(platforms)] = { 0 }; 286 287 Study_Filter filter; 288 289 // Check in the forwarded order. The loop cond is <= arraysize(platforms) 290 // instead of < so that the result of adding the last channel gets checked. 291 for (size_t i = 0; i <= arraysize(platforms); ++i) { 292 for (size_t j = 0; j < arraysize(platforms); ++j) { 293 const bool expected = platform_added[j] || filter.platform_size() == 0; 294 const bool result = seed_processor.CheckStudyPlatform(filter, 295 platforms[j]); 296 EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!"; 297 } 298 299 if (i < arraysize(platforms)) { 300 filter.add_platform(platforms[i]); 301 platform_added[i] = true; 302 } 303 } 304 305 // Do the same check in the reverse order. 306 filter.clear_platform(); 307 memset(&platform_added, 0, sizeof(platform_added)); 308 for (size_t i = 0; i <= arraysize(platforms); ++i) { 309 for (size_t j = 0; j < arraysize(platforms); ++j) { 310 const bool expected = platform_added[j] || filter.platform_size() == 0; 311 const bool result = seed_processor.CheckStudyPlatform(filter, 312 platforms[j]); 313 EXPECT_EQ(expected, result) << "Case " << i << "," << j << " failed!"; 314 } 315 316 if (i < arraysize(platforms)) { 317 const int index = arraysize(platforms) - i - 1; 318 filter.add_platform(platforms[index]); 319 platform_added[index] = true; 320 } 321 } 322 } 323 324 TEST_F(VariationsSeedProcessorTest, CheckStudyStartDate) { 325 VariationsSeedProcessor seed_processor; 326 327 const base::Time now = base::Time::Now(); 328 const base::TimeDelta delta = base::TimeDelta::FromHours(1); 329 const struct { 330 const base::Time start_date; 331 bool expected_result; 332 } start_test_cases[] = { 333 { now - delta, true }, 334 { now, true }, 335 { now + delta, false }, 336 }; 337 338 Study_Filter filter; 339 340 // Start date not set should result in true. 341 EXPECT_TRUE(seed_processor.CheckStudyStartDate(filter, now)); 342 343 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(start_test_cases); ++i) { 344 filter.set_start_date(TimeToProtoTime(start_test_cases[i].start_date)); 345 const bool result = seed_processor.CheckStudyStartDate(filter, now); 346 EXPECT_EQ(start_test_cases[i].expected_result, result) 347 << "Case " << i << " failed!"; 348 } 349 } 350 351 TEST_F(VariationsSeedProcessorTest, CheckStudyVersion) { 352 VariationsSeedProcessor seed_processor; 353 354 const struct { 355 const char* min_version; 356 const char* version; 357 bool expected_result; 358 } min_test_cases[] = { 359 { "1.2.2", "1.2.3", true }, 360 { "1.2.3", "1.2.3", true }, 361 { "1.2.4", "1.2.3", false }, 362 { "1.3.2", "1.2.3", false }, 363 { "2.1.2", "1.2.3", false }, 364 { "0.3.4", "1.2.3", true }, 365 // Wildcards. 366 { "1.*", "1.2.3", true }, 367 { "1.2.*", "1.2.3", true }, 368 { "1.2.3.*", "1.2.3", true }, 369 { "1.2.4.*", "1.2.3", false }, 370 { "2.*", "1.2.3", false }, 371 { "0.3.*", "1.2.3", true }, 372 }; 373 374 const struct { 375 const char* max_version; 376 const char* version; 377 bool expected_result; 378 } max_test_cases[] = { 379 { "1.2.2", "1.2.3", false }, 380 { "1.2.3", "1.2.3", true }, 381 { "1.2.4", "1.2.3", true }, 382 { "2.1.1", "1.2.3", true }, 383 { "2.1.1", "2.3.4", false }, 384 // Wildcards 385 { "2.1.*", "2.3.4", false }, 386 { "2.*", "2.3.4", true }, 387 { "2.3.*", "2.3.4", true }, 388 { "2.3.4.*", "2.3.4", true }, 389 { "2.3.4.0.*", "2.3.4", true }, 390 { "2.4.*", "2.3.4", true }, 391 { "1.3.*", "2.3.4", false }, 392 { "1.*", "2.3.4", false }, 393 }; 394 395 Study_Filter filter; 396 397 // Min/max version not set should result in true. 398 EXPECT_TRUE(seed_processor.CheckStudyVersion(filter, base::Version("1.2.3"))); 399 400 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) { 401 filter.set_min_version(min_test_cases[i].min_version); 402 const bool result = 403 seed_processor.CheckStudyVersion(filter, 404 Version(min_test_cases[i].version)); 405 EXPECT_EQ(min_test_cases[i].expected_result, result) << 406 "Min. version case " << i << " failed!"; 407 } 408 filter.clear_min_version(); 409 410 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(max_test_cases); ++i) { 411 filter.set_max_version(max_test_cases[i].max_version); 412 const bool result = 413 seed_processor.CheckStudyVersion(filter, 414 Version(max_test_cases[i].version)); 415 EXPECT_EQ(max_test_cases[i].expected_result, result) << 416 "Max version case " << i << " failed!"; 417 } 418 419 // Check intersection semantics. 420 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(min_test_cases); ++i) { 421 for (size_t j = 0; j < ARRAYSIZE_UNSAFE(max_test_cases); ++j) { 422 filter.set_min_version(min_test_cases[i].min_version); 423 filter.set_max_version(max_test_cases[j].max_version); 424 425 if (!min_test_cases[i].expected_result) { 426 const bool result = 427 seed_processor.CheckStudyVersion( 428 filter, Version(min_test_cases[i].version)); 429 EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!"; 430 } 431 432 if (!max_test_cases[j].expected_result) { 433 const bool result = 434 seed_processor.CheckStudyVersion( 435 filter, Version(max_test_cases[j].version)); 436 EXPECT_FALSE(result) << "Case " << i << "," << j << " failed!"; 437 } 438 } 439 } 440 } 441 442 TEST_F(VariationsSeedProcessorTest, FilterAndValidateStudies) { 443 const std::string kTrial1Name = "A"; 444 const std::string kGroup1Name = "Group1"; 445 const std::string kTrial3Name = "B"; 446 447 VariationsSeed seed; 448 Study* study1 = seed.add_study(); 449 study1->set_name(kTrial1Name); 450 study1->set_default_experiment_name("Default"); 451 AddExperiment(kGroup1Name, 100, study1); 452 AddExperiment("Default", 0, study1); 453 454 Study* study2 = seed.add_study(); 455 *study2 = *study1; 456 study2->mutable_experiment(0)->set_name("Bam"); 457 ASSERT_EQ(seed.study(0).name(), seed.study(1).name()); 458 459 Study* study3 = seed.add_study(); 460 study3->set_name(kTrial3Name); 461 study3->set_default_experiment_name("Default"); 462 AddExperiment("A", 10, study3); 463 AddExperiment("Default", 25, study3); 464 465 std::vector<ProcessedStudy> processed_studies; 466 VariationsSeedProcessor().FilterAndValidateStudies( 467 seed, "en-CA", base::Time::Now(), base::Version("20.0.0.0"), 468 Study_Channel_STABLE, Study_FormFactor_DESKTOP, &processed_studies); 469 470 // Check that only the first kTrial1Name study was kept. 471 ASSERT_EQ(2U, processed_studies.size()); 472 EXPECT_EQ(kTrial1Name, processed_studies[0].study()->name()); 473 EXPECT_EQ(kGroup1Name, processed_studies[0].study()->experiment(0).name()); 474 EXPECT_EQ(kTrial3Name, processed_studies[1].study()->name()); 475 } 476 477 TEST_F(VariationsSeedProcessorTest, ForbidForceGroupWithVariationId) { 478 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1); 479 480 base::FieldTrialList field_trial_list(NULL); 481 482 Study study = CreateStudyWithFlagGroups(100, 0, 0); 483 study.mutable_experiment(1)->set_google_web_experiment_id(kExperimentId); 484 // Adding windows platform makes forcing_flag and variation Id incompatible. 485 study.mutable_filter()->add_platform(Study_Platform_PLATFORM_WINDOWS); 486 487 EXPECT_TRUE(CreateTrialFromStudy(&study)); 488 EXPECT_EQ(kFlagGroup1Name, 489 base::FieldTrialList::FindFullName(kFlagStudyName)); 490 VariationID id = GetGoogleVariationID(GOOGLE_WEB_PROPERTIES, kFlagStudyName, 491 kFlagGroup1Name); 492 EXPECT_EQ(EMPTY_ID, id); 493 } 494 495 // Test that the group for kForcingFlag1 is forced. 496 TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag1) { 497 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1); 498 499 base::FieldTrialList field_trial_list(NULL); 500 501 Study study = CreateStudyWithFlagGroups(100, 0, 0); 502 EXPECT_TRUE(CreateTrialFromStudy(&study)); 503 EXPECT_EQ(kFlagGroup1Name, 504 base::FieldTrialList::FindFullName(kFlagStudyName)); 505 } 506 507 // Test that the group for kForcingFlag2 is forced. 508 TEST_F(VariationsSeedProcessorTest, ForceGroupWithFlag2) { 509 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2); 510 511 base::FieldTrialList field_trial_list(NULL); 512 513 Study study = CreateStudyWithFlagGroups(100, 0, 0); 514 EXPECT_TRUE(CreateTrialFromStudy(&study)); 515 EXPECT_EQ(kFlagGroup2Name, 516 base::FieldTrialList::FindFullName(kFlagStudyName)); 517 } 518 519 TEST_F(VariationsSeedProcessorTest, ForceGroup_ChooseFirstGroupWithFlag) { 520 // Add the flag to the command line arguments so the flag group is forced. 521 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1); 522 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag2); 523 524 base::FieldTrialList field_trial_list(NULL); 525 526 Study study = CreateStudyWithFlagGroups(100, 0, 0); 527 EXPECT_TRUE(CreateTrialFromStudy(&study)); 528 EXPECT_EQ(kFlagGroup1Name, 529 base::FieldTrialList::FindFullName(kFlagStudyName)); 530 } 531 532 TEST_F(VariationsSeedProcessorTest, ForceGroup_DontChooseGroupWithFlag) { 533 base::FieldTrialList field_trial_list(NULL); 534 535 // The two flag groups are given high probability, which would normally make 536 // them very likely to be chosen. They won't be chosen since flag groups are 537 // never chosen when their flag isn't present. 538 Study study = CreateStudyWithFlagGroups(1, 999, 999); 539 EXPECT_TRUE(CreateTrialFromStudy(&study)); 540 EXPECT_EQ(kNonFlagGroupName, 541 base::FieldTrialList::FindFullName(kFlagStudyName)); 542 } 543 544 TEST_F(VariationsSeedProcessorTest, IsStudyExpired) { 545 VariationsSeedProcessor seed_processor; 546 547 const base::Time now = base::Time::Now(); 548 const base::TimeDelta delta = base::TimeDelta::FromHours(1); 549 const struct { 550 const base::Time expiry_date; 551 bool expected_result; 552 } expiry_test_cases[] = { 553 { now - delta, true }, 554 { now, true }, 555 { now + delta, false }, 556 }; 557 558 Study study; 559 560 // Expiry date not set should result in false. 561 EXPECT_FALSE(seed_processor.IsStudyExpired(study, now)); 562 563 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(expiry_test_cases); ++i) { 564 study.set_expiry_date(TimeToProtoTime(expiry_test_cases[i].expiry_date)); 565 const bool result = seed_processor.IsStudyExpired(study, now); 566 EXPECT_EQ(expiry_test_cases[i].expected_result, result) 567 << "Case " << i << " failed!"; 568 } 569 } 570 571 TEST_F(VariationsSeedProcessorTest, 572 NonExpiredStudyPrioritizedOverExpiredStudy) { 573 VariationsSeedProcessor seed_processor; 574 575 const std::string kTrialName = "A"; 576 const std::string kGroup1Name = "Group1"; 577 578 VariationsSeed seed; 579 Study* study1 = seed.add_study(); 580 study1->set_name(kTrialName); 581 study1->set_default_experiment_name("Default"); 582 AddExperiment(kGroup1Name, 100, study1); 583 AddExperiment("Default", 0, study1); 584 Study* study2 = seed.add_study(); 585 *study2 = *study1; 586 ASSERT_EQ(seed.study(0).name(), seed.study(1).name()); 587 588 const base::Time year_ago = 589 base::Time::Now() - base::TimeDelta::FromDays(365); 590 591 const base::Version version("20.0.0.0"); 592 593 // Check that adding [expired, non-expired] activates the non-expired one. 594 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName)); 595 { 596 base::FieldTrialList field_trial_list(NULL); 597 study1->set_expiry_date(TimeToProtoTime(year_ago)); 598 seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(), 599 version, Study_Channel_STABLE, 600 Study_FormFactor_DESKTOP); 601 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName)); 602 } 603 604 // Check that adding [non-expired, expired] activates the non-expired one. 605 ASSERT_EQ(std::string(), base::FieldTrialList::FindFullName(kTrialName)); 606 { 607 base::FieldTrialList field_trial_list(NULL); 608 study1->clear_expiry_date(); 609 study2->set_expiry_date(TimeToProtoTime(year_ago)); 610 seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(), 611 version, Study_Channel_STABLE, 612 Study_FormFactor_DESKTOP); 613 EXPECT_EQ(kGroup1Name, base::FieldTrialList::FindFullName(kTrialName)); 614 } 615 } 616 617 TEST_F(VariationsSeedProcessorTest, ValidateStudy) { 618 Study study; 619 study.set_default_experiment_name("def"); 620 AddExperiment("abc", 100, &study); 621 Study_Experiment* default_group = AddExperiment("def", 200, &study); 622 623 ProcessedStudy processed_study; 624 EXPECT_TRUE(processed_study.Init(&study, false)); 625 EXPECT_EQ(300, processed_study.total_probability()); 626 627 // Min version checks. 628 study.mutable_filter()->set_min_version("1.2.3.*"); 629 EXPECT_TRUE(processed_study.Init(&study, false)); 630 study.mutable_filter()->set_min_version("1.*.3"); 631 EXPECT_FALSE(processed_study.Init(&study, false)); 632 study.mutable_filter()->set_min_version("1.2.3"); 633 EXPECT_TRUE(processed_study.Init(&study, false)); 634 635 // Max version checks. 636 study.mutable_filter()->set_max_version("2.3.4.*"); 637 EXPECT_TRUE(processed_study.Init(&study, false)); 638 study.mutable_filter()->set_max_version("*.3"); 639 EXPECT_FALSE(processed_study.Init(&study, false)); 640 study.mutable_filter()->set_max_version("2.3.4"); 641 EXPECT_TRUE(processed_study.Init(&study, false)); 642 643 study.clear_default_experiment_name(); 644 EXPECT_FALSE(processed_study.Init(&study, false)); 645 646 study.set_default_experiment_name("xyz"); 647 EXPECT_FALSE(processed_study.Init(&study, false)); 648 649 study.set_default_experiment_name("def"); 650 default_group->clear_name(); 651 EXPECT_FALSE(processed_study.Init(&study, false)); 652 653 default_group->set_name("def"); 654 EXPECT_TRUE(processed_study.Init(&study, false)); 655 Study_Experiment* repeated_group = study.add_experiment(); 656 repeated_group->set_name("abc"); 657 repeated_group->set_probability_weight(1); 658 EXPECT_FALSE(processed_study.Init(&study, false)); 659 } 660 661 TEST_F(VariationsSeedProcessorTest, VariationParams) { 662 base::FieldTrialList field_trial_list(NULL); 663 664 Study study; 665 study.set_name("Study1"); 666 study.set_default_experiment_name("B"); 667 668 Study_Experiment* experiment1 = AddExperiment("A", 1, &study); 669 Study_Experiment_Param* param = experiment1->add_param(); 670 param->set_name("x"); 671 param->set_value("y"); 672 673 Study_Experiment* experiment2 = AddExperiment("B", 0, &study); 674 675 EXPECT_TRUE(CreateTrialFromStudy(&study)); 676 EXPECT_EQ("y", GetVariationParamValue("Study1", "x")); 677 678 study.set_name("Study2"); 679 experiment1->set_probability_weight(0); 680 experiment2->set_probability_weight(1); 681 EXPECT_TRUE(CreateTrialFromStudy(&study)); 682 EXPECT_EQ(std::string(), GetVariationParamValue("Study2", "x")); 683 } 684 685 TEST_F(VariationsSeedProcessorTest, VariationParamsWithForcingFlag) { 686 Study study = CreateStudyWithFlagGroups(100, 0, 0); 687 ASSERT_EQ(kForcingFlag1, study.experiment(1).forcing_flag()); 688 Study_Experiment_Param* param = study.mutable_experiment(1)->add_param(); 689 param->set_name("x"); 690 param->set_value("y"); 691 692 CommandLine::ForCurrentProcess()->AppendSwitch(kForcingFlag1); 693 base::FieldTrialList field_trial_list(NULL); 694 EXPECT_TRUE(CreateTrialFromStudy(&study)); 695 EXPECT_EQ(kFlagGroup1Name, base::FieldTrialList::FindFullName(study.name())); 696 EXPECT_EQ("y", GetVariationParamValue(study.name(), "x")); 697 } 698 699 TEST_F(VariationsSeedProcessorTest, StartsActive) { 700 base::FieldTrialList field_trial_list(NULL); 701 702 VariationsSeed seed; 703 Study* study1 = seed.add_study(); 704 study1->set_name("A"); 705 study1->set_default_experiment_name("Default"); 706 AddExperiment("AA", 100, study1); 707 AddExperiment("Default", 0, study1); 708 709 Study* study2 = seed.add_study(); 710 study2->set_name("B"); 711 study2->set_default_experiment_name("Default"); 712 AddExperiment("BB", 100, study2); 713 AddExperiment("Default", 0, study2); 714 study2->set_activation_type(Study_ActivationType_ACTIVATION_AUTO); 715 716 Study* study3 = seed.add_study(); 717 study3->set_name("C"); 718 study3->set_default_experiment_name("Default"); 719 AddExperiment("CC", 100, study3); 720 AddExperiment("Default", 0, study3); 721 study3->set_activation_type(Study_ActivationType_ACTIVATION_EXPLICIT); 722 723 VariationsSeedProcessor seed_processor; 724 seed_processor.CreateTrialsFromSeed(seed, "en-CA", base::Time::Now(), 725 base::Version("20.0.0.0"), 726 Study_Channel_STABLE, 727 Study_FormFactor_DESKTOP); 728 729 // Non-specified and ACTIVATION_EXPLICIT should not start active, but 730 // ACTIVATION_AUTO should. 731 EXPECT_FALSE(IsFieldTrialActive("A")); 732 EXPECT_TRUE(IsFieldTrialActive("B")); 733 EXPECT_FALSE(IsFieldTrialActive("C")); 734 735 EXPECT_EQ("AA", base::FieldTrialList::FindFullName("A")); 736 EXPECT_EQ("BB", base::FieldTrialList::FindFullName("B")); 737 EXPECT_EQ("CC", base::FieldTrialList::FindFullName("C")); 738 739 // Now, all studies should be active. 740 EXPECT_TRUE(IsFieldTrialActive("A")); 741 EXPECT_TRUE(IsFieldTrialActive("B")); 742 EXPECT_TRUE(IsFieldTrialActive("C")); 743 } 744 745 } // namespace chrome_variations 746