// 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/adaptor_generator.h" #include <string> #include <vector> #include <base/files/file_path.h> #include <base/files/file_util.h> #include <base/files/scoped_temp_dir.h> #include <gtest/gtest.h> #include "chromeos-dbus-bindings/interface.h" #include "chromeos-dbus-bindings/test_utils.h" using std::string; using std::vector; using testing::Test; namespace chromeos_dbus_bindings { namespace { const char kDBusTypeArryOfObjects[] = "ao"; const char kDBusTypeBool[] = "b"; const char kDBusTypeInt32[] = "i"; const char kDBusTypeInt64[] = "x"; const char kDBusTypeString[] = "s"; const char kPropertyAccessReadOnly[] = "read"; const char kPropertyAccessReadWrite[] = "readwrite"; const char kInterfaceName[] = "org.chromium.Test"; const char kInterfaceName2[] = "org.chromium.Test2"; const char kExpectedContent[] = R"literal_string( #include <memory> #include <string> #include <tuple> #include <vector> #include <base/macros.h> #include <dbus/object_path.h> #include <brillo/any.h> #include <brillo/dbus/dbus_object.h> #include <brillo/dbus/exported_object_manager.h> #include <brillo/variant_dictionary.h> namespace org { namespace chromium { // Interface definition for org::chromium::Test. class TestInterface { public: virtual ~TestInterface() = default; virtual bool Kaneda( brillo::ErrorPtr* error, dbus::Message* message, const std::string& in_iwata, const std::vector<dbus::ObjectPath>& in_clarke, std::string* out_3) = 0; virtual bool Tetsuo( brillo::ErrorPtr* error, int32_t in_1, int64_t* out_2) = 0; virtual bool Kei( brillo::ErrorPtr* error) = 0; virtual bool Kiyoko( brillo::ErrorPtr* error, int64_t* out_akira, std::string* out_2) = 0; }; // Interface adaptor for org::chromium::Test. class TestAdaptor { public: TestAdaptor(TestInterface* interface) : interface_(interface) {} void RegisterWithDBusObject(brillo::dbus_utils::DBusObject* object) { brillo::dbus_utils::DBusInterface* itf = object->AddOrGetInterface("org.chromium.Test"); itf->AddSimpleMethodHandlerWithErrorAndMessage( "Kaneda", base::Unretained(interface_), &TestInterface::Kaneda); itf->AddSimpleMethodHandlerWithError( "Tetsuo", base::Unretained(interface_), &TestInterface::Tetsuo); itf->AddSimpleMethodHandlerWithError( "Kei", base::Unretained(interface_), &TestInterface::Kei); itf->AddSimpleMethodHandlerWithError( "Kiyoko", base::Unretained(interface_), &TestInterface::Kiyoko); signal_Update_ = itf->RegisterSignalOfType<SignalUpdateType>("Update"); signal_Mapping_ = itf->RegisterSignalOfType<SignalMappingType>("Mapping"); itf->AddProperty(CharacterNameName(), &character_name_); write_property_.SetAccessMode( brillo::dbus_utils::ExportedPropertyBase::Access::kReadWrite); write_property_.SetValidator( base::Bind(&TestAdaptor::ValidateWriteProperty, base::Unretained(this))); itf->AddProperty(WritePropertyName(), &write_property_); } void SendUpdateSignal() { auto signal = signal_Update_.lock(); if (signal) signal->Send(); } void SendMappingSignal( const std::string& in_key, const std::vector<dbus::ObjectPath>& in_2) { auto signal = signal_Mapping_.lock(); if (signal) signal->Send(in_key, in_2); } static const char* CharacterNameName() { return "CharacterName"; } std::string GetCharacterName() const { return character_name_.GetValue().Get<std::string>(); } void SetCharacterName(const std::string& character_name) { character_name_.SetValue(character_name); } static const char* WritePropertyName() { return "WriteProperty"; } std::string GetWriteProperty() const { return write_property_.GetValue().Get<std::string>(); } void SetWriteProperty(const std::string& write_property) { write_property_.SetValue(write_property); } virtual bool ValidateWriteProperty( brillo::ErrorPtr* /*error*/, const std::string& /*value*/) { return true; } static dbus::ObjectPath GetObjectPath() { return dbus::ObjectPath{"/org/chromium/Test"}; } private: using SignalUpdateType = brillo::dbus_utils::DBusSignal<>; std::weak_ptr<SignalUpdateType> signal_Update_; using SignalMappingType = brillo::dbus_utils::DBusSignal< std::string /*key*/, std::vector<dbus::ObjectPath>>; std::weak_ptr<SignalMappingType> signal_Mapping_; brillo::dbus_utils::ExportedProperty<std::string> character_name_; brillo::dbus_utils::ExportedProperty<std::string> write_property_; TestInterface* interface_; // Owned by container of this adapter. DISALLOW_COPY_AND_ASSIGN(TestAdaptor); }; } // namespace chromium } // namespace org namespace org { namespace chromium { // Interface definition for org::chromium::Test2. class Test2Interface { public: virtual ~Test2Interface() = default; virtual std::string Kaneda2( const std::string& in_iwata) const = 0; virtual void Tetsuo2( std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<int64_t>> response, int32_t in_1) = 0; virtual void Kei2( std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<bool>> response, dbus::Message* message) = 0; }; // Interface adaptor for org::chromium::Test2. class Test2Adaptor { public: Test2Adaptor(Test2Interface* interface) : interface_(interface) {} void RegisterWithDBusObject(brillo::dbus_utils::DBusObject* object) { brillo::dbus_utils::DBusInterface* itf = object->AddOrGetInterface("org.chromium.Test2"); itf->AddSimpleMethodHandler( "Kaneda2", base::Unretained(interface_), &Test2Interface::Kaneda2); itf->AddMethodHandler( "Tetsuo2", base::Unretained(interface_), &Test2Interface::Tetsuo2); itf->AddMethodHandlerWithMessage( "Kei2", base::Unretained(interface_), &Test2Interface::Kei2); } private: Test2Interface* interface_; // Owned by container of this adapter. DISALLOW_COPY_AND_ASSIGN(Test2Adaptor); }; } // namespace chromium } // namespace org )literal_string"; } // namespace class AdaptorGeneratorTest : public Test { public: void SetUp() override { ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); } protected: base::FilePath CreateInputFile(const string& contents) { base::FilePath path; EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &path)); int written = base::WriteFile(path, contents.c_str(), contents.size()); EXPECT_EQ(contents.size(), static_cast<size_t>(written)); return path; } base::ScopedTempDir temp_dir_; }; TEST_F(AdaptorGeneratorTest, GenerateAdaptors) { Interface interface; interface.name = kInterfaceName; interface.path = "/org/chromium/Test"; interface.methods.emplace_back( "Kaneda", vector<Interface::Argument>{ {"iwata", kDBusTypeString}, {"clarke", kDBusTypeArryOfObjects}}, vector<Interface::Argument>{{"", kDBusTypeString}}); interface.methods.back().include_dbus_message = true; interface.methods.emplace_back( "Tetsuo", vector<Interface::Argument>{{"", kDBusTypeInt32}}, vector<Interface::Argument>{{"", kDBusTypeInt64}}); interface.methods.emplace_back("Kei"); // Interface methods with more than one return argument should be ignored. interface.methods.emplace_back( "Kiyoko", vector<Interface::Argument>{}, vector<Interface::Argument>{ {"akira", kDBusTypeInt64}, {"", kDBusTypeString}}); // Signals generate helper methods to send them. interface.signals.emplace_back( "Update", vector<Interface::Argument>{}); interface.signals.emplace_back( "Mapping", vector<Interface::Argument>{ {"key", kDBusTypeString}, {"", kDBusTypeArryOfObjects}}); interface.properties.emplace_back( "CharacterName", kDBusTypeString, kPropertyAccessReadOnly); interface.properties.emplace_back( "WriteProperty", kDBusTypeString, kPropertyAccessReadWrite); Interface interface2; interface2.name = kInterfaceName2; interface2.methods.emplace_back( "Kaneda2", vector<Interface::Argument>{{"iwata", kDBusTypeString}}, vector<Interface::Argument>{{"", kDBusTypeString}}); interface2.methods.back().is_const = true; interface2.methods.back().kind = Interface::Method::Kind::kSimple; interface2.methods.emplace_back( "Tetsuo2", vector<Interface::Argument>{{"", kDBusTypeInt32}}, vector<Interface::Argument>{{"", kDBusTypeInt64}}); interface2.methods.back().kind = Interface::Method::Kind::kAsync; interface2.methods.emplace_back( "Kei2", vector<Interface::Argument>{}, vector<Interface::Argument>{{"", kDBusTypeBool}}); interface2.methods.back().kind = Interface::Method::Kind::kAsync; interface2.methods.back().include_dbus_message = true; base::FilePath output_path = temp_dir_.path().Append("output.h"); EXPECT_TRUE(AdaptorGenerator::GenerateAdaptors({interface, interface2}, output_path)); string contents; EXPECT_TRUE(base::ReadFileToString(output_path, &contents)); // The header guards contain the (temporary) filename, so we search for // the content we need within the string. test_utils::EXPECT_TEXT_CONTAINED(kExpectedContent, contents); } } // namespace chromeos_dbus_bindings