| // Copyright 2014 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chromeos-dbus-bindings/dbus_signature.h" |
| |
| #include <map> |
| #include <string> |
| |
| #include <dbus/dbus-protocol.h> |
| #include <gtest/gtest.h> |
| |
| using std::map; |
| using std::string; |
| using testing::Test; |
| using testing::TestWithParam; |
| |
| namespace chromeos_dbus_bindings { |
| |
| using Direction = DBusType::Direction; |
| using Receiver = DBusType::Receiver; |
| |
| namespace { |
| |
| // Failing signatures. |
| const char kEmptySignature[] = ""; |
| const char kEmptyDictSignature[] = "a{}"; |
| const char kMissingArraryParameterSignature[] = "a"; |
| const char kMissingArraryParameterInnerSignature[] = "a{sa}i"; |
| const char kOrphanDictSignature[] = "a{s{i}}"; |
| const char kTooFewDictMembersSignature[] = "a{s}"; |
| const char kTooManyDictMembersSignature[] = "a{sa{i}u}"; |
| const char kUnclosedDictOuterSignature[] = "a{s"; |
| const char kUnclosedDictInnerSignature[] = "a{a{u}"; |
| const char kUnexpectedCloseSignature[] = "a}i{"; |
| const char kUnknownSignature[] = "al"; |
| |
| // Signature for protobuf type |
| const char kMyProtobufClassName[] = "MyProtobufClass"; |
| |
| } // namespace |
| |
| TEST(DBusSignatureTest, ParseFailures) { |
| DBusSignature signature; |
| for (const auto& failing_string : { kEmptySignature, |
| kEmptyDictSignature, |
| kMissingArraryParameterSignature, |
| kMissingArraryParameterInnerSignature, |
| kOrphanDictSignature, |
| kTooFewDictMembersSignature, |
| kTooManyDictMembersSignature, |
| kUnclosedDictOuterSignature, |
| kUnclosedDictInnerSignature, |
| kUnexpectedCloseSignature, |
| kUnknownSignature }) { |
| EXPECT_FALSE(signature.Parse(failing_string)) |
| << "Expected signature " << failing_string |
| << " to fail but it succeeded"; |
| } |
| } |
| |
| TEST(DBusSignatureTest, ParseSuccesses) { |
| DBusSignature signature; |
| const map<string, string> parse_values { |
| // Simple types. |
| { DBUS_TYPE_BOOLEAN_AS_STRING, "bool" }, |
| { DBUS_TYPE_BYTE_AS_STRING, "uint8_t" }, |
| { DBUS_TYPE_DOUBLE_AS_STRING, "double" }, |
| { DBUS_TYPE_OBJECT_PATH_AS_STRING, "dbus::ObjectPath" }, |
| { DBUS_TYPE_INT16_AS_STRING, "int16_t" }, |
| { DBUS_TYPE_INT32_AS_STRING, "int32_t" }, |
| { DBUS_TYPE_INT64_AS_STRING, "int64_t" }, |
| { DBUS_TYPE_STRING_AS_STRING, "std::string" }, |
| { DBUS_TYPE_UINT16_AS_STRING, "uint16_t" }, |
| { DBUS_TYPE_UINT32_AS_STRING, "uint32_t" }, |
| { DBUS_TYPE_UINT64_AS_STRING, "uint64_t" }, |
| { DBUS_TYPE_VARIANT_AS_STRING, "brillo::Any" }, |
| |
| // Complex types. |
| { "ab", "std::vector<bool>" }, |
| { "ay", "std::vector<uint8_t>" }, |
| { "aay", "std::vector<std::vector<uint8_t>>" }, |
| { "ao", "std::vector<dbus::ObjectPath>" }, |
| { "a{oa{sa{sv}}}", "std::map<dbus::ObjectPath, std::map<std::string, " |
| "brillo::VariantDictionary>>" }, |
| { "a{os}", "std::map<dbus::ObjectPath, std::string>" }, |
| { "as", "std::vector<std::string>" }, |
| { "a{ss}", "std::map<std::string, std::string>" }, |
| { "a{sa{ss}}", "std::map<std::string, std::map<std::string, " |
| "std::string>>"}, |
| { "a{sa{sv}}", "std::map<std::string, brillo::VariantDictionary>" }, |
| { "a{sv}", "brillo::VariantDictionary" }, |
| { "a{sv}Garbage", "brillo::VariantDictionary" }, |
| { "at", "std::vector<uint64_t>" }, |
| { "a{iv}", "std::map<int32_t, brillo::Any>" }, |
| { "(ib)", "std::tuple<int32_t, bool>" }, |
| { "(ibs)", "std::tuple<int32_t, bool, std::string>" }, |
| }; |
| for (const auto& parse_test : parse_values) { |
| auto type = signature.Parse(parse_test.first); |
| EXPECT_TRUE(type) |
| << "Expected signature " << parse_test.first |
| << " to succeed but it failed."; |
| |
| string output = type->GetBaseType(Direction::kAppend); |
| EXPECT_EQ(parse_test.second, output) |
| << "Expected typename for " << parse_test.first << " to be " |
| << parse_test.second << " but instead it was " << output; |
| output = type->GetBaseType(Direction::kExtract); |
| EXPECT_EQ(parse_test.second, output) |
| << "Expected typename for " << parse_test.first << " to be " |
| << parse_test.second << " but instead it was " << output; |
| } |
| } |
| |
| // Scalar types should not have reference behavior when used as in-args, and |
| // should just produce the base type as their in-arg type. |
| TEST(DBusSignatureTest, ScalarTypes) { |
| DBusSignature signature; |
| const std::vector<string> parse_values{ |
| DBUS_TYPE_BOOLEAN_AS_STRING, |
| DBUS_TYPE_BYTE_AS_STRING, |
| DBUS_TYPE_DOUBLE_AS_STRING, |
| DBUS_TYPE_INT16_AS_STRING, |
| DBUS_TYPE_INT32_AS_STRING, |
| DBUS_TYPE_INT64_AS_STRING, |
| DBUS_TYPE_UINT16_AS_STRING, |
| DBUS_TYPE_UINT32_AS_STRING, |
| DBUS_TYPE_UINT64_AS_STRING, |
| }; |
| |
| for (const auto& parse_test : parse_values) { |
| auto type = signature.Parse(parse_test); |
| EXPECT_TRUE(type); |
| EXPECT_EQ(type->GetBaseType(Direction::kExtract), |
| type->GetInArgType(Receiver::kAdaptor)); |
| EXPECT_EQ(type->GetBaseType(Direction::kAppend), |
| type->GetInArgType(Receiver::kProxy)); |
| } |
| } |
| |
| // Non-scalar types should have const reference behavior when used as in-args. |
| // The references should not be nested. |
| TEST(DBusSignatureTest, NonScalarTypes) { |
| DBusSignature signature; |
| const map<string, string> parse_values{ |
| { "o", "const dbus::ObjectPath&" }, |
| { "s", "const std::string&" }, |
| { "v", "const brillo::Any&" }, |
| { "ab", "const std::vector<bool>&" }, |
| { "ay", "const std::vector<uint8_t>&" }, |
| { "aay", "const std::vector<std::vector<uint8_t>>&" }, |
| { "ao", "const std::vector<dbus::ObjectPath>&" }, |
| { "a{oa{sa{sv}}}", "const std::map<dbus::ObjectPath, std::map<" |
| "std::string, brillo::VariantDictionary>>&" }, |
| { "a{os}", "const std::map<dbus::ObjectPath, std::string>&" }, |
| { "as", "const std::vector<std::string>&" }, |
| { "a{ss}", "const std::map<std::string, std::string>&" }, |
| { "a{sa{ss}}", "const std::map<std::string, std::map<std::string, " |
| "std::string>>&" }, |
| { "a{sa{sv}}", "const std::map<std::string, " |
| "brillo::VariantDictionary>&" }, |
| { "a{sv}", "const brillo::VariantDictionary&" }, |
| { "at", "const std::vector<uint64_t>&" }, |
| { "a{iv}", "const std::map<int32_t, brillo::Any>&" }, |
| { "(ib)", "const std::tuple<int32_t, bool>&" }, |
| { "(ibs)", "const std::tuple<int32_t, bool, std::string>&" }, |
| }; |
| |
| for (const auto& parse_test : parse_values) { |
| auto type = signature.Parse(parse_test.first); |
| EXPECT_TRUE(type); |
| EXPECT_EQ(parse_test.second, type->GetInArgType(Receiver::kAdaptor)); |
| EXPECT_EQ(parse_test.second, type->GetInArgType(Receiver::kProxy)); |
| } |
| } |
| |
| // Out-args should be pointers, but only at the top level. |
| TEST(DBusSignatureTest, OutArgTypes) { |
| DBusSignature signature; |
| const map<string, string> parse_values{ |
| { "b", "bool*" }, |
| { "y", "uint8_t*" }, |
| { "i", "int32_t*" }, |
| { "t", "uint64_t*" }, |
| { "o", "dbus::ObjectPath*" }, |
| { "s", "std::string*" }, |
| { "v", "brillo::Any*" }, |
| { "ab", "std::vector<bool>*" }, |
| { "ay", "std::vector<uint8_t>*" }, |
| { "aay", "std::vector<std::vector<uint8_t>>*" }, |
| { "ao", "std::vector<dbus::ObjectPath>*" }, |
| { "a{oa{sa{sv}}}", "std::map<dbus::ObjectPath, std::map<" |
| "std::string, brillo::VariantDictionary>>*" }, |
| { "a{os}", "std::map<dbus::ObjectPath, std::string>*" }, |
| { "as", "std::vector<std::string>*" }, |
| { "a{ss}", "std::map<std::string, std::string>*" }, |
| { "a{sa{ss}}", "std::map<std::string, std::map<std::string, " |
| "std::string>>*" }, |
| { "a{sa{sv}}", "std::map<std::string, " |
| "brillo::VariantDictionary>*" }, |
| { "a{sv}", "brillo::VariantDictionary*" }, |
| { "at", "std::vector<uint64_t>*" }, |
| { "a{iv}", "std::map<int32_t, brillo::Any>*" }, |
| { "(ib)", "std::tuple<int32_t, bool>*" }, |
| { "(ibs)", "std::tuple<int32_t, bool, std::string>*" }, |
| }; |
| |
| for (const auto& parse_test : parse_values) { |
| auto type = signature.Parse(parse_test.first); |
| EXPECT_TRUE(type); |
| EXPECT_EQ(parse_test.second, type->GetOutArgType(Receiver::kAdaptor)); |
| EXPECT_EQ(parse_test.second, type->GetOutArgType(Receiver::kProxy)); |
| } |
| } |
| |
| // Test to ensure that file descriptors at varying levels of depth do |
| // not produce valid types. |
| TEST(DBusSignatureTest, IsValidPropertyType) { |
| DBusSignature signature; |
| const std::vector<string> valid_property_types{ |
| "b", |
| "y", |
| "i", |
| "t", |
| "o", |
| "s", |
| "v", |
| "ab", |
| "ay", |
| "aay", |
| "ao", |
| "a{oa{sa{sv}}}", |
| "a{os}", |
| "as", |
| "a{ss}", |
| "a{sa{ss}}", |
| "a{sa{sv}}", |
| "a{sv}", |
| "at", |
| "a{iv}", |
| "(ib)", |
| "(ibs)", |
| }; |
| |
| for (const auto& parse_test : valid_property_types) { |
| auto type = signature.Parse(parse_test); |
| EXPECT_TRUE(type); |
| EXPECT_TRUE(type->IsValidPropertyType()); |
| } |
| |
| const std::vector<string> invalid_property_types{ |
| "h", |
| "ah", |
| "aah", |
| "a{sh}", |
| "a{ia{oh}}", |
| "a{hi}", |
| "(sih)", |
| "a(ta{sh})", |
| }; |
| |
| for (const auto& parse_test : invalid_property_types) { |
| auto type = signature.Parse(parse_test); |
| EXPECT_TRUE(type); |
| EXPECT_FALSE(type->IsValidPropertyType()); |
| } |
| } |
| |
| TEST(DBusSignatureTest, FileDescriptors) { |
| DBusSignature signature; |
| |
| auto type = signature.Parse(DBUS_TYPE_UNIX_FD_AS_STRING); |
| EXPECT_TRUE(type); |
| // for_extraction does matter now. |
| EXPECT_EQ("brillo::dbus_utils::FileDescriptor", |
| type->GetBaseType(Direction::kAppend)); |
| EXPECT_EQ("base::ScopedFD", type->GetBaseType(Direction::kExtract)); |
| // for_adaptor propagates as a different for_extraction as well. |
| EXPECT_EQ("const brillo::dbus_utils::FileDescriptor&", |
| type->GetInArgType(Receiver::kProxy)); |
| EXPECT_EQ("const base::ScopedFD&", type->GetInArgType(Receiver::kAdaptor)); |
| EXPECT_EQ("base::ScopedFD*", type->GetOutArgType(Receiver::kProxy)); |
| EXPECT_EQ("brillo::dbus_utils::FileDescriptor*", |
| type->GetOutArgType(Receiver::kAdaptor)); |
| |
| // Check that more involved types are correct as well. |
| type = signature.Parse("ah"); |
| EXPECT_EQ("std::vector<brillo::dbus_utils::FileDescriptor>", |
| type->GetBaseType(Direction::kAppend)); |
| EXPECT_EQ("std::vector<base::ScopedFD>", |
| type->GetBaseType(Direction::kExtract)); |
| |
| type = signature.Parse("a{ih}"); |
| EXPECT_EQ("std::map<int32_t, brillo::dbus_utils::FileDescriptor>", |
| type->GetBaseType(Direction::kAppend)); |
| EXPECT_EQ("std::map<int32_t, base::ScopedFD>", |
| type->GetBaseType(Direction::kExtract)); |
| |
| type = signature.Parse("(ih)"); |
| EXPECT_EQ("std::tuple<int32_t, brillo::dbus_utils::FileDescriptor>", |
| type->GetBaseType(Direction::kAppend)); |
| EXPECT_EQ("std::tuple<int32_t, base::ScopedFD>", |
| type->GetBaseType(Direction::kExtract)); |
| } |
| |
| TEST(DBusSignatureTest, Protobufs) { |
| DBusSignature signature; |
| |
| auto type = signature.Parse(string(kProtobufType)+kMyProtobufClassName); |
| EXPECT_TRUE(type); |
| |
| EXPECT_EQ("MyProtobufClass", type->GetBaseType(Direction::kAppend)); |
| EXPECT_EQ("MyProtobufClass", type->GetBaseType(Direction::kExtract)); |
| |
| EXPECT_EQ("const MyProtobufClass&", type->GetInArgType(Receiver::kAdaptor)); |
| EXPECT_EQ("const MyProtobufClass&", type->GetInArgType(Receiver::kProxy)); |
| |
| EXPECT_EQ("MyProtobufClass*", type->GetOutArgType(Receiver::kAdaptor)); |
| EXPECT_EQ("MyProtobufClass*", type->GetOutArgType(Receiver::kProxy)); |
| } |
| |
| } // namespace chromeos_dbus_bindings |