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