1 // Copyright 2017 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_params.h"
6
7 #include "base/feature_list.h"
8 #include "base/macros.h"
9 #include "base/metrics/field_trial.h"
10 #include "base/metrics/field_trial_param_associator.h"
11 #include "base/test/scoped_feature_list.h"
12 #include "testing/gtest/include/gtest/gtest.h"
13
14 namespace base {
15
16 namespace {
17
18 // Call FieldTrialList::FactoryGetFieldTrial() with a future expiry date.
CreateFieldTrial(const std::string & trial_name,int total_probability,const std::string & default_group_name,int * default_group_number)19 scoped_refptr<FieldTrial> CreateFieldTrial(
20 const std::string& trial_name,
21 int total_probability,
22 const std::string& default_group_name,
23 int* default_group_number) {
24 return FieldTrialList::FactoryGetFieldTrial(
25 trial_name, total_probability, default_group_name,
26 FieldTrialList::kNoExpirationYear, 1, 1, FieldTrial::SESSION_RANDOMIZED,
27 default_group_number);
28 }
29
30 } // namespace
31
32 class FieldTrialParamsTest : public ::testing::Test {
33 public:
FieldTrialParamsTest()34 FieldTrialParamsTest() : field_trial_list_(nullptr) {}
35
~FieldTrialParamsTest()36 ~FieldTrialParamsTest() override {
37 // Ensure that the maps are cleared between tests, since they are stored as
38 // process singletons.
39 FieldTrialParamAssociator::GetInstance()->ClearAllParamsForTesting();
40 }
41
CreateFeatureWithTrial(const Feature & feature,FeatureList::OverrideState override_state,FieldTrial * trial)42 void CreateFeatureWithTrial(const Feature& feature,
43 FeatureList::OverrideState override_state,
44 FieldTrial* trial) {
45 std::unique_ptr<FeatureList> feature_list(new FeatureList);
46 feature_list->RegisterFieldTrialOverride(feature.name, override_state,
47 trial);
48 scoped_feature_list_.InitWithFeatureList(std::move(feature_list));
49 }
50
51 private:
52 FieldTrialList field_trial_list_;
53 test::ScopedFeatureList scoped_feature_list_;
54
55 DISALLOW_COPY_AND_ASSIGN(FieldTrialParamsTest);
56 };
57
TEST_F(FieldTrialParamsTest,AssociateFieldTrialParams)58 TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams) {
59 const std::string kTrialName = "AssociateFieldTrialParams";
60
61 {
62 std::map<std::string, std::string> params;
63 params["a"] = "10";
64 params["b"] = "test";
65 ASSERT_TRUE(AssociateFieldTrialParams(kTrialName, "A", params));
66 }
67 {
68 std::map<std::string, std::string> params;
69 params["a"] = "5";
70 ASSERT_TRUE(AssociateFieldTrialParams(kTrialName, "B", params));
71 }
72
73 FieldTrialList::CreateFieldTrial(kTrialName, "B");
74 EXPECT_EQ("5", GetFieldTrialParamValue(kTrialName, "a"));
75 EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "b"));
76 EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x"));
77
78 std::map<std::string, std::string> params;
79 EXPECT_TRUE(GetFieldTrialParams(kTrialName, ¶ms));
80 EXPECT_EQ(1U, params.size());
81 EXPECT_EQ("5", params["a"]);
82 }
83
TEST_F(FieldTrialParamsTest,AssociateFieldTrialParams_Fail)84 TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams_Fail) {
85 const std::string kTrialName = "AssociateFieldTrialParams_Fail";
86 const std::string kGroupName = "A";
87
88 std::map<std::string, std::string> params;
89 params["a"] = "10";
90 ASSERT_TRUE(AssociateFieldTrialParams(kTrialName, kGroupName, params));
91 params["a"] = "1";
92 params["b"] = "2";
93 ASSERT_FALSE(AssociateFieldTrialParams(kTrialName, kGroupName, params));
94
95 FieldTrialList::CreateFieldTrial(kTrialName, kGroupName);
96 EXPECT_EQ("10", GetFieldTrialParamValue(kTrialName, "a"));
97 EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "b"));
98 }
99
TEST_F(FieldTrialParamsTest,AssociateFieldTrialParams_TrialActiveFail)100 TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams_TrialActiveFail) {
101 const std::string kTrialName = "AssociateFieldTrialParams_TrialActiveFail";
102 FieldTrialList::CreateFieldTrial(kTrialName, "A");
103 ASSERT_EQ("A", FieldTrialList::FindFullName(kTrialName));
104
105 std::map<std::string, std::string> params;
106 params["a"] = "10";
107 EXPECT_FALSE(AssociateFieldTrialParams(kTrialName, "B", params));
108 EXPECT_FALSE(AssociateFieldTrialParams(kTrialName, "A", params));
109 }
110
TEST_F(FieldTrialParamsTest,AssociateFieldTrialParams_DoesntActivateTrial)111 TEST_F(FieldTrialParamsTest, AssociateFieldTrialParams_DoesntActivateTrial) {
112 const std::string kTrialName =
113 "AssociateFieldTrialParams_DoesntActivateTrial";
114
115 ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
116 scoped_refptr<FieldTrial> trial(
117 CreateFieldTrial(kTrialName, 100, "A", nullptr));
118 ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
119
120 std::map<std::string, std::string> params;
121 params["a"] = "10";
122 EXPECT_TRUE(AssociateFieldTrialParams(kTrialName, "A", params));
123 ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
124 }
125
TEST_F(FieldTrialParamsTest,GetFieldTrialParams_NoTrial)126 TEST_F(FieldTrialParamsTest, GetFieldTrialParams_NoTrial) {
127 const std::string kTrialName = "GetFieldTrialParams_NoParams";
128
129 std::map<std::string, std::string> params;
130 EXPECT_FALSE(GetFieldTrialParams(kTrialName, ¶ms));
131 EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x"));
132 EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "y"));
133 }
134
TEST_F(FieldTrialParamsTest,GetFieldTrialParams_NoParams)135 TEST_F(FieldTrialParamsTest, GetFieldTrialParams_NoParams) {
136 const std::string kTrialName = "GetFieldTrialParams_NoParams";
137
138 FieldTrialList::CreateFieldTrial(kTrialName, "A");
139
140 std::map<std::string, std::string> params;
141 EXPECT_FALSE(GetFieldTrialParams(kTrialName, ¶ms));
142 EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x"));
143 EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "y"));
144 }
145
TEST_F(FieldTrialParamsTest,GetFieldTrialParams_ActivatesTrial)146 TEST_F(FieldTrialParamsTest, GetFieldTrialParams_ActivatesTrial) {
147 const std::string kTrialName = "GetFieldTrialParams_ActivatesTrial";
148
149 ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
150 scoped_refptr<FieldTrial> trial(
151 CreateFieldTrial(kTrialName, 100, "A", nullptr));
152 ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
153
154 std::map<std::string, std::string> params;
155 EXPECT_FALSE(GetFieldTrialParams(kTrialName, ¶ms));
156 ASSERT_TRUE(FieldTrialList::IsTrialActive(kTrialName));
157 }
158
TEST_F(FieldTrialParamsTest,GetFieldTrialParamValue_ActivatesTrial)159 TEST_F(FieldTrialParamsTest, GetFieldTrialParamValue_ActivatesTrial) {
160 const std::string kTrialName = "GetFieldTrialParamValue_ActivatesTrial";
161
162 ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
163 scoped_refptr<FieldTrial> trial(
164 CreateFieldTrial(kTrialName, 100, "A", nullptr));
165 ASSERT_FALSE(FieldTrialList::IsTrialActive(kTrialName));
166
167 std::map<std::string, std::string> params;
168 EXPECT_EQ(std::string(), GetFieldTrialParamValue(kTrialName, "x"));
169 ASSERT_TRUE(FieldTrialList::IsTrialActive(kTrialName));
170 }
171
TEST_F(FieldTrialParamsTest,GetFieldTrialParamsByFeature)172 TEST_F(FieldTrialParamsTest, GetFieldTrialParamsByFeature) {
173 const std::string kTrialName = "GetFieldTrialParamsByFeature";
174 const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
175
176 std::map<std::string, std::string> params;
177 params["x"] = "1";
178 AssociateFieldTrialParams(kTrialName, "A", params);
179 scoped_refptr<FieldTrial> trial(
180 CreateFieldTrial(kTrialName, 100, "A", nullptr));
181
182 CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
183 trial.get());
184
185 std::map<std::string, std::string> actualParams;
186 EXPECT_TRUE(GetFieldTrialParamsByFeature(kFeature, &actualParams));
187 EXPECT_EQ(params, actualParams);
188 }
189
TEST_F(FieldTrialParamsTest,GetFieldTrialParamValueByFeature)190 TEST_F(FieldTrialParamsTest, GetFieldTrialParamValueByFeature) {
191 const std::string kTrialName = "GetFieldTrialParamsByFeature";
192 const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
193
194 std::map<std::string, std::string> params;
195 params["x"] = "1";
196 AssociateFieldTrialParams(kTrialName, "A", params);
197 scoped_refptr<FieldTrial> trial(
198 CreateFieldTrial(kTrialName, 100, "A", nullptr));
199
200 CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
201 trial.get());
202
203 std::map<std::string, std::string> actualParams;
204 EXPECT_EQ(params["x"], GetFieldTrialParamValueByFeature(kFeature, "x"));
205 }
206
TEST_F(FieldTrialParamsTest,GetFieldTrialParamsByFeature_Disable)207 TEST_F(FieldTrialParamsTest, GetFieldTrialParamsByFeature_Disable) {
208 const std::string kTrialName = "GetFieldTrialParamsByFeature";
209 const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
210
211 std::map<std::string, std::string> params;
212 params["x"] = "1";
213 AssociateFieldTrialParams(kTrialName, "A", params);
214 scoped_refptr<FieldTrial> trial(
215 CreateFieldTrial(kTrialName, 100, "A", nullptr));
216
217 CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_DISABLE_FEATURE,
218 trial.get());
219
220 std::map<std::string, std::string> actualParams;
221 EXPECT_FALSE(GetFieldTrialParamsByFeature(kFeature, &actualParams));
222 }
223
TEST_F(FieldTrialParamsTest,GetFieldTrialParamValueByFeature_Disable)224 TEST_F(FieldTrialParamsTest, GetFieldTrialParamValueByFeature_Disable) {
225 const std::string kTrialName = "GetFieldTrialParamsByFeature";
226 const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
227
228 std::map<std::string, std::string> params;
229 params["x"] = "1";
230 AssociateFieldTrialParams(kTrialName, "A", params);
231 scoped_refptr<FieldTrial> trial(
232 CreateFieldTrial(kTrialName, 100, "A", nullptr));
233
234 CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_DISABLE_FEATURE,
235 trial.get());
236
237 std::map<std::string, std::string> actualParams;
238 EXPECT_EQ(std::string(), GetFieldTrialParamValueByFeature(kFeature, "x"));
239 }
240
TEST_F(FieldTrialParamsTest,FeatureParamString)241 TEST_F(FieldTrialParamsTest, FeatureParamString) {
242 const std::string kTrialName = "GetFieldTrialParamsByFeature";
243
244 static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
245 static const FeatureParam<std::string> a{&kFeature, "a", "default"};
246 static const FeatureParam<std::string> b{&kFeature, "b", ""};
247 static const FeatureParam<std::string> c{&kFeature, "c", "default"};
248 static const FeatureParam<std::string> d{&kFeature, "d", ""};
249 static const FeatureParam<std::string> e{&kFeature, "e", "default"};
250 static const FeatureParam<std::string> f{&kFeature, "f", ""};
251
252 std::map<std::string, std::string> params;
253 params["a"] = "";
254 params["b"] = "non-default";
255 params["c"] = "non-default";
256 params["d"] = "";
257 // "e" is not registered
258 // "f" is not registered
259 AssociateFieldTrialParams(kTrialName, "A", params);
260 scoped_refptr<FieldTrial> trial(
261 CreateFieldTrial(kTrialName, 100, "A", nullptr));
262
263 CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
264 trial.get());
265
266 EXPECT_EQ("default", a.Get()); // empty
267 EXPECT_EQ("non-default", b.Get());
268 EXPECT_EQ("non-default", c.Get());
269 EXPECT_EQ("", d.Get()); // empty
270 EXPECT_EQ("default", e.Get()); // not registered
271 EXPECT_EQ("", f.Get()); // not registered
272 }
273
TEST_F(FieldTrialParamsTest,FeatureParamInt)274 TEST_F(FieldTrialParamsTest, FeatureParamInt) {
275 const std::string kTrialName = "GetFieldTrialParamsByFeature";
276
277 static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
278 static const FeatureParam<int> a{&kFeature, "a", 0};
279 static const FeatureParam<int> b{&kFeature, "b", 0};
280 static const FeatureParam<int> c{&kFeature, "c", 0};
281 static const FeatureParam<int> d{&kFeature, "d", 0};
282 static const FeatureParam<int> e{&kFeature, "e", 0};
283
284 std::map<std::string, std::string> params;
285 params["a"] = "1";
286 params["b"] = "1.5";
287 params["c"] = "foo";
288 params["d"] = "";
289 // "e" is not registered
290 AssociateFieldTrialParams(kTrialName, "A", params);
291 scoped_refptr<FieldTrial> trial(
292 CreateFieldTrial(kTrialName, 100, "A", nullptr));
293
294 CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
295 trial.get());
296
297 EXPECT_EQ(1, GetFieldTrialParamByFeatureAsInt(kFeature, "a", 0));
298 EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "b", 0)); // invalid
299 EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "c", 0)); // invalid
300 EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "d", 0)); // empty
301 EXPECT_EQ(0, GetFieldTrialParamByFeatureAsInt(kFeature, "e", 0)); // empty
302
303 EXPECT_EQ(1, a.Get());
304 EXPECT_EQ(0, b.Get()); // invalid
305 EXPECT_EQ(0, c.Get()); // invalid
306 EXPECT_EQ(0, d.Get()); // empty
307 EXPECT_EQ(0, e.Get()); // empty
308 }
309
TEST_F(FieldTrialParamsTest,FeatureParamDouble)310 TEST_F(FieldTrialParamsTest, FeatureParamDouble) {
311 const std::string kTrialName = "GetFieldTrialParamsByFeature";
312
313 static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
314 static const FeatureParam<double> a{&kFeature, "a", 0.0};
315 static const FeatureParam<double> b{&kFeature, "b", 0.0};
316 static const FeatureParam<double> c{&kFeature, "c", 0.0};
317 static const FeatureParam<double> d{&kFeature, "d", 0.0};
318 static const FeatureParam<double> e{&kFeature, "e", 0.0};
319 static const FeatureParam<double> f{&kFeature, "f", 0.0};
320
321 std::map<std::string, std::string> params;
322 params["a"] = "1";
323 params["b"] = "1.5";
324 params["c"] = "1.0e-10";
325 params["d"] = "foo";
326 params["e"] = "";
327 // "f" is not registered
328 AssociateFieldTrialParams(kTrialName, "A", params);
329 scoped_refptr<FieldTrial> trial(
330 CreateFieldTrial(kTrialName, 100, "A", nullptr));
331
332 CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
333 trial.get());
334
335 EXPECT_EQ(1, GetFieldTrialParamByFeatureAsDouble(kFeature, "a", 0));
336 EXPECT_EQ(1.5, GetFieldTrialParamByFeatureAsDouble(kFeature, "b", 0));
337 EXPECT_EQ(1.0e-10, GetFieldTrialParamByFeatureAsDouble(kFeature, "c", 0));
338 EXPECT_EQ(0,
339 GetFieldTrialParamByFeatureAsDouble(kFeature, "d", 0)); // invalid
340 EXPECT_EQ(0, GetFieldTrialParamByFeatureAsDouble(kFeature, "e", 0)); // empty
341 EXPECT_EQ(0, GetFieldTrialParamByFeatureAsDouble(kFeature, "f", 0)); // empty
342
343 EXPECT_EQ(1, a.Get());
344 EXPECT_EQ(1.5, b.Get());
345 EXPECT_EQ(1.0e-10, c.Get());
346 EXPECT_EQ(0, d.Get()); // invalid
347 EXPECT_EQ(0, e.Get()); // empty
348 EXPECT_EQ(0, f.Get()); // empty
349 }
350
TEST_F(FieldTrialParamsTest,FeatureParamBool)351 TEST_F(FieldTrialParamsTest, FeatureParamBool) {
352 const std::string kTrialName = "GetFieldTrialParamsByFeature";
353
354 static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
355 static const FeatureParam<bool> a{&kFeature, "a", false};
356 static const FeatureParam<bool> b{&kFeature, "b", true};
357 static const FeatureParam<bool> c{&kFeature, "c", false};
358 static const FeatureParam<bool> d{&kFeature, "d", true};
359 static const FeatureParam<bool> e{&kFeature, "e", true};
360 static const FeatureParam<bool> f{&kFeature, "f", true};
361
362 std::map<std::string, std::string> params;
363 params["a"] = "true";
364 params["b"] = "false";
365 params["c"] = "1";
366 params["d"] = "False";
367 params["e"] = "";
368 // "f" is not registered
369 AssociateFieldTrialParams(kTrialName, "A", params);
370 scoped_refptr<FieldTrial> trial(
371 CreateFieldTrial(kTrialName, 100, "A", nullptr));
372
373 CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
374 trial.get());
375
376 EXPECT_TRUE(a.Get());
377 EXPECT_FALSE(b.Get());
378 EXPECT_FALSE(c.Get()); // invalid
379 EXPECT_TRUE(d.Get()); // invalid
380 EXPECT_TRUE(e.Get()); // empty
381 EXPECT_TRUE(f.Get()); // empty
382 }
383
384 enum Hand { ROCK, PAPER, SCISSORS };
385
TEST_F(FieldTrialParamsTest,FeatureParamEnum)386 TEST_F(FieldTrialParamsTest, FeatureParamEnum) {
387 const std::string kTrialName = "GetFieldTrialParamsByFeature";
388
389 static const FeatureParam<Hand>::Option hands[] = {
390 {ROCK, "rock"}, {PAPER, "paper"}, {SCISSORS, "scissors"}};
391 static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
392 static const FeatureParam<Hand> a{&kFeature, "a", ROCK, &hands};
393 static const FeatureParam<Hand> b{&kFeature, "b", ROCK, &hands};
394 static const FeatureParam<Hand> c{&kFeature, "c", ROCK, &hands};
395 static const FeatureParam<Hand> d{&kFeature, "d", ROCK, &hands};
396 static const FeatureParam<Hand> e{&kFeature, "e", PAPER, &hands};
397 static const FeatureParam<Hand> f{&kFeature, "f", SCISSORS, &hands};
398
399 std::map<std::string, std::string> params;
400 params["a"] = "rock";
401 params["b"] = "paper";
402 params["c"] = "scissors";
403 params["d"] = "lizard";
404 params["e"] = "";
405 // "f" is not registered
406 AssociateFieldTrialParams(kTrialName, "A", params);
407 scoped_refptr<FieldTrial> trial(
408 CreateFieldTrial(kTrialName, 100, "A", nullptr));
409
410 CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
411 trial.get());
412
413 EXPECT_EQ(ROCK, a.Get());
414 EXPECT_EQ(PAPER, b.Get());
415 EXPECT_EQ(SCISSORS, c.Get());
416 EXPECT_EQ(ROCK, d.Get()); // invalid
417 EXPECT_EQ(PAPER, e.Get()); // invalid/empty
418 EXPECT_EQ(SCISSORS, f.Get()); // not registered
419 }
420
421 enum class UI { ONE_D, TWO_D, THREE_D };
422
TEST_F(FieldTrialParamsTest,FeatureParamEnumClass)423 TEST_F(FieldTrialParamsTest, FeatureParamEnumClass) {
424 const std::string kTrialName = "GetFieldTrialParamsByFeature";
425
426 static const FeatureParam<UI>::Option uis[] = {
427 {UI::ONE_D, "1d"}, {UI::TWO_D, "2d"}, {UI::THREE_D, "3d"}};
428 static const Feature kFeature{"TestFeature", FEATURE_DISABLED_BY_DEFAULT};
429 static const FeatureParam<UI> a{&kFeature, "a", UI::ONE_D, &uis};
430 static const FeatureParam<UI> b{&kFeature, "b", UI::ONE_D, &uis};
431 static const FeatureParam<UI> c{&kFeature, "c", UI::ONE_D, &uis};
432 static const FeatureParam<UI> d{&kFeature, "d", UI::ONE_D, &uis};
433 static const FeatureParam<UI> e{&kFeature, "e", UI::TWO_D, &uis};
434 static const FeatureParam<UI> f{&kFeature, "f", UI::THREE_D, &uis};
435
436 std::map<std::string, std::string> params;
437 params["a"] = "1d";
438 params["b"] = "2d";
439 params["c"] = "3d";
440 params["d"] = "4d";
441 params["e"] = "";
442 // "f" is not registered
443 AssociateFieldTrialParams(kTrialName, "A", params);
444 scoped_refptr<FieldTrial> trial(
445 CreateFieldTrial(kTrialName, 100, "A", nullptr));
446
447 CreateFeatureWithTrial(kFeature, FeatureList::OVERRIDE_ENABLE_FEATURE,
448 trial.get());
449
450 EXPECT_EQ(UI::ONE_D, a.Get());
451 EXPECT_EQ(UI::TWO_D, b.Get());
452 EXPECT_EQ(UI::THREE_D, c.Get());
453 EXPECT_EQ(UI::ONE_D, d.Get()); // invalid
454 EXPECT_EQ(UI::TWO_D, e.Get()); // invalid/empty
455 EXPECT_EQ(UI::THREE_D, f.Get()); // not registered
456 }
457
458 } // namespace base
459