diff options
Diffstat (limited to 'common')
-rw-r--r-- | common/binder_constants.cc | 23 | ||||
-rw-r--r-- | common/binder_constants.h | 26 | ||||
-rw-r--r-- | common/binder_utils.cc | 65 | ||||
-rw-r--r-- | common/binder_utils.h | 63 | ||||
-rw-r--r-- | common/data_conversion.cc | 263 | ||||
-rw-r--r-- | common/data_conversion.h | 38 | ||||
-rw-r--r-- | common/data_conversion_unittest.cc | 200 |
7 files changed, 678 insertions, 0 deletions
diff --git a/common/binder_constants.cc b/common/binder_constants.cc new file mode 100644 index 0000000..b17962b --- /dev/null +++ b/common/binder_constants.cc @@ -0,0 +1,23 @@ +// Copyright 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/binder_constants.h" + +namespace weaved { +namespace binder { + +const char kWeaveServiceName[] = "weave_service"; + +} // namespace binder +} // namespace weaved diff --git a/common/binder_constants.h b/common/binder_constants.h new file mode 100644 index 0000000..82935cd --- /dev/null +++ b/common/binder_constants.h @@ -0,0 +1,26 @@ +// Copyright 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_BINDER_CONSTANTS_H_ +#define COMMON_BINDER_CONSTANTS_H_ + +namespace weaved { +namespace binder { + +extern const char kWeaveServiceName[]; + +} // namespace binder +} // namespace weaved + +#endif // COMMON_BINDER_CONSTANTS_H_ diff --git a/common/binder_utils.cc b/common/binder_utils.cc new file mode 100644 index 0000000..6f66040 --- /dev/null +++ b/common/binder_utils.cc @@ -0,0 +1,65 @@ +// Copyright 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/binder_utils.h" + +#include <base/json/json_reader.h> +#include <base/json/json_writer.h> + +namespace weaved { +namespace binder_utils { + +android::binder::Status ToStatus(bool success, weave::ErrorPtr* error) { + if (success) + return android::binder::Status::ok(); + return android::binder::Status::fromServiceSpecificError( + 1, android::String8{error->get()->GetMessage().c_str()}); +} + +bool StatusToError(android::binder::Status status, brillo::ErrorPtr* error) { + if (status.isOk()) + return true; + brillo::Error::AddTo(error, FROM_HERE, "binder", + std::to_string(status.exceptionCode()), + status.exceptionMessage().string()); + return false; +} + +android::String16 ToString16(const base::Value& value) { + std::string json; + base::JSONWriter::Write(value, &json); + return ToString16(json); +} + +android::binder::Status ParseDictionary( + const android::String16& json, + std::unique_ptr<base::DictionaryValue>* dict) { + int error = 0; + std::string message; + std::unique_ptr<base::Value> value{ + base::JSONReader::ReadAndReturnError(ToString(json), base::JSON_PARSE_RFC, + &error, &message) + .release()}; + base::DictionaryValue* dict_value = nullptr; + if (!value || !value->GetAsDictionary(&dict_value)) { + return android::binder::Status::fromServiceSpecificError( + error, android::String8{message.c_str()}); + } + dict->reset(dict_value); + value.release(); // |dict| now owns the object. + return android::binder::Status::ok(); +} + +} // namespace binder_utils +} // namespace weaved diff --git a/common/binder_utils.h b/common/binder_utils.h new file mode 100644 index 0000000..65b462d --- /dev/null +++ b/common/binder_utils.h @@ -0,0 +1,63 @@ +// Copyright 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_BINDER_UTILS_H_ +#define COMMON_BINDER_UTILS_H_ + +#include <memory> +#include <string> + +#include <base/values.h> +#include <binder/Status.h> +#include <utils/String8.h> +#include <utils/String16.h> +#include <weave/error.h> +#include <brillo/errors/error.h> + +namespace weaved { +namespace binder_utils { + +// Converts the result of weave API call into a binder Status object. +// If |success| is true, return binder::Status::ok(), otherwise the method +// constructs a service-specific failure status with an error message obtained +// from the |error| object. +android::binder::Status ToStatus(bool success, weave::ErrorPtr* error); + +// Converts a binder status code to a Brillo error object. Returns true if the +// status was isOk(), otherwise returns false and provides error information +// in the |error| object. +bool StatusToError(android::binder::Status status, brillo::ErrorPtr* error); + +// Converts binder's UTF16 string into a regular UTF8-encoded standard string. +inline std::string ToString(const android::String16& value) { + return android::String8{value}.string(); +} + +// Converts regular UTF8-encoded standard string into a binder's UTF16 string. +inline android::String16 ToString16(const std::string& value) { + return android::String16{value.c_str()}; +} + +// Serializes a dictionary to a string for transferring over binder. +android::String16 ToString16(const base::Value& value); + +// De-serializes a dictionary from a binder string. +android::binder::Status ParseDictionary( + const android::String16& json, + std::unique_ptr<base::DictionaryValue>* dict); + +} // namespace binder_utils +} // namespace weaved + +#endif // COMMON_BINDER_UTILS_H_ diff --git a/common/data_conversion.cc b/common/data_conversion.cc new file mode 100644 index 0000000..fd20ad4 --- /dev/null +++ b/common/data_conversion.cc @@ -0,0 +1,263 @@ +// Copyright 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/data_conversion.h" + +#include <string> +#include <vector> + +#include <brillo/type_name_undecorate.h> + +namespace weaved { + +namespace { + +// Helpers for JsonToAny(). +template <typename T> +brillo::Any ValueToAny(const base::Value& json, + bool (base::Value::*fnc)(T*) const) { + T val; + CHECK((json.*fnc)(&val)); + return val; +} + +brillo::Any ValueToAny(const base::Value& json); + +template <typename T> +brillo::Any ListToAny(const base::ListValue& list, + bool (base::Value::*fnc)(T*) const) { + std::vector<T> result; + result.reserve(list.GetSize()); + for (const base::Value* v : list) { + T val; + CHECK((v->*fnc)(&val)); + result.push_back(val); + } + return result; +} + +brillo::Any DictListToAny(const base::ListValue& list) { + std::vector<brillo::VariantDictionary> result; + result.reserve(list.GetSize()); + for (const base::Value* v : list) { + const base::DictionaryValue* dict = nullptr; + CHECK(v->GetAsDictionary(&dict)); + result.push_back(details::DictionaryValueToVariantDictionary(*dict)); + } + return result; +} + +brillo::Any ListListToAny(const base::ListValue& list) { + std::vector<brillo::Any> result; + result.reserve(list.GetSize()); + for (const base::Value* v : list) + result.push_back(ValueToAny(*v)); + return result; +} + +// Converts a JSON value into an Any so it can be sent over D-Bus using +// UpdateState D-Bus method from Buffet. +brillo::Any ValueToAny(const base::Value& json) { + brillo::Any prop_value; + switch (json.GetType()) { + case base::Value::TYPE_BOOLEAN: + prop_value = ValueToAny<bool>(json, &base::Value::GetAsBoolean); + break; + case base::Value::TYPE_INTEGER: + prop_value = ValueToAny<int>(json, &base::Value::GetAsInteger); + break; + case base::Value::TYPE_DOUBLE: + prop_value = ValueToAny<double>(json, &base::Value::GetAsDouble); + break; + case base::Value::TYPE_STRING: + prop_value = ValueToAny<std::string>(json, &base::Value::GetAsString); + break; + case base::Value::TYPE_DICTIONARY: { + const base::DictionaryValue* dict = nullptr; + CHECK(json.GetAsDictionary(&dict)); + prop_value = details::DictionaryValueToVariantDictionary(*dict); + break; + } + case base::Value::TYPE_LIST: { + const base::ListValue* list = nullptr; + CHECK(json.GetAsList(&list)); + if (list->empty()) { + // We don't know type of objects this list intended for, so we just use + // vector<brillo::Any>. + prop_value = ListListToAny(*list); + break; + } + auto type = (*list->begin())->GetType(); + for (const base::Value* v : *list) + CHECK_EQ(v->GetType(), type) << "Unsupported different type elements"; + + switch (type) { + case base::Value::TYPE_BOOLEAN: + prop_value = ListToAny<bool>(*list, &base::Value::GetAsBoolean); + break; + case base::Value::TYPE_INTEGER: + prop_value = ListToAny<int>(*list, &base::Value::GetAsInteger); + break; + case base::Value::TYPE_DOUBLE: + prop_value = ListToAny<double>(*list, &base::Value::GetAsDouble); + break; + case base::Value::TYPE_STRING: + prop_value = ListToAny<std::string>(*list, &base::Value::GetAsString); + break; + case base::Value::TYPE_DICTIONARY: + prop_value = DictListToAny(*list); + break; + case base::Value::TYPE_LIST: + // We can't support Any{vector<vector<>>} as the type is only known + // in runtime when we need to instantiate templates in compile time. + // We can use Any{vector<Any>} instead. + prop_value = ListListToAny(*list); + break; + default: + LOG(FATAL) << "Unsupported JSON value type for list element: " + << (*list->begin())->GetType(); + } + break; + } + default: + LOG(FATAL) << "Unexpected JSON value type: " << json.GetType(); + break; + } + return prop_value; +} + +template <typename T> +std::unique_ptr<base::Value> CreateValue(const T& value, + brillo::ErrorPtr* error) { + return std::unique_ptr<base::Value>{new base::FundamentalValue{value}}; +} + +std::unique_ptr<base::Value> CreateValue(const std::string& value, + brillo::ErrorPtr* error) { + return std::unique_ptr<base::Value>{new base::StringValue{value}}; +} + +std::unique_ptr<base::Value> CreateValue(const char* value, + brillo::ErrorPtr* error) { + return std::unique_ptr<base::Value>{new base::StringValue{value}}; +} + +template <> +std::unique_ptr<base::Value> CreateValue<brillo::VariantDictionary>( + const brillo::VariantDictionary& value, + brillo::ErrorPtr* error) { + return details::VariantDictionaryToDictionaryValue(value, error); +} + +template <typename T> +std::unique_ptr<base::ListValue> CreateListValue(const std::vector<T>& value, + brillo::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 brillo::Any& any, + std::unique_ptr<base::Value>* value, + brillo::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<brillo::Any>( + const brillo::Any& any, + brillo::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 (any.IsTypeCompatible<const char*>()) + return CreateValue(any.Get<const char*>(), error); + + if (!TryCreateValue<brillo::VariantDictionary>(any, &result, error) || + result) { + return result; + } + + // This will collapse Any{Any{T}} and vector{Any{T}}. + if (!TryCreateValue<brillo::Any>(any, &result, error) || result) + return result; + + brillo::Error::AddToPrintf( + error, FROM_HERE, "buffet", "unknown_type", "Type '%s' is not supported.", + any.GetUndecoratedTypeName().c_str()); + + return nullptr; +} + +} // namespace + +namespace details { + +brillo::VariantDictionary DictionaryValueToVariantDictionary( + const base::DictionaryValue& object) { + brillo::VariantDictionary result; + + for (base::DictionaryValue::Iterator it(object); !it.IsAtEnd(); it.Advance()) + result.emplace(it.key(), ValueToAny(it.value())); + + return result; +} + +std::unique_ptr<base::DictionaryValue> VariantDictionaryToDictionaryValue( + const brillo::VariantDictionary& object, + brillo::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 details +} // namespace weaved diff --git a/common/data_conversion.h b/common/data_conversion.h new file mode 100644 index 0000000..accc520 --- /dev/null +++ b/common/data_conversion.h @@ -0,0 +1,38 @@ +// Copyright 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef COMMON_DATA_CONVERSION_H_ +#define COMMON_DATA_CONVERSION_H_ + +#include <base/values.h> +#include <brillo/any.h> +#include <brillo/errors/error.h> +#include <brillo/variant_dictionary.h> + +namespace weaved { +namespace details { + +// Converts DictionaryValue to variant dictionary. +brillo::VariantDictionary DictionaryValueToVariantDictionary( + const base::DictionaryValue& object); + +// Converts variant dictionary to DictionaryValue. +std::unique_ptr<base::DictionaryValue> VariantDictionaryToDictionaryValue( + const brillo::VariantDictionary& object, + brillo::ErrorPtr* error); + +} // namespace details +} // namespace weaved + +#endif // COMMON_DATA_CONVERSION_H_ diff --git a/common/data_conversion_unittest.cc b/common/data_conversion_unittest.cc new file mode 100644 index 0000000..832fffb --- /dev/null +++ b/common/data_conversion_unittest.cc @@ -0,0 +1,200 @@ +// Copyright 2015 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "common/data_conversion.h" + +#include <limits> +#include <memory> +#include <string> +#include <vector> + +#include <base/guid.h> +#include <base/rand_util.h> +#include <base/values.h> +#include <brillo/variant_dictionary.h> +#include <gtest/gtest.h> +#include <weave/test/unittest_utils.h> + +namespace weaved { + +namespace { + +using brillo::Any; +using brillo::VariantDictionary; +using weave::test::CreateDictionaryValue; +using weave::test::IsEqualValue; + +brillo::VariantDictionary ToVariant(const base::DictionaryValue& object) { + return details::DictionaryValueToVariantDictionary(object); +} + +std::unique_ptr<base::DictionaryValue> FromVariant( + const brillo::VariantDictionary& object) { + brillo::ErrorPtr error; + auto result = details::VariantDictionaryToDictionaryValue(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, DictionaryToDBusVariantDictionary) { + EXPECT_EQ((VariantDictionary{{"bool", true}}), + ToVariant(*CreateDictionaryValue("{'bool': true}"))); + EXPECT_EQ((VariantDictionary{{"int", 5}}), + ToVariant(*CreateDictionaryValue("{'int': 5}"))); + EXPECT_EQ((VariantDictionary{{"double", 6.7}}), + ToVariant(*CreateDictionaryValue("{'double': 6.7}"))); + EXPECT_EQ((VariantDictionary{{"string", std::string{"abc"}}}), + ToVariant(*CreateDictionaryValue("{'string': 'abc'}"))); + EXPECT_EQ((VariantDictionary{{"object", VariantDictionary{{"bool", true}}}}), + ToVariant(*CreateDictionaryValue("{'object': {'bool': true}}"))); + EXPECT_EQ((VariantDictionary{{"emptyList", std::vector<Any>{}}}), + ToVariant(*CreateDictionaryValue("{'emptyList': []}"))); + EXPECT_EQ((VariantDictionary{{"intList", std::vector<int>{5}}}), + ToVariant(*CreateDictionaryValue("{'intList': [5]}"))); + EXPECT_EQ((VariantDictionary{ + {"intListList", std::vector<Any>{std::vector<int>{5}, + std::vector<int>{6, 7}}}}), + ToVariant(*CreateDictionaryValue( + "{'intListList': [[5], [6, 7]]}"))); + EXPECT_EQ((VariantDictionary{{"objList", + std::vector<VariantDictionary>{ + {{"string", std::string{"abc"}}}}}}), + ToVariant(*CreateDictionaryValue( + "{'objList': [{'string': 'abc'}]}"))); +} + +TEST(DBusConversionTest, VariantDictionaryToDictionaryValue) { + EXPECT_JSON_EQ("{'bool': true}", *FromVariant({{"bool", true}})); + EXPECT_JSON_EQ("{'int': 5}", *FromVariant({{"int", 5}})); + EXPECT_JSON_EQ("{'double': 6.7}", *FromVariant({{"double", 6.7}})); + EXPECT_JSON_EQ("{'string': 'abc'}", + *FromVariant({{"string", std::string{"abc"}}})); + EXPECT_JSON_EQ("{'object': {'bool': true}}", + *FromVariant({{"object", VariantDictionary{{"bool", true}}}})); + EXPECT_JSON_EQ("{'emptyList': []}", + *FromVariant({{"emptyList", std::vector<bool>{}}})); + EXPECT_JSON_EQ("{'intList': [5]}", + *FromVariant({{"intList", std::vector<int>{5}}})); + EXPECT_JSON_EQ( + "{'intListList': [[5], [6, 7]]}", + *FromVariant({{"intListList", + std::vector<Any>{std::vector<int>{5}, + std::vector<int>{6, 7}}}})); + EXPECT_JSON_EQ( + "{'objList': [{'string': 'abc'}]}", + *FromVariant({{"objList", std::vector<VariantDictionary>{ + {{"string", std::string{"abc"}}}}}})); + EXPECT_JSON_EQ("{'int': 5}", *FromVariant({{"int", Any{Any{5}}}})); +} + +TEST(DBusConversionTest, VariantDictionaryToDictionaryValueErrors) { + EXPECT_FALSE(FromVariant({{"cString", "abc"}})); + EXPECT_FALSE(FromVariant({{"float", 1.0f}})); + EXPECT_FALSE(FromVariant({{"listList", std::vector<std::vector<int>>{}}})); + EXPECT_FALSE(FromVariant({{"any", Any{}}})); + EXPECT_FALSE(FromVariant({{"null", nullptr}})); +} + +TEST(DBusConversionTest, DBusRandomDictionaryConversion) { + auto dict = CreateRandomDictionary(10000); + auto varian_dict = ToVariant(*dict); + auto dict_restored = FromVariant(varian_dict); + EXPECT_PRED2(IsEqualValue, *dict, *dict_restored); +} + +} // namespace buffet |