1 /*
2 * Copyright 2019 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
11 #include "rtc_base/experiments/field_trial_list.h"
12
13 #include "absl/strings/string_view.h"
14 #include "rtc_base/gunit.h"
15 #include "test/gmock.h"
16
17 using testing::ElementsAre;
18 using testing::IsEmpty;
19
20 namespace webrtc {
21
22 struct Garment {
23 int price = 0;
24 std::string color = "";
25 bool has_glitter = false;
26
27 // Only needed for testing.
28 Garment() = default;
Garmentwebrtc::Garment29 Garment(int p, absl::string_view c, bool g)
30 : price(p), color(c), has_glitter(g) {}
31
operator ==webrtc::Garment32 bool operator==(const Garment& other) const {
33 return price == other.price && color == other.color &&
34 has_glitter == other.has_glitter;
35 }
36 };
37
TEST(FieldTrialListTest,ParsesListParameter)38 TEST(FieldTrialListTest, ParsesListParameter) {
39 FieldTrialList<int> my_list("l", {5});
40 EXPECT_THAT(my_list.Get(), ElementsAre(5));
41 // If one element is invalid the list is unchanged.
42 ParseFieldTrial({&my_list}, "l:1|2|hat");
43 EXPECT_THAT(my_list.Get(), ElementsAre(5));
44 ParseFieldTrial({&my_list}, "l");
45 EXPECT_THAT(my_list.Get(), IsEmpty());
46 ParseFieldTrial({&my_list}, "l:1|2|3");
47 EXPECT_THAT(my_list.Get(), ElementsAre(1, 2, 3));
48 ParseFieldTrial({&my_list}, "l:-1");
49 EXPECT_THAT(my_list.Get(), ElementsAre(-1));
50
51 FieldTrialList<std::string> another_list("l", {"hat"});
52 EXPECT_THAT(another_list.Get(), ElementsAre("hat"));
53 ParseFieldTrial({&another_list}, "l");
54 EXPECT_THAT(another_list.Get(), IsEmpty());
55 ParseFieldTrial({&another_list}, "l:");
56 EXPECT_THAT(another_list.Get(), ElementsAre(""));
57 ParseFieldTrial({&another_list}, "l:scarf|hat|mittens");
58 EXPECT_THAT(another_list.Get(), ElementsAre("scarf", "hat", "mittens"));
59 ParseFieldTrial({&another_list}, "l:scarf");
60 EXPECT_THAT(another_list.Get(), ElementsAre("scarf"));
61 }
62
63 // Normal usage.
TEST(FieldTrialListTest,ParsesStructList)64 TEST(FieldTrialListTest, ParsesStructList) {
65 FieldTrialStructList<Garment> my_list(
66 {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }),
67 FieldTrialStructMember("price", [](Garment* g) { return &g->price; }),
68 FieldTrialStructMember("has_glitter",
69 [](Garment* g) { return &g->has_glitter; })},
70 {{1, "blue", false}, {2, "red", true}});
71
72 ParseFieldTrial({&my_list},
73 "color:mauve|red|gold,"
74 "price:10|20|30,"
75 "has_glitter:1|0|1,"
76 "other_param:asdf");
77
78 ASSERT_THAT(my_list.Get(),
79 ElementsAre(Garment{10, "mauve", true}, Garment{20, "red", false},
80 Garment{30, "gold", true}));
81 }
82
83 // One FieldTrialList has the wrong length, so we use the user-provided default
84 // list.
TEST(FieldTrialListTest,StructListKeepsDefaultWithMismatchingLength)85 TEST(FieldTrialListTest, StructListKeepsDefaultWithMismatchingLength) {
86 FieldTrialStructList<Garment> my_list(
87 {FieldTrialStructMember("wrong_length",
88 [](Garment* g) { return &g->color; }),
89 FieldTrialStructMember("price", [](Garment* g) { return &g->price; })},
90 {{1, "blue", true}, {2, "red", false}});
91
92 ParseFieldTrial({&my_list},
93 "wrong_length:mauve|magenta|chartreuse|indigo,"
94 "garment:hat|hat|crown,"
95 "price:10|20|30");
96
97 ASSERT_THAT(my_list.Get(),
98 ElementsAre(Garment{1, "blue", true}, Garment{2, "red", false}));
99 }
100
101 // One list is missing. We set the values we're given, and the others remain
102 // as whatever the Garment default constructor set them to.
TEST(FieldTrialListTest,StructListUsesDefaultForMissingList)103 TEST(FieldTrialListTest, StructListUsesDefaultForMissingList) {
104 FieldTrialStructList<Garment> my_list(
105 {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }),
106 FieldTrialStructMember("price", [](Garment* g) { return &g->price; })},
107 {{1, "blue", true}, {2, "red", false}});
108
109 ParseFieldTrial({&my_list}, "price:10|20|30");
110
111 ASSERT_THAT(my_list.Get(),
112 ElementsAre(Garment{10, "", false}, Garment{20, "", false},
113 Garment{30, "", false}));
114 }
115
116 // The user haven't provided values for any lists, so we use the default list.
TEST(FieldTrialListTest,StructListUsesDefaultListWithoutValues)117 TEST(FieldTrialListTest, StructListUsesDefaultListWithoutValues) {
118 FieldTrialStructList<Garment> my_list(
119 {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }),
120 FieldTrialStructMember("price", [](Garment* g) { return &g->price; })},
121 {{1, "blue", true}, {2, "red", false}});
122
123 ParseFieldTrial({&my_list}, "");
124
125 ASSERT_THAT(my_list.Get(),
126 ElementsAre(Garment{1, "blue", true}, Garment{2, "red", false}));
127 }
128
129 // Some lists are provided and all are empty, so we return a empty list.
TEST(FieldTrialListTest,StructListHandlesEmptyLists)130 TEST(FieldTrialListTest, StructListHandlesEmptyLists) {
131 FieldTrialStructList<Garment> my_list(
132 {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }),
133 FieldTrialStructMember("price", [](Garment* g) { return &g->price; })},
134 {{1, "blue", true}, {2, "red", false}});
135
136 ParseFieldTrial({&my_list}, "color,price");
137
138 ASSERT_EQ(my_list.Get().size(), 0u);
139 }
140
141 } // namespace webrtc
142