Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // Test of FieldTrial class | 5 // Test of FieldTrial class |
| 6 | 6 |
| 7 #include "base/metrics/field_trial.h" | 7 #include "base/metrics/field_trial.h" |
| 8 | 8 |
| 9 #include "base/rand_util.h" | |
| 9 #include "base/stringprintf.h" | 10 #include "base/stringprintf.h" |
| 11 #include "base/string_number_conversions.h" | |
| 10 #include "testing/gtest/include/gtest/gtest.h" | 12 #include "testing/gtest/include/gtest/gtest.h" |
| 11 | 13 |
| 14 #include <limits> | |
| 15 | |
| 12 namespace base { | 16 namespace base { |
| 13 | 17 |
| 14 class FieldTrialTest : public testing::Test { | 18 class FieldTrialTest : public testing::Test { |
| 15 public: | 19 public: |
| 16 FieldTrialTest() : trial_list_() { | 20 FieldTrialTest() : trial_list_("client_id") { |
| 17 Time now = Time::NowFromSystemTime(); | 21 Time now = Time::NowFromSystemTime(); |
| 18 TimeDelta oneYear = TimeDelta::FromDays(365); | 22 TimeDelta oneYear = TimeDelta::FromDays(365); |
| 19 Time::Exploded exploded; | 23 Time::Exploded exploded; |
| 20 | 24 |
| 21 Time next_year_time = now + oneYear; | 25 Time next_year_time = now + oneYear; |
| 22 next_year_time.LocalExplode(&exploded); | 26 next_year_time.LocalExplode(&exploded); |
| 23 next_year_ = exploded.year; | 27 next_year_ = exploded.year; |
| 24 | 28 |
| 25 Time last_year_time = now - oneYear; | 29 Time last_year_time = now - oneYear; |
| 26 last_year_time.LocalExplode(&exploded); | 30 last_year_time.LocalExplode(&exploded); |
| 27 last_year_ = exploded.year; | 31 last_year_ = exploded.year; |
| 28 } | 32 } |
| 29 | 33 |
| 34 void SetClientId(const std::string& client_id) { | |
| 35 trial_list_.client_id_ = client_id; | |
| 36 } | |
| 37 | |
| 38 void DoSaveTest(); | |
| 39 void DoRestoreTest(const std::string& restored_client_id); | |
| 40 | |
| 30 protected: | 41 protected: |
| 31 int next_year_; | 42 int next_year_; |
| 32 int last_year_; | 43 int last_year_; |
| 33 | 44 |
| 34 private: | 45 private: |
| 35 FieldTrialList trial_list_; | 46 FieldTrialList trial_list_; |
| 36 }; | 47 }; |
| 37 | 48 |
| 38 // Test registration, and also check that destructors are called for trials | 49 // Test registration, and also check that destructors are called for trials |
| 39 // (and that Purify doesn't catch us leaking). | 50 // (and that Purify doesn't catch us leaking). |
| (...skipping 170 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 210 name, 1000000000, default_group_name, last_year_, 1, 1); | 221 name, 1000000000, default_group_name, last_year_, 1, 1); |
| 211 trial->AppendGroup(loser, 999999999); // 99.9999999% chance of being chosen. | 222 trial->AppendGroup(loser, 999999999); // 99.9999999% chance of being chosen. |
| 212 | 223 |
| 213 // Because trial has expired, we should always be in the default group. | 224 // Because trial has expired, we should always be in the default group. |
| 214 EXPECT_EQ(FieldTrial::kDefaultGroupNumber, trial->group()); | 225 EXPECT_EQ(FieldTrial::kDefaultGroupNumber, trial->group()); |
| 215 | 226 |
| 216 // And that default_group_name should ALWAYS win. | 227 // And that default_group_name should ALWAYS win. |
| 217 EXPECT_EQ(default_group_name, trial->group_name()); | 228 EXPECT_EQ(default_group_name, trial->group_name()); |
| 218 } | 229 } |
| 219 | 230 |
| 220 TEST_F(FieldTrialTest, Save) { | 231 void FieldTrialTest::DoSaveTest() { |
| 232 const std::string& client_id = FieldTrialList::client_id(); | |
| 221 std::string save_string; | 233 std::string save_string; |
| 222 | 234 |
| 223 FieldTrial* trial = | 235 FieldTrial* trial = |
| 224 new FieldTrial( | 236 new FieldTrial( |
| 225 "Some name", 10, "Default some name", next_year_, 12, 31); | 237 "Some name", 10, "Default some name", next_year_, 12, 31); |
| 226 // There is no winner yet, so no textual group name is associated with trial. | 238 // There is no winner yet, so no textual group name is associated with trial. |
| 227 EXPECT_EQ("", trial->group_name_internal()); | 239 EXPECT_EQ("", trial->group_name_internal()); |
| 228 FieldTrialList::StatesToString(&save_string); | 240 FieldTrialList::StatesToString(&save_string); |
| 229 EXPECT_EQ("Some name/Default some name/", save_string); | 241 EXPECT_EQ(client_id + "/Some name/Default some name/", save_string); |
|
jar (doing other things)
2011/05/03 00:17:47
Warning: This test may need to change if we stop d
Jói
2011/05/03 17:41:47
Done.
| |
| 230 save_string.clear(); | 242 save_string.clear(); |
| 231 | 243 |
| 232 // Create a winning group. | 244 // Create a winning group. |
| 233 trial->AppendGroup("Winner", 10); | 245 trial->AppendGroup("Winner", 10); |
| 234 FieldTrialList::StatesToString(&save_string); | 246 FieldTrialList::StatesToString(&save_string); |
| 235 EXPECT_EQ("Some name/Winner/", save_string); | 247 EXPECT_EQ(client_id + "/Some name/Winner/", save_string); |
| 236 save_string.clear(); | 248 save_string.clear(); |
| 237 | 249 |
| 238 // Create a second trial and winning group. | 250 // Create a second trial and winning group. |
| 239 FieldTrial* trial2 = | 251 FieldTrial* trial2 = |
| 240 new FieldTrial("xxx", 10, "Default xxx", next_year_, 12, 31); | 252 new FieldTrial("xxx", 10, "Default xxx", next_year_, 12, 31); |
| 241 trial2->AppendGroup("yyyy", 10); | 253 trial2->AppendGroup("yyyy", 10); |
| 242 | 254 |
| 243 FieldTrialList::StatesToString(&save_string); | 255 FieldTrialList::StatesToString(&save_string); |
| 244 // We assume names are alphabetized... though this is not critical. | 256 // We assume names are alphabetized... though this is not critical. |
| 245 EXPECT_EQ("Some name/Winner/xxx/yyyy/", save_string); | 257 EXPECT_EQ(client_id + "/Some name/Winner/xxx/yyyy/", save_string); |
| 246 } | 258 } |
| 247 | 259 |
| 248 TEST_F(FieldTrialTest, Restore) { | 260 TEST_F(FieldTrialTest, Save) { |
| 261 DoSaveTest(); | |
| 262 } | |
| 263 | |
| 264 TEST_F(FieldTrialTest, SaveWithEmptyClientId) { | |
| 265 SetClientId(""); | |
| 266 DoSaveTest(); | |
| 267 } | |
| 268 | |
| 269 void FieldTrialTest::DoRestoreTest(const std::string& restored_client_id) { | |
| 249 EXPECT_TRUE(FieldTrialList::Find("Some_name") == NULL); | 270 EXPECT_TRUE(FieldTrialList::Find("Some_name") == NULL); |
| 250 EXPECT_TRUE(FieldTrialList::Find("xxx") == NULL); | 271 EXPECT_TRUE(FieldTrialList::Find("xxx") == NULL); |
| 251 | 272 |
| 252 FieldTrialList::CreateTrialsInChildProcess("Some_name/Winner/xxx/yyyy/"); | 273 FieldTrialList::CreateTrialsInChildProcess( |
| 274 restored_client_id + "/Some_name/Winner/xxx/yyyy/"); | |
| 275 EXPECT_EQ(restored_client_id, FieldTrialList::client_id()); | |
| 253 | 276 |
| 254 FieldTrial* trial = FieldTrialList::Find("Some_name"); | 277 FieldTrial* trial = FieldTrialList::Find("Some_name"); |
| 255 ASSERT_NE(static_cast<FieldTrial*>(NULL), trial); | 278 ASSERT_NE(static_cast<FieldTrial*>(NULL), trial); |
| 256 EXPECT_EQ("Winner", trial->group_name()); | 279 EXPECT_EQ("Winner", trial->group_name()); |
| 257 EXPECT_EQ("Some_name", trial->name()); | 280 EXPECT_EQ("Some_name", trial->name()); |
| 258 | 281 |
| 259 trial = FieldTrialList::Find("xxx"); | 282 trial = FieldTrialList::Find("xxx"); |
| 260 ASSERT_NE(static_cast<FieldTrial*>(NULL), trial); | 283 ASSERT_NE(static_cast<FieldTrial*>(NULL), trial); |
| 261 EXPECT_EQ("yyyy", trial->group_name()); | 284 EXPECT_EQ("yyyy", trial->group_name()); |
| 262 EXPECT_EQ("xxx", trial->name()); | 285 EXPECT_EQ("xxx", trial->name()); |
| 263 } | 286 } |
| 264 | 287 |
| 288 TEST_F(FieldTrialTest, Restore) { | |
| 289 DoRestoreTest("restored_client_id"); | |
| 290 } | |
| 291 | |
| 292 TEST_F(FieldTrialTest, RestoreWithEmptyClientId) { | |
| 293 DoRestoreTest(""); | |
| 294 } | |
| 295 | |
| 265 TEST_F(FieldTrialTest, BogusRestore) { | 296 TEST_F(FieldTrialTest, BogusRestore) { |
| 266 EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess("MissingSlash")); | 297 EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess( |
| 267 EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess("MissingGroupName/")); | 298 "restored_client_id/MissingSlash")); |
| 268 EXPECT_FALSE( | 299 EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess( |
| 269 FieldTrialList::CreateTrialsInChildProcess("MissingFinalSlash/gname")); | 300 "restored_client_id/MissingGroupName/")); |
| 270 EXPECT_FALSE( | 301 EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess( |
| 271 FieldTrialList::CreateTrialsInChildProcess("/noname, only group/")); | 302 "restored_client_id/MissingFinalSlash/gname")); |
| 303 EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess( | |
| 304 "restored_client_id/noname, only group/")); | |
| 272 } | 305 } |
| 273 | 306 |
| 274 TEST_F(FieldTrialTest, DuplicateRestore) { | 307 TEST_F(FieldTrialTest, DuplicateRestore) { |
| 275 FieldTrial* trial = | 308 FieldTrial* trial = |
| 276 new FieldTrial( | 309 new FieldTrial( |
| 277 "Some name", 10, "Default some name", next_year_, 12, 31); | 310 "Some name", 10, "Default some name", next_year_, 12, 31); |
| 278 trial->AppendGroup("Winner", 10); | 311 trial->AppendGroup("Winner", 10); |
| 279 std::string save_string; | 312 std::string save_string; |
| 280 FieldTrialList::StatesToString(&save_string); | 313 FieldTrialList::StatesToString(&save_string); |
| 281 EXPECT_EQ("Some name/Winner/", save_string); | 314 EXPECT_EQ("client_id/Some name/Winner/", save_string); |
| 282 | 315 |
| 283 // It is OK if we redundantly specify a winner. | 316 // It is OK if we redundantly specify a winner. |
| 284 EXPECT_TRUE(FieldTrialList::CreateTrialsInChildProcess(save_string)); | 317 EXPECT_TRUE(FieldTrialList::CreateTrialsInChildProcess(save_string)); |
| 285 | 318 |
| 286 // But it is an error to try to change to a different winner. | 319 // But it is an error to try to change to a different winner. |
| 287 EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess("Some name/Loser/")); | 320 EXPECT_FALSE(FieldTrialList::CreateTrialsInChildProcess( |
| 321 "client_id/Some name/Loser/")); | |
| 288 } | 322 } |
| 289 | 323 |
| 290 TEST_F(FieldTrialTest, CreateFieldTrial) { | 324 TEST_F(FieldTrialTest, CreateFieldTrial) { |
| 291 EXPECT_TRUE(FieldTrialList::Find("Some_name") == NULL); | 325 EXPECT_TRUE(FieldTrialList::Find("Some_name") == NULL); |
| 292 | 326 |
| 293 FieldTrialList::CreateFieldTrial("Some_name", "Winner"); | 327 FieldTrialList::CreateFieldTrial("Some_name", "Winner"); |
| 294 | 328 |
| 295 FieldTrial* trial = FieldTrialList::Find("Some_name"); | 329 FieldTrial* trial = FieldTrialList::Find("Some_name"); |
| 296 ASSERT_NE(static_cast<FieldTrial*>(NULL), trial); | 330 ASSERT_NE(static_cast<FieldTrial*>(NULL), trial); |
| 297 EXPECT_EQ("Winner", trial->group_name()); | 331 EXPECT_EQ("Winner", trial->group_name()); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 314 } | 348 } |
| 315 | 349 |
| 316 TEST_F(FieldTrialTest, MakeName) { | 350 TEST_F(FieldTrialTest, MakeName) { |
| 317 FieldTrial* trial = | 351 FieldTrial* trial = |
| 318 new FieldTrial("Field Trial", 10, "Winner", next_year_, 12, 31); | 352 new FieldTrial("Field Trial", 10, "Winner", next_year_, 12, 31); |
| 319 trial->group(); | 353 trial->group(); |
| 320 EXPECT_EQ("Histogram_Winner", | 354 EXPECT_EQ("Histogram_Winner", |
| 321 FieldTrial::MakeName("Histogram", "Field Trial")); | 355 FieldTrial::MakeName("Histogram", "Field Trial")); |
| 322 } | 356 } |
| 323 | 357 |
| 358 TEST_F(FieldTrialTest, HashClientId) { | |
| 359 double results[] = { | |
| 360 FieldTrial::HashClientId("hi", "1"), | |
| 361 FieldTrial::HashClientId("there", "1"), | |
| 362 }; | |
| 363 ASSERT_NE(results[0], results[1]); | |
| 364 for (size_t i = 0; i < arraysize(results); ++i) { | |
| 365 ASSERT_LE(0.0, results[i]); | |
| 366 ASSERT_GT(1.0, results[i]); | |
| 367 } | |
| 368 | |
| 369 ASSERT_EQ(FieldTrial::HashClientId("yo", "1"), | |
| 370 FieldTrial::HashClientId("yo", "1")); | |
| 371 ASSERT_NE(FieldTrial::HashClientId("yo", "something"), | |
| 372 FieldTrial::HashClientId("yo", "else")); | |
| 373 } | |
| 374 | |
| 375 TEST_F(FieldTrialTest, HashClientIdIsUniform) { | |
| 376 // Choose a random start number but go sequentially from there, so | |
| 377 // that each test tries a different range but we never provide uniformly | |
| 378 // distributed input data. | |
| 379 int current_number = RandInt(0, std::numeric_limits<int>::max()); | |
| 380 | |
| 381 // The expected value of a random distribution is the average over all | |
| 382 // samples as the number of samples approaches infinity. For a uniform | |
| 383 // distribution from [0.0, 1.0) this would be 0.5. | |
| 384 // | |
| 385 // We do kSamplesBetweenChecks at a time and check if the value has converged | |
| 386 // to a narrow interval around 0.5. A non-uniform distribution would likely | |
| 387 // converge at something different, or not converge consistently within this | |
| 388 // range (i.e. the test would start timing out occasionally). | |
| 389 int kSamplesBetweenChecks = 300; | |
| 390 int num_samples = 0; | |
| 391 double total_value = 0.0; | |
| 392 while (true) { | |
| 393 for (int i = 0; i < kSamplesBetweenChecks; ++i) { | |
| 394 total_value += FieldTrial::HashClientId( | |
| 395 IntToString(current_number++), "salt"); | |
| 396 num_samples++; | |
| 397 } | |
| 398 | |
| 399 double average = total_value / num_samples; | |
| 400 double kExpectedMin = 0.48; | |
| 401 double kExpectedMax = 0.52; | |
| 402 | |
| 403 if (num_samples > 1000 && | |
| 404 (average < kExpectedMin || average > kExpectedMax)) { | |
| 405 // Only printed once we have enough samples that it's very unlikely | |
| 406 // things haven't converged. | |
| 407 printf("After %d samples, the average was %f, outside the expected\n" | |
| 408 "range (%f, %f). We will add more samples and check after every\n" | |
| 409 "%d samples. If the average does not converge, something\n" | |
| 410 "is broken. If it does converge, the test will pass.\n", | |
| 411 num_samples, average, | |
| 412 kExpectedMin, kExpectedMax, kSamplesBetweenChecks); | |
| 413 } else { | |
| 414 // Success. | |
| 415 break; | |
| 416 } | |
| 417 } | |
| 418 } | |
| 419 | |
| 420 TEST_F(FieldTrialTest, UseOneTimeRandomization) { | |
| 421 // Simply asserts that two trials using one-time randomization | |
| 422 // that have different names, normally generate different results. | |
| 423 // | |
| 424 // Note that depending on the one-time random initialization, they | |
| 425 // _might_ actually give the same result, but we know that given | |
| 426 // the particular client_id we use for unit tests they won't. | |
| 427 scoped_refptr<FieldTrial> trials[] = { | |
| 428 new FieldTrial("one", 100, "default", next_year_, 1, 1), | |
| 429 new FieldTrial("two", 100, "default", next_year_, 1, 1), | |
| 430 }; | |
| 431 | |
| 432 for (size_t i = 0; i < arraysize(trials); ++i) { | |
| 433 trials[i]->UseOneTimeRandomization(); | |
| 434 | |
| 435 for (int j = 0; j < 100; ++j) { | |
| 436 trials[i]->AppendGroup("", 1); | |
| 437 } | |
| 438 } | |
| 439 | |
| 440 // The trials are most likely to give different results since they have | |
| 441 // different names. | |
| 442 ASSERT_NE(trials[0]->group(), trials[1]->group()); | |
| 443 ASSERT_NE(trials[0]->group_name(), trials[1]->group_name()); | |
| 444 } | |
| 445 | |
| 446 TEST_F(FieldTrialTest, DisableImmediately) { | |
| 447 FieldTrial* trial = | |
| 448 new FieldTrial("trial", 100, "default", next_year_, 12, 31); | |
| 449 trial->Disable(); | |
| 450 ASSERT_EQ("default", trial->group_name()); | |
| 451 ASSERT_EQ(FieldTrial::kDefaultGroupNumber, trial->group()); | |
| 452 } | |
| 453 | |
| 454 TEST_F(FieldTrialTest, DisableAfterInitialization) { | |
| 455 FieldTrial* trial = | |
| 456 new FieldTrial("trial", 100, "default", next_year_, 12, 31); | |
| 457 trial->AppendGroup("non_default", 100); | |
| 458 ASSERT_EQ("non_default", trial->group_name()); | |
| 459 trial->Disable(); | |
| 460 ASSERT_EQ("default", trial->group_name()); | |
| 461 } | |
| 462 | |
| 324 } // namespace base | 463 } // namespace base |
| OLD | NEW |