Add support for lists to the FieldTrialParser.
List elements are separated by a |. If the key is given without a : we treat that as a empty list. We also support parsing multiple lists as a list-of-structs, see the unit test for usage examples. Bug: webrtc:9346 Change-Id: I32d3ce612fef476b1c481c00a893d7fa2f339e92 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/130464 Reviewed-by: Sebastian Jansson <srte@webrtc.org> Commit-Queue: Jonas Olsson <jonasolsson@webrtc.org> Cr-Commit-Position: refs/heads/master@{#27560}
This commit is contained in:
parent
7ddef1af88
commit
97d84ef78e
@ -40,6 +40,8 @@ rtc_static_library("audio_allocation_settings") {
|
||||
|
||||
rtc_static_library("field_trial_parser") {
|
||||
sources = [
|
||||
"field_trial_list.cc",
|
||||
"field_trial_list.h",
|
||||
"field_trial_parser.cc",
|
||||
"field_trial_parser.h",
|
||||
"field_trial_units.cc",
|
||||
@ -51,6 +53,7 @@ rtc_static_library("field_trial_parser") {
|
||||
"../../api/units:time_delta",
|
||||
"../../rtc_base:checks",
|
||||
"../../rtc_base:logging",
|
||||
"../../rtc_base:stringutils",
|
||||
"//third_party/abseil-cpp/absl/types:optional",
|
||||
]
|
||||
}
|
||||
@ -151,6 +154,7 @@ if (rtc_include_tests) {
|
||||
|
||||
sources = [
|
||||
"cpu_speed_experiment_unittest.cc",
|
||||
"field_trial_list_unittest.cc",
|
||||
"field_trial_parser_unittest.cc",
|
||||
"field_trial_units_unittest.cc",
|
||||
"keyframe_interval_settings_unittest.cc",
|
||||
|
||||
57
rtc_base/experiments/field_trial_list.cc
Normal file
57
rtc_base/experiments/field_trial_list.cc
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
FieldTrialListBase::FieldTrialListBase(std::string key)
|
||||
: FieldTrialParameterInterface(key),
|
||||
failed_(false),
|
||||
parse_got_called_(false) {}
|
||||
|
||||
bool FieldTrialListBase::Failed() const {
|
||||
return failed_;
|
||||
}
|
||||
bool FieldTrialListBase::Used() const {
|
||||
return parse_got_called_;
|
||||
}
|
||||
|
||||
int FieldTrialListWrapper::Length() {
|
||||
return GetList()->Size();
|
||||
}
|
||||
bool FieldTrialListWrapper::Failed() {
|
||||
return GetList()->Failed();
|
||||
}
|
||||
bool FieldTrialListWrapper::Used() {
|
||||
return GetList()->Used();
|
||||
}
|
||||
|
||||
bool FieldTrialStructListBase::Parse(absl::optional<std::string> str_value) {
|
||||
RTC_NOTREACHED();
|
||||
return true;
|
||||
}
|
||||
|
||||
int FieldTrialStructListBase::ValidateAndGetLength() {
|
||||
int length = -1;
|
||||
for (std::unique_ptr<FieldTrialListWrapper>& list : sub_lists_) {
|
||||
if (list->Failed())
|
||||
return -1;
|
||||
else if (!list->Used())
|
||||
continue;
|
||||
else if (length == -1)
|
||||
length = list->Length();
|
||||
else if (length != list->Length())
|
||||
return -1;
|
||||
}
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
224
rtc_base/experiments/field_trial_list.h
Normal file
224
rtc_base/experiments/field_trial_list.h
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
* Copyright 2018 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.
|
||||
*/
|
||||
#ifndef RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_
|
||||
#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_
|
||||
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rtc_base/experiments/field_trial_parser.h"
|
||||
#include "rtc_base/string_encode.h"
|
||||
|
||||
// List support for field trial strings. FieldTrialList and FieldTrialStructList
|
||||
// are used similarly to the other FieldTrialParameters, but take a variable
|
||||
// number of parameters. A FieldTrialList<T> parses a |-delimeted string into a
|
||||
// list of T, using ParseTypedParameter to parse the individual tokens.
|
||||
// Example string: "my_list:1|2|3,empty_list,other_list:aardvark".
|
||||
|
||||
// A FieldTrialStructList combines multiple lists into a list-of-structs. It
|
||||
// ensures that all its sublists parse correctly and have the same length, then
|
||||
// uses user-supplied accessor functions to write those elements into structs of
|
||||
// a user-supplied type.
|
||||
|
||||
// See the unit test for usage and behavior.
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class FieldTrialListBase : public FieldTrialParameterInterface {
|
||||
protected:
|
||||
friend class FieldTrialListWrapper;
|
||||
explicit FieldTrialListBase(std::string key);
|
||||
|
||||
bool Failed() const;
|
||||
bool Used() const;
|
||||
|
||||
virtual int Size() = 0;
|
||||
|
||||
bool failed_;
|
||||
bool parse_got_called_;
|
||||
};
|
||||
|
||||
// This class represents a vector of type T. The elements are separated by a |
|
||||
// and parsed using ParseTypedParameter.
|
||||
template <typename T>
|
||||
class FieldTrialList : public FieldTrialListBase {
|
||||
public:
|
||||
explicit FieldTrialList(std::string key) : FieldTrialList(key, {}) {}
|
||||
FieldTrialList(std::string key, std::initializer_list<T> default_values)
|
||||
: FieldTrialListBase(key), values_(default_values) {}
|
||||
|
||||
std::vector<T> Get() const { return values_; }
|
||||
operator std::vector<T>() const { return Get(); }
|
||||
const T& operator[](size_t index) const { return values_[index]; }
|
||||
const std::vector<T>* operator->() const { return &values_; }
|
||||
|
||||
protected:
|
||||
bool Parse(absl::optional<std::string> str_value) override {
|
||||
parse_got_called_ = true;
|
||||
|
||||
if (!str_value) {
|
||||
values_.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
std::vector<T> new_values_;
|
||||
rtc::split(str_value.value(), '|', &tokens);
|
||||
|
||||
for (std::string token : tokens) {
|
||||
absl::optional<T> value = ParseTypedParameter<T>(token);
|
||||
if (value) {
|
||||
new_values_.push_back(*value);
|
||||
} else {
|
||||
failed_ = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
values_.swap(new_values_);
|
||||
return true;
|
||||
}
|
||||
|
||||
int Size() override { return values_.size(); }
|
||||
|
||||
private:
|
||||
std::vector<T> values_;
|
||||
};
|
||||
|
||||
class FieldTrialListWrapper {
|
||||
public:
|
||||
virtual ~FieldTrialListWrapper() = default;
|
||||
|
||||
// Takes the element at the given index in the wrapped list and writes it to
|
||||
// the given struct.
|
||||
virtual void WriteElement(void* struct_to_write, int index) = 0;
|
||||
|
||||
virtual FieldTrialListBase* GetList() = 0;
|
||||
|
||||
int Length();
|
||||
|
||||
// Returns true iff the wrapped list has failed to parse at least one token.
|
||||
bool Failed();
|
||||
|
||||
bool Used();
|
||||
|
||||
protected:
|
||||
FieldTrialListWrapper() = default;
|
||||
};
|
||||
|
||||
namespace field_trial_list_impl {
|
||||
// The LambdaTypeTraits struct provides type information about lambdas in the
|
||||
// template expressions below.
|
||||
template <typename T>
|
||||
struct LambdaTypeTraits : public LambdaTypeTraits<decltype(&T::operator())> {};
|
||||
|
||||
template <typename ClassType, typename RetType, typename SourceType>
|
||||
struct LambdaTypeTraits<RetType* (ClassType::*)(SourceType*)const> {
|
||||
using ret = RetType;
|
||||
using src = SourceType;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct TypedFieldTrialListWrapper : FieldTrialListWrapper {
|
||||
public:
|
||||
TypedFieldTrialListWrapper(std::string key,
|
||||
std::function<void(void*, T)> sink)
|
||||
: list_(key), sink_(sink) {}
|
||||
|
||||
void WriteElement(void* struct_to_write, int index) override {
|
||||
sink_(struct_to_write, list_[index]);
|
||||
}
|
||||
|
||||
FieldTrialListBase* GetList() { return &list_; }
|
||||
|
||||
private:
|
||||
FieldTrialList<T> list_;
|
||||
std::function<void(void*, T)> sink_;
|
||||
};
|
||||
|
||||
} // namespace field_trial_list_impl
|
||||
|
||||
template <typename F,
|
||||
typename Traits = typename field_trial_list_impl::LambdaTypeTraits<F>>
|
||||
FieldTrialListWrapper* FieldTrialStructMember(std::string key, F accessor) {
|
||||
return new field_trial_list_impl::TypedFieldTrialListWrapper<
|
||||
typename Traits::ret>(key, [accessor](void* s, typename Traits::ret t) {
|
||||
*accessor(static_cast<typename Traits::src*>(s)) = t;
|
||||
});
|
||||
}
|
||||
|
||||
// This base class is here to reduce the amount of code we have to generate for
|
||||
// each type of FieldTrialStructList.
|
||||
class FieldTrialStructListBase : public FieldTrialParameterInterface {
|
||||
protected:
|
||||
FieldTrialStructListBase(
|
||||
std::initializer_list<FieldTrialListWrapper*> sub_lists)
|
||||
: FieldTrialParameterInterface(""), sub_lists_() {
|
||||
// Take ownership of the list wrappers generated by FieldTrialStructMember
|
||||
// on the call site.
|
||||
for (FieldTrialListWrapper* const* it = sub_lists.begin();
|
||||
it != sub_lists.end(); it++) {
|
||||
sub_parameters_.push_back((*it)->GetList());
|
||||
sub_lists_.push_back(std::unique_ptr<FieldTrialListWrapper>(*it));
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all of our sublists that were in the field trial string had the
|
||||
// same number of elements. If they do, we return that length. If they had
|
||||
// different lengths, any sublist had parse failures or no sublists had
|
||||
// user-supplied values, we return -1.
|
||||
int ValidateAndGetLength();
|
||||
|
||||
bool Parse(absl::optional<std::string> str_value) override;
|
||||
|
||||
std::vector<std::unique_ptr<FieldTrialListWrapper>> sub_lists_;
|
||||
};
|
||||
|
||||
template <typename S>
|
||||
class FieldTrialStructList : public FieldTrialStructListBase {
|
||||
public:
|
||||
FieldTrialStructList(std::initializer_list<FieldTrialListWrapper*> l,
|
||||
std::initializer_list<S> default_list)
|
||||
: FieldTrialStructListBase(l), values_(default_list) {}
|
||||
|
||||
std::vector<S> Get() const { return values_; }
|
||||
operator std::vector<S>() const { return Get(); }
|
||||
const S& operator[](size_t index) const { return values_[index]; }
|
||||
const std::vector<S>* operator->() const { return &values_; }
|
||||
|
||||
protected:
|
||||
void ParseDone() override {
|
||||
int length = ValidateAndGetLength();
|
||||
|
||||
if (length == -1)
|
||||
return;
|
||||
|
||||
std::vector<S> new_values(length, S());
|
||||
|
||||
for (std::unique_ptr<FieldTrialListWrapper>& li : sub_lists_) {
|
||||
if (li->Used()) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
li->WriteElement(&new_values[i], i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
values_.swap(new_values);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<S> values_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // RTC_BASE_EXPERIMENTS_FIELD_TRIAL_LIST_H_
|
||||
133
rtc_base/experiments/field_trial_list_unittest.cc
Normal file
133
rtc_base/experiments/field_trial_list_unittest.cc
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* 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 "rtc_base/gunit.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
using testing::ElementsAre;
|
||||
using testing::IsEmpty;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
struct Garment {
|
||||
int price = 0;
|
||||
std::string color = "";
|
||||
|
||||
// Only needed for testing.
|
||||
Garment() = default;
|
||||
Garment(int p, std::string c) : price(p), color(c) {}
|
||||
|
||||
bool operator==(const Garment& other) const {
|
||||
return price == other.price && color == other.color;
|
||||
}
|
||||
};
|
||||
|
||||
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; })},
|
||||
{{1, "blue"}, {2, "red"}});
|
||||
|
||||
ParseFieldTrial({&my_list},
|
||||
"color:mauve|red|gold,"
|
||||
"price:10|20|30,"
|
||||
"other_param:asdf");
|
||||
|
||||
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
|
||||
// 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"}, {2, "red"}});
|
||||
|
||||
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"}, 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<Garment> my_list(
|
||||
{FieldTrialStructMember("color", [](Garment* g) { return &g->color; }),
|
||||
FieldTrialStructMember("price", [](Garment* g) { return &g->price; })},
|
||||
{{1, "blue"}, {2, "red"}});
|
||||
|
||||
ParseFieldTrial({&my_list}, "price:10|20|30");
|
||||
|
||||
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<Garment> my_list(
|
||||
{FieldTrialStructMember("color", [](Garment* g) { return &g->color; }),
|
||||
FieldTrialStructMember("price", [](Garment* g) { return &g->price; })},
|
||||
{{1, "blue"}, {2, "red"}});
|
||||
|
||||
ParseFieldTrial({&my_list}, "");
|
||||
|
||||
ASSERT_THAT(my_list.Get(),
|
||||
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<Garment> my_list(
|
||||
{FieldTrialStructMember("color", [](Garment* g) { return &g->color; }),
|
||||
FieldTrialStructMember("price", [](Garment* g) { return &g->price; })},
|
||||
{{1, "blue"}, {2, "red"}});
|
||||
|
||||
ParseFieldTrial({&my_list}, "color,price");
|
||||
|
||||
ASSERT_EQ(my_list.Get().size(), 0u);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018 The WebRTC project authors. All Rights Reserved.
|
||||
* 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
|
||||
@ -33,9 +33,6 @@ FieldTrialParameterInterface::~FieldTrialParameterInterface() {
|
||||
RTC_DCHECK(used_) << "Field trial parameter with key: '" << key_
|
||||
<< "' never used.";
|
||||
}
|
||||
std::string FieldTrialParameterInterface::Key() const {
|
||||
return key_;
|
||||
}
|
||||
|
||||
void ParseFieldTrial(
|
||||
std::initializer_list<FieldTrialParameterInterface*> fields,
|
||||
@ -44,13 +41,23 @@ void ParseFieldTrial(
|
||||
FieldTrialParameterInterface* keyless_field = nullptr;
|
||||
for (FieldTrialParameterInterface* field : fields) {
|
||||
field->MarkAsUsed();
|
||||
if (field->Key().empty()) {
|
||||
if (!field->sub_parameters_.empty()) {
|
||||
for (FieldTrialParameterInterface* sub_field : field->sub_parameters_) {
|
||||
RTC_DCHECK(!sub_field->key_.empty());
|
||||
sub_field->MarkAsUsed();
|
||||
field_map[sub_field->key_] = sub_field;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (field->key_.empty()) {
|
||||
RTC_DCHECK(!keyless_field);
|
||||
keyless_field = field;
|
||||
} else {
|
||||
field_map[field->Key()] = field;
|
||||
field_map[field->key_] = field;
|
||||
}
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
while (i < trial_string.length()) {
|
||||
int val_end = FindOrEnd(trial_string, i, ',');
|
||||
@ -78,6 +85,10 @@ void ParseFieldTrial(
|
||||
<< "' (found in trial: \"" << trial_string << "\")";
|
||||
}
|
||||
}
|
||||
|
||||
for (FieldTrialParameterInterface* field : fields) {
|
||||
field->ParseDone();
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
|
||||
@ -11,10 +11,13 @@
|
||||
#define RTC_BASE_EXPERIMENTS_FIELD_TRIAL_PARSER_H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
|
||||
// Field trial parser functionality. Provides funcitonality to parse field trial
|
||||
@ -24,7 +27,7 @@
|
||||
// ignored. Parameters are declared with a given type for which an
|
||||
// implementation of ParseTypedParameter should be provided. The
|
||||
// ParseTypedParameter implementation is given whatever is between the : and the
|
||||
// ,. FieldTrialOptional will use nullopt if the key is provided without :.
|
||||
// ,. If the key is provided without : a FieldTrialOptional will use nullopt.
|
||||
|
||||
// Example string: "my_optional,my_int:3,my_string:hello"
|
||||
|
||||
@ -47,10 +50,14 @@ class FieldTrialParameterInterface {
|
||||
std::string raw_string);
|
||||
void MarkAsUsed() { used_ = true; }
|
||||
virtual bool Parse(absl::optional<std::string> str_value) = 0;
|
||||
std::string Key() const;
|
||||
|
||||
virtual void ParseDone() {}
|
||||
|
||||
std::vector<FieldTrialParameterInterface*> sub_parameters_;
|
||||
|
||||
std::string key_;
|
||||
|
||||
private:
|
||||
std::string key_;
|
||||
bool used_ = false;
|
||||
};
|
||||
|
||||
|
||||
@ -8,9 +8,12 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
#include "rtc_base/experiments/field_trial_parser.h"
|
||||
|
||||
#include "rtc_base/experiments/field_trial_list.h"
|
||||
#include "rtc_base/gunit.h"
|
||||
#include "system_wrappers/include/field_trial.h"
|
||||
#include "test/field_trial.h"
|
||||
#include "test/gmock.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
@ -38,6 +41,7 @@ enum class CustomEnum {
|
||||
kRed = 1,
|
||||
kBlue = 2,
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(FieldTrialParserTest, ParsesValidParameters) {
|
||||
@ -152,4 +156,5 @@ TEST(FieldTrialParserTest, ParsesCustomEnumParameter) {
|
||||
ParseFieldTrial({&my_enum}, "e:5");
|
||||
EXPECT_EQ(my_enum.Get(), CustomEnum::kBlue);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user