1 /*
2  *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 #include "rtc_base/experiments/field_trial_parser.h"
11 
12 #include "rtc_base/experiments/field_trial_list.h"
13 #include "rtc_base/gunit.h"
14 #include "system_wrappers/include/field_trial.h"
15 #include "test/field_trial.h"
16 #include "test/gmock.h"
17 
18 namespace webrtc {
19 namespace {
20 const char kDummyExperiment[] = "WebRTC-DummyExperiment";
21 
22 struct DummyExperiment {
23   FieldTrialFlag enabled = FieldTrialFlag("Enabled");
24   FieldTrialParameter<double> factor = FieldTrialParameter<double>("f", 0.5);
25   FieldTrialParameter<int> retries = FieldTrialParameter<int>("r", 5);
26   FieldTrialParameter<unsigned> size = FieldTrialParameter<unsigned>("s", 3);
27   FieldTrialParameter<bool> ping = FieldTrialParameter<bool>("p", 0);
28   FieldTrialParameter<std::string> hash =
29       FieldTrialParameter<std::string>("h", "a80");
30 
DummyExperimentwebrtc::__anon500d1a9a0111::DummyExperiment31   explicit DummyExperiment(std::string field_trial) {
32     ParseFieldTrial({&enabled, &factor, &retries, &size, &ping, &hash},
33                     field_trial);
34   }
DummyExperimentwebrtc::__anon500d1a9a0111::DummyExperiment35   DummyExperiment() {
36     std::string trial_string = field_trial::FindFullName(kDummyExperiment);
37     ParseFieldTrial({&enabled, &factor, &retries, &size, &ping, &hash},
38                     trial_string);
39   }
40 };
41 
42 enum class CustomEnum {
43   kDefault = 0,
44   kRed = 1,
45   kBlue = 2,
46 };
47 
48 }  // namespace
49 
TEST(FieldTrialParserTest,ParsesValidParameters)50 TEST(FieldTrialParserTest, ParsesValidParameters) {
51   DummyExperiment exp("Enabled,f:-1.7,r:2,s:10,p:1,h:x7c");
52   EXPECT_TRUE(exp.enabled.Get());
53   EXPECT_EQ(exp.factor.Get(), -1.7);
54   EXPECT_EQ(exp.retries.Get(), 2);
55   EXPECT_EQ(exp.size.Get(), 10u);
56   EXPECT_EQ(exp.ping.Get(), true);
57   EXPECT_EQ(exp.hash.Get(), "x7c");
58 }
TEST(FieldTrialParserTest,InitializesFromFieldTrial)59 TEST(FieldTrialParserTest, InitializesFromFieldTrial) {
60   test::ScopedFieldTrials field_trials(
61       "WebRTC-OtherExperiment/Disabled/"
62       "WebRTC-DummyExperiment/Enabled,f:-1.7,r:2,s:10,p:1,h:x7c/"
63       "WebRTC-AnotherExperiment/Enabled,f:-3.1,otherstuff:beef/");
64   DummyExperiment exp;
65   EXPECT_TRUE(exp.enabled.Get());
66   EXPECT_EQ(exp.factor.Get(), -1.7);
67   EXPECT_EQ(exp.retries.Get(), 2);
68   EXPECT_EQ(exp.size.Get(), 10u);
69   EXPECT_EQ(exp.ping.Get(), true);
70   EXPECT_EQ(exp.hash.Get(), "x7c");
71 }
TEST(FieldTrialParserTest,UsesDefaults)72 TEST(FieldTrialParserTest, UsesDefaults) {
73   DummyExperiment exp("");
74   EXPECT_FALSE(exp.enabled.Get());
75   EXPECT_EQ(exp.factor.Get(), 0.5);
76   EXPECT_EQ(exp.retries.Get(), 5);
77   EXPECT_EQ(exp.size.Get(), 3u);
78   EXPECT_EQ(exp.ping.Get(), false);
79   EXPECT_EQ(exp.hash.Get(), "a80");
80 }
TEST(FieldTrialParserTest,CanHandleMixedInput)81 TEST(FieldTrialParserTest, CanHandleMixedInput) {
82   DummyExperiment exp("p:true,h:,Enabled");
83   EXPECT_TRUE(exp.enabled.Get());
84   EXPECT_EQ(exp.factor.Get(), 0.5);
85   EXPECT_EQ(exp.retries.Get(), 5);
86   EXPECT_EQ(exp.size.Get(), 3u);
87   EXPECT_EQ(exp.ping.Get(), true);
88   EXPECT_EQ(exp.hash.Get(), "");
89 }
TEST(FieldTrialParserTest,ParsesDoubleParameter)90 TEST(FieldTrialParserTest, ParsesDoubleParameter) {
91   FieldTrialParameter<double> double_param("f", 0.0);
92   ParseFieldTrial({&double_param}, "f:45%");
93   EXPECT_EQ(double_param.Get(), 0.45);
94   ParseFieldTrial({&double_param}, "f:34 %");
95   EXPECT_EQ(double_param.Get(), 0.34);
96   ParseFieldTrial({&double_param}, "f:0.67");
97   EXPECT_EQ(double_param.Get(), 0.67);
98 }
TEST(FieldTrialParserTest,IgnoresNewKey)99 TEST(FieldTrialParserTest, IgnoresNewKey) {
100   DummyExperiment exp("Disabled,r:-11,foo");
101   EXPECT_FALSE(exp.enabled.Get());
102   EXPECT_EQ(exp.factor.Get(), 0.5);
103   EXPECT_EQ(exp.retries.Get(), -11);
104 }
TEST(FieldTrialParserTest,IgnoresInvalid)105 TEST(FieldTrialParserTest, IgnoresInvalid) {
106   DummyExperiment exp("Enabled,f,p:,r:%,,s:-1,:foo,h");
107   EXPECT_TRUE(exp.enabled.Get());
108   EXPECT_EQ(exp.factor.Get(), 0.5);
109   EXPECT_EQ(exp.retries.Get(), 5);
110   EXPECT_EQ(exp.size.Get(), 3u);
111   EXPECT_EQ(exp.ping.Get(), false);
112   EXPECT_EQ(exp.hash.Get(), "a80");
113 }
TEST(FieldTrialParserTest,IgnoresOutOfRange)114 TEST(FieldTrialParserTest, IgnoresOutOfRange) {
115   FieldTrialConstrained<double> low("low", 10, absl::nullopt, 100);
116   FieldTrialConstrained<double> high("high", 10, 5, absl::nullopt);
117   ParseFieldTrial({&low, &high}, "low:1000,high:0");
118   EXPECT_EQ(low.Get(), 10);
119   EXPECT_EQ(high.Get(), 10);
120   ParseFieldTrial({&low, &high}, "low:inf,high:nan");
121   EXPECT_EQ(low.Get(), 10);
122   EXPECT_EQ(high.Get(), 10);
123   ParseFieldTrial({&low, &high}, "low:20,high:20");
124   EXPECT_EQ(low.Get(), 20);
125   EXPECT_EQ(high.Get(), 20);
126 
127   FieldTrialConstrained<unsigned> size("size", 5, 1, 10);
128   ParseFieldTrial({&size}, "size:0");
129   EXPECT_EQ(size.Get(), 5u);
130 }
TEST(FieldTrialParserTest,ReadsValuesFromFieldWithoutKey)131 TEST(FieldTrialParserTest, ReadsValuesFromFieldWithoutKey) {
132   FieldTrialFlag enabled("Enabled");
133   FieldTrialParameter<int> req("", 10);
134   ParseFieldTrial({&enabled, &req}, "Enabled,20");
135   EXPECT_EQ(req.Get(), 20);
136   ParseFieldTrial({&req}, "30");
137   EXPECT_EQ(req.Get(), 30);
138 }
TEST(FieldTrialParserTest,ParsesOptionalParameters)139 TEST(FieldTrialParserTest, ParsesOptionalParameters) {
140   FieldTrialOptional<int> max_count("c", absl::nullopt);
141   ParseFieldTrial({&max_count}, "");
142   EXPECT_FALSE(max_count.GetOptional().has_value());
143   ParseFieldTrial({&max_count}, "c:10");
144   EXPECT_EQ(max_count.GetOptional().value(), 10);
145   ParseFieldTrial({&max_count}, "c");
146   EXPECT_FALSE(max_count.GetOptional().has_value());
147   ParseFieldTrial({&max_count}, "c:20");
148   EXPECT_EQ(max_count.GetOptional().value(), 20);
149   ParseFieldTrial({&max_count}, "c:");
150   EXPECT_EQ(max_count.GetOptional().value(), 20);
151 
152   FieldTrialOptional<unsigned> max_size("c", absl::nullopt);
153   ParseFieldTrial({&max_size}, "");
154   EXPECT_FALSE(max_size.GetOptional().has_value());
155   ParseFieldTrial({&max_size}, "c:10");
156   EXPECT_EQ(max_size.GetOptional().value(), 10u);
157   ParseFieldTrial({&max_size}, "c");
158   EXPECT_FALSE(max_size.GetOptional().has_value());
159   ParseFieldTrial({&max_size}, "c:20");
160   EXPECT_EQ(max_size.GetOptional().value(), 20u);
161 
162   FieldTrialOptional<std::string> optional_string("s", std::string("ab"));
163   ParseFieldTrial({&optional_string}, "s:");
164   EXPECT_EQ(optional_string.GetOptional().value(), "");
165   ParseFieldTrial({&optional_string}, "s");
166   EXPECT_FALSE(optional_string.GetOptional().has_value());
167 }
TEST(FieldTrialParserTest,ParsesCustomEnumParameter)168 TEST(FieldTrialParserTest, ParsesCustomEnumParameter) {
169   FieldTrialEnum<CustomEnum> my_enum("e", CustomEnum::kDefault,
170                                      {{"default", CustomEnum::kDefault},
171                                       {"red", CustomEnum::kRed},
172                                       {"blue", CustomEnum::kBlue}});
173   ParseFieldTrial({&my_enum}, "");
174   EXPECT_EQ(my_enum.Get(), CustomEnum::kDefault);
175   ParseFieldTrial({&my_enum}, "e:red");
176   EXPECT_EQ(my_enum.Get(), CustomEnum::kRed);
177   ParseFieldTrial({&my_enum}, "e:2");
178   EXPECT_EQ(my_enum.Get(), CustomEnum::kBlue);
179   ParseFieldTrial({&my_enum}, "e:5");
180   EXPECT_EQ(my_enum.Get(), CustomEnum::kBlue);
181 }
182 
183 }  // namespace webrtc
184