libweave: Add DictionaryFromDBusVariantDictionary Added unittests for DictionaryFromDBusVariantDictionary and test with reverse conversion. BUG=brillo:1245 TEST='FEATURES=test emerge-gizmo buffet' Change-Id: I061f753d158f53398aa7ef0ec2757e50ed6cec7a Reviewed-on: https://chromium-review.googlesource.com/287946 Reviewed-by: Vitaly Buka <vitalybuka@chromium.org> Commit-Queue: Vitaly Buka <vitalybuka@chromium.org> Tested-by: Vitaly Buka <vitalybuka@chromium.org>
diff --git a/libweave/src/commands/dbus_conversion.cc b/libweave/src/commands/dbus_conversion.cc index 4723918..cadbafa 100644 --- a/libweave/src/commands/dbus_conversion.cc +++ b/libweave/src/commands/dbus_conversion.cc
@@ -8,6 +8,8 @@ #include <string> #include <vector> +#include <chromeos/type_name_undecorate.h> + #include "libweave/src/commands/object_schema.h" #include "libweave/src/commands/prop_types.h" #include "libweave/src/commands/prop_values.h" @@ -93,7 +95,7 @@ } auto type = (*list->begin())->GetType(); for (const base::Value* v : *list) - CHECK_EQ(v->GetType(), type); + CHECK_EQ(v->GetType(), type) << "Unsupported different type elements"; switch (type) { case base::Value::TYPE_BOOLEAN: @@ -130,6 +132,94 @@ return prop_value; } +template <typename T> +std::unique_ptr<base::Value> CreateValue(const T& value, + chromeos::ErrorPtr* error) { + return std::unique_ptr<base::Value>{new base::FundamentalValue{value}}; +} + +template <> +std::unique_ptr<base::Value> CreateValue<std::string>( + const std::string& value, + chromeos::ErrorPtr* error) { + return std::unique_ptr<base::Value>{new base::StringValue{value}}; +} + +template <> +std::unique_ptr<base::Value> CreateValue<chromeos::VariantDictionary>( + const chromeos::VariantDictionary& value, + chromeos::ErrorPtr* error) { + return DictionaryFromDBusVariantDictionary(value, error); +} + +template <typename T> +std::unique_ptr<base::ListValue> CreateListValue(const std::vector<T>& value, + chromeos::ErrorPtr* error) { + std::unique_ptr<base::ListValue> list{new base::ListValue}; + + for (const T& i : value) { + auto item = CreateValue(i, error); + if (!item) + return nullptr; + list->Append(item.release()); + } + + return list; +} + +// Returns false only in case of error. True can be returned if type is not +// matched. +template <typename T> +bool TryCreateValue(const chromeos::Any& any, + std::unique_ptr<base::Value>* value, + chromeos::ErrorPtr* error) { + if (any.IsTypeCompatible<T>()) { + *value = CreateValue(any.Get<T>(), error); + return *value != nullptr; + } + + if (any.IsTypeCompatible<std::vector<T>>()) { + *value = CreateListValue(any.Get<std::vector<T>>(), error); + return *value != nullptr; + } + + return true; // Not an error, we will try different type. +} + +template <> +std::unique_ptr<base::Value> CreateValue<chromeos::Any>( + const chromeos::Any& any, + chromeos::ErrorPtr* error) { + std::unique_ptr<base::Value> result; + if (!TryCreateValue<bool>(any, &result, error) || result) + return result; + + if (!TryCreateValue<int>(any, &result, error) || result) + return result; + + if (!TryCreateValue<double>(any, &result, error) || result) + return result; + + if (!TryCreateValue<std::string>(any, &result, error) || result) + return result; + + if (!TryCreateValue<chromeos::VariantDictionary>(any, &result, error) || + result) { + return result; + } + + // This will collapse Any{Any{T}} and vector{Any{T}}. + if (!TryCreateValue<chromeos::Any>(any, &result, error) || result) + return result; + + chromeos::Error::AddToPrintf( + error, FROM_HERE, errors::commands::kDomain, + errors::commands::kUnknownType, "Type '%s' is not supported.", + chromeos::UndecorateTypeName(any.GetType().name()).c_str()); + + return nullptr; +} + bool ErrorMissingProperty(chromeos::ErrorPtr* error, const tracked_objects::Location& location, const char* param_name) { @@ -264,4 +354,19 @@ return result; } +std::unique_ptr<base::DictionaryValue> DictionaryFromDBusVariantDictionary( + const chromeos::VariantDictionary& object, + chromeos::ErrorPtr* error) { + std::unique_ptr<base::DictionaryValue> result{new base::DictionaryValue}; + + for (const auto& pair : object) { + auto value = CreateValue(pair.second, error); + if (!value) + return nullptr; + result->Set(pair.first, value.release()); + } + + return result; +} + } // namespace weave
diff --git a/libweave/src/commands/dbus_conversion.h b/libweave/src/commands/dbus_conversion.h index ab49f70..5da2fef 100644 --- a/libweave/src/commands/dbus_conversion.h +++ b/libweave/src/commands/dbus_conversion.h
@@ -41,6 +41,11 @@ chromeos::VariantDictionary DictionaryToDBusVariantDictionary( const base::DictionaryValue& object); +// Converts D-Bus variant dictionary to DictionaryValue. +std::unique_ptr<base::DictionaryValue> DictionaryFromDBusVariantDictionary( + const chromeos::VariantDictionary& object, + chromeos::ErrorPtr* error); + } // namespace weave #endif // LIBWEAVE_SRC_COMMANDS_DBUS_CONVERSION_H_
diff --git a/libweave/src/commands/dbus_conversion_unittest.cc b/libweave/src/commands/dbus_conversion_unittest.cc index c25245e..06e1718 100644 --- a/libweave/src/commands/dbus_conversion_unittest.cc +++ b/libweave/src/commands/dbus_conversion_unittest.cc
@@ -4,10 +4,13 @@ #include "libweave/src/commands/dbus_conversion.h" +#include <limits> #include <memory> #include <string> #include <vector> +#include <base/guid.h> +#include <base/rand_util.h> #include <base/values.h> #include <chromeos/variant_dictionary.h> #include <gtest/gtest.h> @@ -17,6 +20,8 @@ namespace weave { +namespace { + using chromeos::Any; using chromeos::VariantDictionary; using unittests::CreateDictionaryValue; @@ -25,6 +30,98 @@ return DictionaryToDBusVariantDictionary(object); } +std::unique_ptr<base::DictionaryValue> FromDBus( + const chromeos::VariantDictionary& object) { + chromeos::ErrorPtr error; + auto result = DictionaryFromDBusVariantDictionary(object, &error); + EXPECT_TRUE(result || error); + return result; +} + +std::unique_ptr<base::Value> CreateRandomValue(int children); +std::unique_ptr<base::Value> CreateRandomValue(int children, + base::Value::Type type); + +const base::Value::Type kRandomTypes[] = { + base::Value::TYPE_BOOLEAN, base::Value::TYPE_INTEGER, + base::Value::TYPE_DOUBLE, base::Value::TYPE_STRING, + base::Value::TYPE_DICTIONARY, base::Value::TYPE_LIST, +}; + +const base::Value::Type kRandomTypesWithChildren[] = { + base::Value::TYPE_DICTIONARY, base::Value::TYPE_LIST, +}; + +base::Value::Type CreateRandomValueType(bool with_children) { + if (with_children) { + return kRandomTypesWithChildren[base::RandInt( + 0, arraysize(kRandomTypesWithChildren) - 1)]; + } + return kRandomTypes[base::RandInt(0, arraysize(kRandomTypes) - 1)]; +} + +std::unique_ptr<base::DictionaryValue> CreateRandomDictionary(int children) { + std::unique_ptr<base::DictionaryValue> result{new base::DictionaryValue}; + + while (children > 0) { + int sub_children = base::RandInt(1, children); + children -= sub_children; + result->Set(base::GenerateGUID(), + CreateRandomValue(sub_children).release()); + } + + return result; +} + +std::unique_ptr<base::ListValue> CreateRandomList(int children) { + std::unique_ptr<base::ListValue> result{new base::ListValue}; + + base::Value::Type type = CreateRandomValueType(children > 0); + while (children > 0) { + size_t max_children = + (type != base::Value::TYPE_DICTIONARY && type != base::Value::TYPE_LIST) + ? 1 + : children; + size_t sub_children = base::RandInt(1, max_children); + children -= sub_children; + result->Append(CreateRandomValue(sub_children, type).release()); + } + + return result; +} + +std::unique_ptr<base::Value> CreateRandomValue(int children, + base::Value::Type type) { + CHECK_GE(children, 1); + switch (type) { + case base::Value::TYPE_INTEGER: + return std::unique_ptr<base::Value>{new base::FundamentalValue{ + base::RandInt(std::numeric_limits<int>::min(), + std::numeric_limits<int>::max())}}; + case base::Value::TYPE_DOUBLE: + return std::unique_ptr<base::Value>{ + new base::FundamentalValue{base::RandDouble()}}; + case base::Value::TYPE_STRING: + return std::unique_ptr<base::Value>{ + new base::StringValue{base::GenerateGUID()}}; + case base::Value::TYPE_DICTIONARY: + CHECK_GE(children, 1); + return CreateRandomDictionary(children - 1); + case base::Value::TYPE_LIST: + CHECK_GE(children, 1); + return CreateRandomList(children - 1); + default: + return std::unique_ptr<base::Value>{ + new base::FundamentalValue{base::RandInt(0, 1) != 0}}; + } +} + +std::unique_ptr<base::Value> CreateRandomValue(int children) { + return CreateRandomValue(children, CreateRandomValueType(children > 0)); +} + +} // namespace + TEST(DBusConversionTest, PropValueToDBusVariant) { IntPropType int_type; auto prop_value = int_type.CreateValue(5, nullptr); @@ -211,4 +308,42 @@ ToDBus(*CreateDictionaryValue("{'objList': [{'string': 'abc'}]}"))); } +TEST(DBusConversionTest, DictionaryFromDBusVariantDictionary) { + EXPECT_JSON_EQ("{'bool': true}", *FromDBus({{"bool", true}})); + EXPECT_JSON_EQ("{'int': 5}", *FromDBus({{"int", 5}})); + EXPECT_JSON_EQ("{'double': 6.7}", *FromDBus({{"double", 6.7}})); + EXPECT_JSON_EQ("{'string': 'abc'}", + *FromDBus({{"string", std::string{"abc"}}})); + EXPECT_JSON_EQ("{'object': {'bool': true}}", + *FromDBus({{"object", VariantDictionary{{"bool", true}}}})); + EXPECT_JSON_EQ("{'emptyList': []}", + *FromDBus({{"emptyList", std::vector<bool>{}}})); + EXPECT_JSON_EQ("{'intList': [5]}", + *FromDBus({{"intList", std::vector<int>{5}}})); + EXPECT_JSON_EQ( + "{'intListList': [[5], [6, 7]]}", + *FromDBus({{"intListList", std::vector<Any>{std::vector<int>{5}, + std::vector<int>{6, 7}}}})); + EXPECT_JSON_EQ( + "{'objList': [{'string': 'abc'}]}", + *FromDBus({{"objList", std::vector<VariantDictionary>{ + {{"string", std::string{"abc"}}}}}})); + EXPECT_JSON_EQ("{'int': 5}", *FromDBus({{"int", Any{Any{5}}}})); +} + +TEST(DBusConversionTest, DictionaryFromDBusVariantDictionary_Errors) { + EXPECT_FALSE(FromDBus({{"cString", "abc"}})); + EXPECT_FALSE(FromDBus({{"float", 1.0f}})); + EXPECT_FALSE(FromDBus({{"listList", std::vector<std::vector<int>>{}}})); + EXPECT_FALSE(FromDBus({{"any", Any{}}})); + EXPECT_FALSE(FromDBus({{"null", nullptr}})); +} + +TEST(DBusConversionTest, DBusRandomDictionaryConversion) { + auto dict = CreateRandomDictionary(10000); + auto varian_dict = ToDBus(*dict); + auto dict_restored = FromDBus(varian_dict); + EXPECT_PRED2(unittests::IsEqualValue, *dict, *dict_restored); +} + } // namespace weave