1// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromeos-dbus-bindings/adaptor_generator.h"
6
7#include <string>
8#include <vector>
9
10#include <base/files/file_path.h>
11#include <base/files/file_util.h>
12#include <base/files/scoped_temp_dir.h>
13#include <gtest/gtest.h>
14
15#include "chromeos-dbus-bindings/interface.h"
16#include "chromeos-dbus-bindings/test_utils.h"
17
18using std::string;
19using std::vector;
20using testing::Test;
21
22namespace chromeos_dbus_bindings {
23
24namespace {
25
26const char kDBusTypeArryOfObjects[] = "ao";
27const char kDBusTypeBool[] = "b";
28const char kDBusTypeInt32[] = "i";
29const char kDBusTypeInt64[] = "x";
30const char kDBusTypeString[] = "s";
31
32const char kPropertyAccessReadOnly[] = "read";
33const char kPropertyAccessReadWrite[] = "readwrite";
34
35const char kInterfaceName[] = "org.chromium.Test";
36const char kInterfaceName2[] = "org.chromium.Test2";
37
38const char kExpectedContent[] = R"literal_string(
39#include <memory>
40#include <string>
41#include <tuple>
42#include <vector>
43
44#include <base/macros.h>
45#include <dbus/object_path.h>
46#include <brillo/any.h>
47#include <brillo/dbus/dbus_object.h>
48#include <brillo/dbus/exported_object_manager.h>
49#include <brillo/variant_dictionary.h>
50
51namespace org {
52namespace chromium {
53
54// Interface definition for org::chromium::Test.
55class TestInterface {
56 public:
57  virtual ~TestInterface() = default;
58
59  virtual bool Kaneda(
60      brillo::ErrorPtr* error,
61      dbus::Message* message,
62      const std::string& in_iwata,
63      const std::vector<dbus::ObjectPath>& in_clarke,
64      std::string* out_3) = 0;
65  virtual bool Tetsuo(
66      brillo::ErrorPtr* error,
67      int32_t in_1,
68      int64_t* out_2) = 0;
69  virtual bool Kei(
70      brillo::ErrorPtr* error) = 0;
71  virtual bool Kiyoko(
72      brillo::ErrorPtr* error,
73      int64_t* out_akira,
74      std::string* out_2) = 0;
75};
76
77// Interface adaptor for org::chromium::Test.
78class TestAdaptor {
79 public:
80  TestAdaptor(TestInterface* interface) : interface_(interface) {}
81
82  void RegisterWithDBusObject(brillo::dbus_utils::DBusObject* object) {
83    brillo::dbus_utils::DBusInterface* itf =
84        object->AddOrGetInterface("org.chromium.Test");
85
86    itf->AddSimpleMethodHandlerWithErrorAndMessage(
87        "Kaneda",
88        base::Unretained(interface_),
89        &TestInterface::Kaneda);
90    itf->AddSimpleMethodHandlerWithError(
91        "Tetsuo",
92        base::Unretained(interface_),
93        &TestInterface::Tetsuo);
94    itf->AddSimpleMethodHandlerWithError(
95        "Kei",
96        base::Unretained(interface_),
97        &TestInterface::Kei);
98    itf->AddSimpleMethodHandlerWithError(
99        "Kiyoko",
100        base::Unretained(interface_),
101        &TestInterface::Kiyoko);
102
103    signal_Update_ = itf->RegisterSignalOfType<SignalUpdateType>("Update");
104    signal_Mapping_ = itf->RegisterSignalOfType<SignalMappingType>("Mapping");
105
106    itf->AddProperty(CharacterNameName(), &character_name_);
107    write_property_.SetAccessMode(
108        brillo::dbus_utils::ExportedPropertyBase::Access::kReadWrite);
109    write_property_.SetValidator(
110        base::Bind(&TestAdaptor::ValidateWriteProperty,
111                   base::Unretained(this)));
112    itf->AddProperty(WritePropertyName(), &write_property_);
113  }
114
115  void SendUpdateSignal() {
116    auto signal = signal_Update_.lock();
117    if (signal)
118      signal->Send();
119  }
120  void SendMappingSignal(
121      const std::string& in_key,
122      const std::vector<dbus::ObjectPath>& in_2) {
123    auto signal = signal_Mapping_.lock();
124    if (signal)
125      signal->Send(in_key, in_2);
126  }
127
128  static const char* CharacterNameName() { return "CharacterName"; }
129  std::string GetCharacterName() const {
130    return character_name_.GetValue().Get<std::string>();
131  }
132  void SetCharacterName(const std::string& character_name) {
133    character_name_.SetValue(character_name);
134  }
135
136  static const char* WritePropertyName() { return "WriteProperty"; }
137  std::string GetWriteProperty() const {
138    return write_property_.GetValue().Get<std::string>();
139  }
140  void SetWriteProperty(const std::string& write_property) {
141    write_property_.SetValue(write_property);
142  }
143  virtual bool ValidateWriteProperty(
144      brillo::ErrorPtr* /*error*/, const std::string& /*value*/) {
145    return true;
146  }
147
148  static dbus::ObjectPath GetObjectPath() {
149    return dbus::ObjectPath{"/org/chromium/Test"};
150  }
151
152 private:
153  using SignalUpdateType = brillo::dbus_utils::DBusSignal<>;
154  std::weak_ptr<SignalUpdateType> signal_Update_;
155
156  using SignalMappingType = brillo::dbus_utils::DBusSignal<
157      std::string /*key*/,
158      std::vector<dbus::ObjectPath>>;
159  std::weak_ptr<SignalMappingType> signal_Mapping_;
160
161  brillo::dbus_utils::ExportedProperty<std::string> character_name_;
162  brillo::dbus_utils::ExportedProperty<std::string> write_property_;
163
164  TestInterface* interface_;  // Owned by container of this adapter.
165
166  DISALLOW_COPY_AND_ASSIGN(TestAdaptor);
167};
168
169}  // namespace chromium
170}  // namespace org
171
172namespace org {
173namespace chromium {
174
175// Interface definition for org::chromium::Test2.
176class Test2Interface {
177 public:
178  virtual ~Test2Interface() = default;
179
180  virtual std::string Kaneda2(
181      const std::string& in_iwata) const = 0;
182  virtual void Tetsuo2(
183      std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<int64_t>> response,
184      int32_t in_1) = 0;
185  virtual void Kei2(
186      std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<bool>> response,
187      dbus::Message* message) = 0;
188};
189
190// Interface adaptor for org::chromium::Test2.
191class Test2Adaptor {
192 public:
193  Test2Adaptor(Test2Interface* interface) : interface_(interface) {}
194
195  void RegisterWithDBusObject(brillo::dbus_utils::DBusObject* object) {
196    brillo::dbus_utils::DBusInterface* itf =
197        object->AddOrGetInterface("org.chromium.Test2");
198
199    itf->AddSimpleMethodHandler(
200        "Kaneda2",
201        base::Unretained(interface_),
202        &Test2Interface::Kaneda2);
203    itf->AddMethodHandler(
204        "Tetsuo2",
205        base::Unretained(interface_),
206        &Test2Interface::Tetsuo2);
207    itf->AddMethodHandlerWithMessage(
208        "Kei2",
209        base::Unretained(interface_),
210        &Test2Interface::Kei2);
211  }
212
213 private:
214  Test2Interface* interface_;  // Owned by container of this adapter.
215
216  DISALLOW_COPY_AND_ASSIGN(Test2Adaptor);
217};
218
219}  // namespace chromium
220}  // namespace org
221)literal_string";
222
223}  // namespace
224class AdaptorGeneratorTest : public Test {
225 public:
226  void SetUp() override {
227    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
228  }
229
230 protected:
231  base::FilePath CreateInputFile(const string& contents) {
232    base::FilePath path;
233    EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_.path(), &path));
234    int written = base::WriteFile(path, contents.c_str(), contents.size());
235    EXPECT_EQ(contents.size(), static_cast<size_t>(written));
236    return path;
237  }
238
239  base::ScopedTempDir temp_dir_;
240};
241
242TEST_F(AdaptorGeneratorTest, GenerateAdaptors) {
243  Interface interface;
244  interface.name = kInterfaceName;
245  interface.path = "/org/chromium/Test";
246  interface.methods.emplace_back(
247      "Kaneda",
248      vector<Interface::Argument>{
249          {"iwata", kDBusTypeString},
250          {"clarke", kDBusTypeArryOfObjects}},
251      vector<Interface::Argument>{{"", kDBusTypeString}});
252  interface.methods.back().include_dbus_message = true;
253  interface.methods.emplace_back(
254      "Tetsuo",
255      vector<Interface::Argument>{{"", kDBusTypeInt32}},
256      vector<Interface::Argument>{{"", kDBusTypeInt64}});
257  interface.methods.emplace_back("Kei");
258  // Interface methods with more than one return argument should be ignored.
259  interface.methods.emplace_back(
260      "Kiyoko",
261      vector<Interface::Argument>{},
262      vector<Interface::Argument>{
263          {"akira", kDBusTypeInt64},
264          {"", kDBusTypeString}});
265  // Signals generate helper methods to send them.
266  interface.signals.emplace_back(
267      "Update",
268      vector<Interface::Argument>{});
269  interface.signals.emplace_back(
270      "Mapping",
271      vector<Interface::Argument>{
272          {"key", kDBusTypeString},
273          {"", kDBusTypeArryOfObjects}});
274  interface.properties.emplace_back(
275      "CharacterName",
276      kDBusTypeString,
277      kPropertyAccessReadOnly);
278  interface.properties.emplace_back(
279      "WriteProperty",
280      kDBusTypeString,
281      kPropertyAccessReadWrite);
282
283  Interface interface2;
284  interface2.name = kInterfaceName2;
285  interface2.methods.emplace_back(
286      "Kaneda2",
287      vector<Interface::Argument>{{"iwata", kDBusTypeString}},
288      vector<Interface::Argument>{{"", kDBusTypeString}});
289  interface2.methods.back().is_const = true;
290  interface2.methods.back().kind = Interface::Method::Kind::kSimple;
291  interface2.methods.emplace_back(
292      "Tetsuo2",
293      vector<Interface::Argument>{{"", kDBusTypeInt32}},
294      vector<Interface::Argument>{{"", kDBusTypeInt64}});
295  interface2.methods.back().kind = Interface::Method::Kind::kAsync;
296  interface2.methods.emplace_back(
297      "Kei2",
298      vector<Interface::Argument>{},
299      vector<Interface::Argument>{{"", kDBusTypeBool}});
300  interface2.methods.back().kind = Interface::Method::Kind::kAsync;
301  interface2.methods.back().include_dbus_message = true;
302
303  base::FilePath output_path = temp_dir_.path().Append("output.h");
304  EXPECT_TRUE(AdaptorGenerator::GenerateAdaptors({interface, interface2},
305                                                 output_path));
306  string contents;
307  EXPECT_TRUE(base::ReadFileToString(output_path, &contents));
308  // The header guards contain the (temporary) filename, so we search for
309  // the content we need within the string.
310  test_utils::EXPECT_TEXT_CONTAINED(kExpectedContent, contents);
311}
312
313}  // namespace chromeos_dbus_bindings
314