diff --git a/rtc_base/experiments/field_trial_list_unittest.cc b/rtc_base/experiments/field_trial_list_unittest.cc index 99066cc144..a1abfe4bf8 100644 --- a/rtc_base/experiments/field_trial_list_unittest.cc +++ b/rtc_base/experiments/field_trial_list_unittest.cc @@ -20,14 +20,14 @@ namespace webrtc { struct Garment { int price = 0; - TimeDelta age = TimeDelta::Zero(); + std::string color = ""; // Only needed for testing. Garment() = default; - Garment(int p, TimeDelta a) : price(p), age(a) {} + Garment(int p, std::string c) : price(p), color(c) {} bool operator==(const Garment& other) const { - return price == other.price && age == other.age; + return price == other.price && color == other.color; } }; @@ -43,23 +43,34 @@ TEST(FieldTrialListTest, ParsesListParameter) { EXPECT_THAT(my_list.Get(), ElementsAre(1, 2, 3)); ParseFieldTrial({&my_list}, "l:-1"); EXPECT_THAT(my_list.Get(), ElementsAre(-1)); + + FieldTrialList another_list("l", {"hat"}); + EXPECT_THAT(another_list.Get(), ElementsAre("hat")); + ParseFieldTrial({&another_list}, "l"); + EXPECT_THAT(another_list.Get(), IsEmpty()); + ParseFieldTrial({&another_list}, "l:"); + EXPECT_THAT(another_list.Get(), ElementsAre("")); + ParseFieldTrial({&another_list}, "l:scarf|hat|mittens"); + EXPECT_THAT(another_list.Get(), ElementsAre("scarf", "hat", "mittens")); + ParseFieldTrial({&another_list}, "l:scarf"); + EXPECT_THAT(another_list.Get(), ElementsAre("scarf")); } // Normal usage. TEST(FieldTrialListTest, ParsesStructList) { FieldTrialStructList my_list( - {FieldTrialStructMember("age", [](Garment* g) { return &g->age; }), + {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, - {{1, TimeDelta::seconds(100)}, {2, TimeDelta::PlusInfinity()}}); + {{1, "blue"}, {2, "red"}}); ParseFieldTrial({&my_list}, - "age:inf|10s|80ms," + "color:mauve|red|gold," "price:10|20|30," "other_param:asdf"); - ASSERT_THAT(my_list.Get(), ElementsAre(Garment{10, TimeDelta::PlusInfinity()}, - Garment{20, TimeDelta::seconds(10)}, - Garment{30, TimeDelta::ms(80)})); + ASSERT_THAT(my_list.Get(), + ElementsAre(Garment{10, "mauve"}, Garment{20, "red"}, + Garment{30, "gold"})); } // One FieldTrialList has the wrong length, so we use the user-provided default @@ -67,57 +78,54 @@ TEST(FieldTrialListTest, ParsesStructList) { TEST(FieldTrialListTest, StructListKeepsDefaultWithMismatchingLength) { FieldTrialStructList my_list( {FieldTrialStructMember("wrong_length", - [](Garment* g) { return &g->age; }), + [](Garment* g) { return &g->color; }), FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, - {{1, TimeDelta::seconds(100)}, {2, TimeDelta::PlusInfinity()}}); + {{1, "blue"}, {2, "red"}}); ParseFieldTrial({&my_list}, - "wrong_length:3|2|4|3," + "wrong_length:mauve|magenta|chartreuse|indigo," "garment:hat|hat|crown," "price:10|20|30"); ASSERT_THAT(my_list.Get(), - ElementsAre(Garment{1, TimeDelta::seconds(100)}, - Garment{2, TimeDelta::PlusInfinity()})); + ElementsAre(Garment{1, "blue"}, Garment{2, "red"})); } // One list is missing. We set the values we're given, and the others remain // as whatever the Garment default constructor set them to. TEST(FieldTrialListTest, StructListUsesDefaultForMissingList) { FieldTrialStructList my_list( - {FieldTrialStructMember("age", [](Garment* g) { return &g->age; }), + {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, - {{1, TimeDelta::seconds(100)}, {2, TimeDelta::PlusInfinity()}}); + {{1, "blue"}, {2, "red"}}); ParseFieldTrial({&my_list}, "price:10|20|30"); - ASSERT_THAT(my_list.Get(), ElementsAre(Garment{10, TimeDelta::Zero()}, - Garment{20, TimeDelta::Zero()}, - Garment{30, TimeDelta::Zero()})); + ASSERT_THAT(my_list.Get(), + ElementsAre(Garment{10, ""}, Garment{20, ""}, Garment{30, ""})); } // The user haven't provided values for any lists, so we use the default list. TEST(FieldTrialListTest, StructListUsesDefaultListWithoutValues) { FieldTrialStructList my_list( - {FieldTrialStructMember("age", [](Garment* g) { return &g->age; }), + {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, - {{1, TimeDelta::seconds(100)}, {2, TimeDelta::PlusInfinity()}}); + {{1, "blue"}, {2, "red"}}); ParseFieldTrial({&my_list}, ""); ASSERT_THAT(my_list.Get(), - ElementsAre(Garment{1, TimeDelta::seconds(100)}, - Garment{2, TimeDelta::PlusInfinity()})); + ElementsAre(Garment{1, "blue"}, Garment{2, "red"})); } // Some lists are provided and all are empty, so we return a empty list. TEST(FieldTrialListTest, StructListHandlesEmptyLists) { FieldTrialStructList my_list( - {FieldTrialStructMember("age", [](Garment* g) { return &g->age; }), + {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, - {{1, TimeDelta::seconds(100)}, {2, TimeDelta::PlusInfinity()}}); + {{1, "blue"}, {2, "red"}}); - ParseFieldTrial({&my_list}, "age,price"); + ParseFieldTrial({&my_list}, "color,price"); ASSERT_EQ(my_list.Get().size(), 0u); } diff --git a/rtc_base/experiments/field_trial_parser.cc b/rtc_base/experiments/field_trial_parser.cc index 1442c0106b..5f33b6eff8 100644 --- a/rtc_base/experiments/field_trial_parser.cc +++ b/rtc_base/experiments/field_trial_parser.cc @@ -139,6 +139,11 @@ absl::optional ParseTypedParameter(std::string str) { return absl::nullopt; } +template <> +absl::optional ParseTypedParameter(std::string str) { + return std::move(str); +} + template <> absl::optional> ParseTypedParameter>( std::string str) { @@ -221,6 +226,7 @@ template class FieldTrialParameter; template class FieldTrialParameter; template class FieldTrialParameter; template class FieldTrialParameter; +template class FieldTrialParameter; template class FieldTrialConstrained; template class FieldTrialConstrained; @@ -230,5 +236,6 @@ template class FieldTrialOptional; template class FieldTrialOptional; template class FieldTrialOptional; template class FieldTrialOptional; +template class FieldTrialOptional; } // namespace webrtc diff --git a/rtc_base/experiments/field_trial_parser.h b/rtc_base/experiments/field_trial_parser.h index 9ab2900811..42535ed6a4 100644 --- a/rtc_base/experiments/field_trial_parser.h +++ b/rtc_base/experiments/field_trial_parser.h @@ -244,9 +244,11 @@ template <> absl::optional ParseTypedParameter(std::string str); template <> absl::optional ParseTypedParameter(std::string str); - template <> absl::optional ParseTypedParameter(std::string str); +template <> +absl::optional ParseTypedParameter(std::string str); + template <> absl::optional> ParseTypedParameter>( std::string str); @@ -268,6 +270,8 @@ extern template class FieldTrialParameter; extern template class FieldTrialParameter; // Interpreted using sscanf %u. extern template class FieldTrialParameter; +// Using the given value as is. +extern template class FieldTrialParameter; extern template class FieldTrialConstrained; extern template class FieldTrialConstrained; @@ -277,6 +281,7 @@ extern template class FieldTrialOptional; extern template class FieldTrialOptional; extern template class FieldTrialOptional; extern template class FieldTrialOptional; +extern template class FieldTrialOptional; } // namespace webrtc diff --git a/rtc_base/experiments/field_trial_parser_unittest.cc b/rtc_base/experiments/field_trial_parser_unittest.cc index 92649b4bf1..d36b3c7d95 100644 --- a/rtc_base/experiments/field_trial_parser_unittest.cc +++ b/rtc_base/experiments/field_trial_parser_unittest.cc @@ -25,13 +25,17 @@ struct DummyExperiment { FieldTrialParameter retries = FieldTrialParameter("r", 5); FieldTrialParameter size = FieldTrialParameter("s", 3); FieldTrialParameter ping = FieldTrialParameter("p", 0); + FieldTrialParameter hash = + FieldTrialParameter("h", "a80"); explicit DummyExperiment(std::string field_trial) { - ParseFieldTrial({&enabled, &factor, &retries, &size, &ping}, field_trial); + ParseFieldTrial({&enabled, &factor, &retries, &size, &ping, &hash}, + field_trial); } DummyExperiment() { std::string trial_string = field_trial::FindFullName(kDummyExperiment); - ParseFieldTrial({&enabled, &factor, &retries, &size, &ping}, trial_string); + ParseFieldTrial({&enabled, &factor, &retries, &size, &ping, &hash}, + trial_string); } }; @@ -44,17 +48,18 @@ enum class CustomEnum { } // namespace TEST(FieldTrialParserTest, ParsesValidParameters) { - DummyExperiment exp("Enabled,f:-1.7,r:2,s:10,p:1"); + DummyExperiment exp("Enabled,f:-1.7,r:2,s:10,p:1,h:x7c"); EXPECT_TRUE(exp.enabled.Get()); EXPECT_EQ(exp.factor.Get(), -1.7); EXPECT_EQ(exp.retries.Get(), 2); EXPECT_EQ(exp.size.Get(), 10u); EXPECT_EQ(exp.ping.Get(), true); + EXPECT_EQ(exp.hash.Get(), "x7c"); } TEST(FieldTrialParserTest, InitializesFromFieldTrial) { test::ScopedFieldTrials field_trials( "WebRTC-OtherExperiment/Disabled/" - "WebRTC-DummyExperiment/Enabled,f:-1.7,r:2,s:10,p:1/" + "WebRTC-DummyExperiment/Enabled,f:-1.7,r:2,s:10,p:1,h:x7c/" "WebRTC-AnotherExperiment/Enabled,f:-3.1,otherstuff:beef/"); DummyExperiment exp; EXPECT_TRUE(exp.enabled.Get()); @@ -62,6 +67,7 @@ TEST(FieldTrialParserTest, InitializesFromFieldTrial) { EXPECT_EQ(exp.retries.Get(), 2); EXPECT_EQ(exp.size.Get(), 10u); EXPECT_EQ(exp.ping.Get(), true); + EXPECT_EQ(exp.hash.Get(), "x7c"); } TEST(FieldTrialParserTest, UsesDefaults) { DummyExperiment exp(""); @@ -70,6 +76,7 @@ TEST(FieldTrialParserTest, UsesDefaults) { EXPECT_EQ(exp.retries.Get(), 5); EXPECT_EQ(exp.size.Get(), 3u); EXPECT_EQ(exp.ping.Get(), false); + EXPECT_EQ(exp.hash.Get(), "a80"); } TEST(FieldTrialParserTest, CanHandleMixedInput) { DummyExperiment exp("p:true,h:,Enabled"); @@ -78,6 +85,7 @@ TEST(FieldTrialParserTest, CanHandleMixedInput) { EXPECT_EQ(exp.retries.Get(), 5); EXPECT_EQ(exp.size.Get(), 3u); EXPECT_EQ(exp.ping.Get(), true); + EXPECT_EQ(exp.hash.Get(), ""); } TEST(FieldTrialParserTest, ParsesDoubleParameter) { FieldTrialParameter double_param("f", 0.0); @@ -101,6 +109,7 @@ TEST(FieldTrialParserTest, IgnoresInvalid) { EXPECT_EQ(exp.retries.Get(), 5); EXPECT_EQ(exp.size.Get(), 3u); EXPECT_EQ(exp.ping.Get(), false); + EXPECT_EQ(exp.hash.Get(), "a80"); } TEST(FieldTrialParserTest, IgnoresOutOfRange) { FieldTrialConstrained low("low", 10, absl::nullopt, 100); @@ -150,6 +159,11 @@ TEST(FieldTrialParserTest, ParsesOptionalParameters) { ParseFieldTrial({&max_size}, "c:20"); EXPECT_EQ(max_size.GetOptional().value(), 20u); + FieldTrialOptional optional_string("s", std::string("ab")); + ParseFieldTrial({&optional_string}, "s:"); + EXPECT_EQ(optional_string.GetOptional().value(), ""); + ParseFieldTrial({&optional_string}, "s"); + EXPECT_FALSE(optional_string.GetOptional().has_value()); } TEST(FieldTrialParserTest, ParsesCustomEnumParameter) { FieldTrialEnum my_enum("e", CustomEnum::kDefault,