buffet: Change OneOf constraint to use generic PropValue list
Now ContraintOneOf contains a vector<PropValue> as opposed to
vector<T>. This will enable support for array types, because the
alternative would be to add explicit specializations for combinations
of basic types (int, bool, double, string,... ) and a vector.
BUG=brillo:107
TEST=`FEATURES=test emerge-link buffet`
Change-Id: Ia6bd93db23517c463ba6d915617d9571499a8491
Reviewed-on: https://chromium-review.googlesource.com/261564
Reviewed-by: Vitaly Buka <vitalybuka@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Trybot-Ready: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/commands/object_schema_unittest.cc b/buffet/commands/object_schema_unittest.cc
index 5e913f8..f257a67 100644
--- a/buffet/commands/object_schema_unittest.cc
+++ b/buffet/commands/object_schema_unittest.cc
@@ -23,6 +23,25 @@
using buffet::unittests::CreateDictionaryValue;
using buffet::unittests::ValueToString;
+namespace {
+
+template<typename T>
+std::vector<T> GetOneOfValues(const buffet::PropType* prop_type) {
+ std::vector<T> values;
+ auto one_of = static_cast<const buffet::ConstraintOneOf*>(
+ prop_type->GetConstraint(buffet::ConstraintType::OneOf));
+ if (!one_of)
+ return values;
+
+ values.reserve(one_of->set_.value.size());
+ for (const auto& prop_value : one_of->set_.value) {
+ values.push_back(prop_value->GetValueAsAny().Get<T>());
+ }
+ return values;
+}
+
+} // anonymous namespace
+
TEST(CommandSchema, IntPropType_Empty) {
buffet::IntPropType prop;
EXPECT_TRUE(prop.GetConstraints().empty());
@@ -192,7 +211,7 @@
param2.FromJson(CreateDictionaryValue("{}").get(), &prop, nullptr);
EXPECT_FALSE(param2.HasOverriddenAttributes());
EXPECT_TRUE(param2.IsBasedOnSchema());
- EXPECT_EQ(std::vector<bool>{true}, prop.GetOneOfValues());
+ EXPECT_EQ(std::vector<bool>{true}, GetOneOfValues<bool>(&prop));
buffet::BooleanPropType prop_base;
buffet::BooleanPropType param3;
@@ -814,34 +833,25 @@
EXPECT_EQ("number", schema.GetProp("param9")->GetTypeAsString());
EXPECT_EQ("integer", schema.GetProp("param10")->GetTypeAsString());
- EXPECT_EQ(4, schema.GetProp("param1")->GetInt()->GetOneOfValues().size());
- EXPECT_EQ(3, schema.GetProp("param2")->GetDouble()->GetOneOfValues().size());
- EXPECT_EQ(2, schema.GetProp("param3")->GetString()->GetOneOfValues().size());
- EXPECT_EQ(3, schema.GetProp("param4")->GetInt()->GetOneOfValues().size());
- EXPECT_EQ(3, schema.GetProp("param5")->GetDouble()->GetOneOfValues().size());
- EXPECT_EQ(2, schema.GetProp("param6")->GetString()->GetOneOfValues().size());
- EXPECT_EQ(3, schema.GetProp("param7")->GetInt()->GetOneOfValues().size());
- EXPECT_EQ(3, schema.GetProp("param8")->GetDouble()->GetOneOfValues().size());
- EXPECT_EQ(0, schema.GetProp("param9")->GetDouble()->GetOneOfValues().size());
- EXPECT_EQ(0, schema.GetProp("param10")->GetInt()->GetOneOfValues().size());
+ EXPECT_EQ((std::vector<int>{0, 1, 2, 3}),
+ GetOneOfValues<int>(schema.GetProp("param1")));
+ EXPECT_EQ((std::vector<double>{0.0, 1.1, 2.2}),
+ GetOneOfValues<double>(schema.GetProp("param2")));
+ EXPECT_EQ((std::vector<std::string>{"id1", "id2"}),
+ GetOneOfValues<std::string>(schema.GetProp("param3")));
- EXPECT_EQ(std::vector<int>({0, 1, 2, 3}),
- schema.GetProp("param1")->GetInt()->GetOneOfValues());
- EXPECT_EQ(std::vector<double>({0.0, 1.1, 2.2}),
- schema.GetProp("param2")->GetDouble()->GetOneOfValues());
- EXPECT_EQ(std::vector<std::string>({"id1", "id2"}),
- schema.GetProp("param3")->GetString()->GetOneOfValues());
-
- EXPECT_EQ(std::vector<int>({1, 2, 3}),
- schema.GetProp("param4")->GetInt()->GetOneOfValues());
- EXPECT_EQ(std::vector<double>({-1.1, 2.2, 3.0}),
- schema.GetProp("param5")->GetDouble()->GetOneOfValues());
- EXPECT_EQ(std::vector<std::string>({"id0", "id1"}),
- schema.GetProp("param6")->GetString()->GetOneOfValues());
- EXPECT_EQ(std::vector<int>({1, 2, 3}),
- schema.GetProp("param7")->GetInt()->GetOneOfValues());
- EXPECT_EQ(std::vector<double>({1.0, 2.0, 3.0}),
- schema.GetProp("param8")->GetDouble()->GetOneOfValues());
+ EXPECT_EQ((std::vector<int>{1, 2, 3}),
+ GetOneOfValues<int>(schema.GetProp("param4")));
+ EXPECT_EQ((std::vector<double>{-1.1, 2.2, 3.0}),
+ GetOneOfValues<double>(schema.GetProp("param5")));
+ EXPECT_EQ((std::vector<std::string>{"id0", "id1"}),
+ GetOneOfValues<std::string>(schema.GetProp("param6")));
+ EXPECT_EQ((std::vector<int>{1, 2, 3}),
+ GetOneOfValues<int>(schema.GetProp("param7")));
+ EXPECT_EQ((std::vector<double>{1.0, 2.0, 3.0}),
+ GetOneOfValues<double>(schema.GetProp("param8")));
+ EXPECT_TRUE(GetOneOfValues<double>(schema.GetProp("param9")).empty());
+ EXPECT_TRUE(GetOneOfValues<int>(schema.GetProp("param10")).empty());
}
TEST(CommandSchema, ObjectSchema_FromJson_Inheritance) {
@@ -939,23 +949,23 @@
EXPECT_EQ(3, schema.GetProp("param12")->GetString()->GetMinLength());
EXPECT_EQ(8, schema.GetProp("param12")->GetString()->GetMaxLength());
EXPECT_EQ("integer", schema.GetProp("param13")->GetTypeAsString());
- EXPECT_EQ(std::vector<int>({1, 2, 3}),
- schema.GetProp("param13")->GetInt()->GetOneOfValues());
+ EXPECT_EQ((std::vector<int>{1, 2, 3}),
+ GetOneOfValues<int>(schema.GetProp("param13")));
EXPECT_EQ("integer", schema.GetProp("param14")->GetTypeAsString());
- EXPECT_EQ(std::vector<int>({1, 2, 3, 4}),
- schema.GetProp("param14")->GetInt()->GetOneOfValues());
+ EXPECT_EQ((std::vector<int>{1, 2, 3, 4}),
+ GetOneOfValues<int>(schema.GetProp("param14")));
EXPECT_EQ("number", schema.GetProp("param15")->GetTypeAsString());
- EXPECT_EQ(std::vector<double>({1.1, 2.2, 3.3}),
- schema.GetProp("param15")->GetDouble()->GetOneOfValues());
+ EXPECT_EQ((std::vector<double>{1.1, 2.2, 3.3}),
+ GetOneOfValues<double>(schema.GetProp("param15")));
EXPECT_EQ("number", schema.GetProp("param16")->GetTypeAsString());
- EXPECT_EQ(std::vector<double>({1.1, 2.2, 3.3, 4.4}),
- schema.GetProp("param16")->GetDouble()->GetOneOfValues());
+ EXPECT_EQ((std::vector<double>{1.1, 2.2, 3.3, 4.4}),
+ GetOneOfValues<double>(schema.GetProp("param16")));
EXPECT_EQ("string", schema.GetProp("param17")->GetTypeAsString());
- EXPECT_EQ(std::vector<std::string>({"id1", "id2"}),
- schema.GetProp("param17")->GetString()->GetOneOfValues());
+ EXPECT_EQ((std::vector<std::string>{"id1", "id2"}),
+ GetOneOfValues<std::string>(schema.GetProp("param17")));
EXPECT_EQ("string", schema.GetProp("param18")->GetTypeAsString());
- EXPECT_EQ(std::vector<std::string>({"id1", "id3"}),
- schema.GetProp("param18")->GetString()->GetOneOfValues());
+ EXPECT_EQ((std::vector<std::string>{"id1", "id3"}),
+ GetOneOfValues<std::string>(schema.GetProp("param18")));
EXPECT_EQ("integer", schema.GetProp("param19")->GetTypeAsString());
EXPECT_EQ(1, schema.GetProp("param19")->GetInt()->GetMinValue());
EXPECT_EQ(5, schema.GetProp("param19")->GetInt()->GetMaxValue());
diff --git a/buffet/commands/prop_constraints.cc b/buffet/commands/prop_constraints.cc
index 45448bc..085fe26 100644
--- a/buffet/commands/prop_constraints.cc
+++ b/buffet/commands/prop_constraints.cc
@@ -4,11 +4,27 @@
#include "buffet/commands/prop_constraints.h"
+#include <base/json/json_writer.h>
+
#include "buffet/commands/prop_values.h"
#include "buffet/commands/schema_constants.h"
namespace buffet {
+namespace {
+
+// Helper function to convert a property value to string, which is used for
+// error reporting.
+std::string PropValueToString(const PropValue& value) {
+ std::string result;
+ auto json = value.ToJson(nullptr);
+ if (json)
+ base::JSONWriter::Write(json.get(), &result);
+ return result;
+}
+
+} // anonymous namespace
+
// Constraint ----------------------------------------------------------------
Constraint::~Constraint() {}
@@ -143,4 +159,59 @@
new ConstraintStringLengthMax{limit_.value}};
}
+// ConstraintOneOf --------------------------------------------------
+ConstraintOneOf::ConstraintOneOf(InheritableAttribute<ChoiceList> set)
+ : set_(std::move(set)) {}
+ConstraintOneOf::ConstraintOneOf(ChoiceList set)
+ : set_(std::move(set)) {}
+
+bool ConstraintOneOf::Validate(const PropValue& value,
+ chromeos::ErrorPtr* error) const {
+ for (const auto& item : set_.value) {
+ if (value.IsEqual(item.get()))
+ return true;
+ }
+ std::vector<std::string> choice_list;
+ choice_list.reserve(set_.value.size());
+ for (const auto& item : set_.value) {
+ choice_list.push_back(PropValueToString(*item));
+ }
+ return ReportErrorNotOneOf(error, PropValueToString(value), choice_list);
+}
+
+std::unique_ptr<Constraint> ConstraintOneOf::Clone() const {
+ InheritableAttribute<ChoiceList> attr;
+ attr.is_inherited = set_.is_inherited;
+ attr.value.reserve(set_.value.size());
+ for (const auto& prop_value : set_.value) {
+ attr.value.push_back(prop_value->Clone());
+ }
+ return std::unique_ptr<Constraint>{new ConstraintOneOf{std::move(attr)}};
+}
+
+std::unique_ptr<Constraint> ConstraintOneOf::CloneAsInherited() const {
+ ChoiceList cloned;
+ cloned.reserve(set_.value.size());
+ for (const auto& prop_value : set_.value) {
+ cloned.push_back(prop_value->Clone());
+ }
+ return std::unique_ptr<Constraint>{new ConstraintOneOf{std::move(cloned)}};
+}
+
+std::unique_ptr<base::Value> ConstraintOneOf::ToJson(
+ chromeos::ErrorPtr* error) const {
+ std::unique_ptr<base::ListValue> list(new base::ListValue);
+ for (const auto& prop_value : set_.value) {
+ auto json = prop_value->ToJson(error);
+ if (!json)
+ return std::unique_ptr<base::Value>();
+ list->Append(json.release());
+ }
+ return std::move(list);
+}
+
+const char* ConstraintOneOf::GetDictKey() const {
+ return commands::attributes::kOneOf_Enum;
+}
+
} // namespace buffet
diff --git a/buffet/commands/prop_constraints.h b/buffet/commands/prop_constraints.h
index 79cc72a..e5efdd2 100644
--- a/buffet/commands/prop_constraints.h
+++ b/buffet/commands/prop_constraints.h
@@ -295,13 +295,12 @@
};
// Implementation of OneOf constraint for different data types.
-template<typename T>
class ConstraintOneOf : public Constraint {
public:
- explicit ConstraintOneOf(const InheritableAttribute<std::vector<T>>& set)
- : set_(set) {}
- explicit ConstraintOneOf(const std::vector<T>& set)
- : set_(set) {}
+ using ChoiceList = std::vector<std::unique_ptr<const PropValue>>;
+
+ explicit ConstraintOneOf(InheritableAttribute<ChoiceList> set);
+ explicit ConstraintOneOf(ChoiceList set);
// Implementation of Constraint::GetType().
ConstraintType GetType() const override {
@@ -315,46 +314,24 @@
// Implementation of Constraint::Validate().
bool Validate(const PropValue& value,
- chromeos::ErrorPtr* error) const override {
- using chromeos::string_utils::ToString;
- T v = value.GetValueAsAny().Get<T>();
- for (const auto& item : set_.value) {
- if (CompareValue(v, item))
- return true;
- }
- std::vector<std::string> values;
- values.reserve(set_.value.size());
- for (const auto& item : set_.value) {
- values.push_back(ToString(item));
- }
- return ReportErrorNotOneOf(error, ToString(v), values);
- }
+ chromeos::ErrorPtr* error) const override;
// Implementation of Constraint::Clone().
- std::unique_ptr<Constraint> Clone() const override {
- return std::unique_ptr<Constraint>{new ConstraintOneOf{set_}};
- }
+ std::unique_ptr<Constraint> Clone() const override;
// Implementation of Constraint::CloneAsInherited().
- std::unique_ptr<Constraint> CloneAsInherited() const override {
- return std::unique_ptr<Constraint>{new ConstraintOneOf{set_.value}};
- }
+ std::unique_ptr<Constraint> CloneAsInherited() const override;
// Implementation of Constraint::ToJson().
- std::unique_ptr<base::Value> ToJson(
- chromeos::ErrorPtr* error) const override {
- return TypedValueToJson(set_.value, error);
- }
+ std::unique_ptr<base::Value> ToJson(chromeos::ErrorPtr* error) const override;
// Implementation of Constraint::GetDictKey().
- const char* GetDictKey() const override {
- return commands::attributes::kOneOf_Enum;
- }
+ const char* GetDictKey() const override;
// Stores the list of acceptable values for the parameter.
// |set_.is_inherited| indicates whether the constraint is inherited
// from base schema or overridden.
- InheritableAttribute<std::vector<T>> set_;
+ InheritableAttribute<ChoiceList> set_;
private:
DISALLOW_COPY_AND_ASSIGN(ConstraintOneOf);
diff --git a/buffet/commands/prop_types.cc b/buffet/commands/prop_types.cc
index 53beb4f..3bb0577 100644
--- a/buffet/commands/prop_types.cc
+++ b/buffet/commands/prop_types.cc
@@ -298,16 +298,17 @@
"Expecting an array");
return constraint;
}
- std::vector<T> set;
- set.reserve(list->GetSize());
+ ConstraintOneOf::ChoiceList choice_list;
+ choice_list.reserve(list->GetSize());
for (const base::Value* item : *list) {
- T val{};
- if (!TypedValueFromJson(item, prop_type, &val, error))
+ std::unique_ptr<PropValue> prop_value = prop_type->CreateValue();
+ if (!prop_value->FromJson(item, error))
return constraint;
- set.push_back(val);
+ choice_list.push_back(std::move(prop_value));
}
- InheritableAttribute<std::vector<T>> val(set, false);
- constraint.reset(new ConstraintOneOf<T>{val});
+ InheritableAttribute<ConstraintOneOf::ChoiceList> val(std::move(choice_list),
+ false);
+ constraint.reset(new ConstraintOneOf{std::move(val)});
return constraint;
}
diff --git a/buffet/commands/prop_types.h b/buffet/commands/prop_types.h
index 892a730..1af057c 100644
--- a/buffet/commands/prop_types.h
+++ b/buffet/commands/prop_types.h
@@ -200,13 +200,6 @@
bool ConstraintsFromJson(const base::DictionaryValue* value,
std::set<std::string>* processed_keys,
chromeos::ErrorPtr* error) override;
-
- // Helper method to obtain a vector of OneOf constraint values.
- std::vector<T> GetOneOfValues() const {
- auto ofc = static_cast<const ConstraintOneOf<T>*>(
- this->GetConstraint(ConstraintType::OneOf));
- return ofc ? ofc->set_.value : std::vector<T>();
- }
};
// Helper base class for Int and Double parameter types.