|  | /* | 
|  | *  Copyright 2019 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  |  | 
|  | #include "rtc_base/experiments/field_trial_list.h" | 
|  |  | 
|  | #include "absl/strings/string_view.h" | 
|  | #include "rtc_base/gunit.h" | 
|  | #include "test/gmock.h" | 
|  |  | 
|  | using testing::ElementsAre; | 
|  | using testing::IsEmpty; | 
|  |  | 
|  | namespace webrtc { | 
|  |  | 
|  | struct Garment { | 
|  | int price = 0; | 
|  | std::string color = ""; | 
|  | bool has_glitter = false; | 
|  |  | 
|  | // Only needed for testing. | 
|  | Garment() = default; | 
|  | Garment(int p, absl::string_view c, bool g) | 
|  | : price(p), color(c), has_glitter(g) {} | 
|  |  | 
|  | bool operator==(const Garment& other) const { | 
|  | return price == other.price && color == other.color && | 
|  | has_glitter == other.has_glitter; | 
|  | } | 
|  | }; | 
|  |  | 
|  | TEST(FieldTrialListTest, ParsesListParameter) { | 
|  | FieldTrialList<int> my_list("l", {5}); | 
|  | EXPECT_THAT(my_list.Get(), ElementsAre(5)); | 
|  | // If one element is invalid the list is unchanged. | 
|  | ParseFieldTrial({&my_list}, "l:1|2|hat"); | 
|  | EXPECT_THAT(my_list.Get(), ElementsAre(5)); | 
|  | ParseFieldTrial({&my_list}, "l"); | 
|  | EXPECT_THAT(my_list.Get(), IsEmpty()); | 
|  | ParseFieldTrial({&my_list}, "l:1|2|3"); | 
|  | EXPECT_THAT(my_list.Get(), ElementsAre(1, 2, 3)); | 
|  | ParseFieldTrial({&my_list}, "l:-1"); | 
|  | EXPECT_THAT(my_list.Get(), ElementsAre(-1)); | 
|  |  | 
|  | FieldTrialList<std::string> 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<Garment> my_list( | 
|  | {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), | 
|  | FieldTrialStructMember("price", [](Garment* g) { return &g->price; }), | 
|  | FieldTrialStructMember("has_glitter", | 
|  | [](Garment* g) { return &g->has_glitter; })}, | 
|  | {{1, "blue", false}, {2, "red", true}}); | 
|  |  | 
|  | ParseFieldTrial({&my_list}, | 
|  | "color:mauve|red|gold," | 
|  | "price:10|20|30," | 
|  | "has_glitter:1|0|1," | 
|  | "other_param:asdf"); | 
|  |  | 
|  | ASSERT_THAT(my_list.Get(), | 
|  | ElementsAre(Garment{10, "mauve", true}, Garment{20, "red", false}, | 
|  | Garment{30, "gold", true})); | 
|  | } | 
|  |  | 
|  | // One FieldTrialList has the wrong length, so we use the user-provided default | 
|  | // list. | 
|  | TEST(FieldTrialListTest, StructListKeepsDefaultWithMismatchingLength) { | 
|  | FieldTrialStructList<Garment> my_list( | 
|  | {FieldTrialStructMember("wrong_length", | 
|  | [](Garment* g) { return &g->color; }), | 
|  | FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, | 
|  | {{1, "blue", true}, {2, "red", false}}); | 
|  |  | 
|  | ParseFieldTrial({&my_list}, | 
|  | "wrong_length:mauve|magenta|chartreuse|indigo," | 
|  | "garment:hat|hat|crown," | 
|  | "price:10|20|30"); | 
|  |  | 
|  | ASSERT_THAT(my_list.Get(), | 
|  | ElementsAre(Garment{1, "blue", true}, Garment{2, "red", false})); | 
|  | } | 
|  |  | 
|  | // 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<Garment> my_list( | 
|  | {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), | 
|  | FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, | 
|  | {{1, "blue", true}, {2, "red", false}}); | 
|  |  | 
|  | ParseFieldTrial({&my_list}, "price:10|20|30"); | 
|  |  | 
|  | ASSERT_THAT(my_list.Get(), | 
|  | ElementsAre(Garment{10, "", false}, Garment{20, "", false}, | 
|  | Garment{30, "", false})); | 
|  | } | 
|  |  | 
|  | // The user haven't provided values for any lists, so we use the default list. | 
|  | TEST(FieldTrialListTest, StructListUsesDefaultListWithoutValues) { | 
|  | FieldTrialStructList<Garment> my_list( | 
|  | {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), | 
|  | FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, | 
|  | {{1, "blue", true}, {2, "red", false}}); | 
|  |  | 
|  | ParseFieldTrial({&my_list}, ""); | 
|  |  | 
|  | ASSERT_THAT(my_list.Get(), | 
|  | ElementsAre(Garment{1, "blue", true}, Garment{2, "red", false})); | 
|  | } | 
|  |  | 
|  | // Some lists are provided and all are empty, so we return a empty list. | 
|  | TEST(FieldTrialListTest, StructListHandlesEmptyLists) { | 
|  | FieldTrialStructList<Garment> my_list( | 
|  | {FieldTrialStructMember("color", [](Garment* g) { return &g->color; }), | 
|  | FieldTrialStructMember("price", [](Garment* g) { return &g->price; })}, | 
|  | {{1, "blue", true}, {2, "red", false}}); | 
|  |  | 
|  | ParseFieldTrial({&my_list}, "color,price"); | 
|  |  | 
|  | ASSERT_EQ(my_list.Get().size(), 0u); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |