blob: c5b84a74cba08bffeb24bf3f27bed6ece3086668 [file] [log] [blame]
// 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