buffet: Add ExportedObjectManager delegate
This makes it easy to export an object manager. We'll use this very
soon to implement the ObjectManager interface on the root Buffet
object.
BUG=chromium:359190
TEST=Unittests
Change-Id: I19d2da33b81557431c5787937c49a18e7d7bacb2
Reviewed-on: https://chromium-review.googlesource.com/196387
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Christopher Wiley <wiley@chromium.org>
Tested-by: Christopher Wiley <wiley@chromium.org>
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
index c4c5a1b..b94677c 100644
--- a/buffet/buffet.gyp
+++ b/buffet/buffet.gyp
@@ -32,6 +32,7 @@
'dbus_manager.cc',
'dbus_utils.cc',
'device_registration_info.cc',
+ 'exported_object_manager.cc',
'exported_property_set.cc',
'http_request.cc',
'http_connection_curl.cc',
@@ -77,6 +78,7 @@
'buffet_testrunner.cc',
'data_encoding_unittest.cc',
'device_registration_info_unittest.cc',
+ 'exported_object_manager_unittest.cc',
'exported_property_set_unittest.cc',
'http_connection_fake.cc',
'http_transport_fake.cc',
diff --git a/buffet/exported_object_manager.cc b/buffet/exported_object_manager.cc
new file mode 100644
index 0000000..6458128
--- /dev/null
+++ b/buffet/exported_object_manager.cc
@@ -0,0 +1,133 @@
+// 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 "buffet/exported_object_manager.h"
+
+#include <dbus/object_manager.h>
+
+#include "buffet/async_event_sequencer.h"
+
+namespace buffet {
+
+namespace dbus_utils {
+
+ExportedObjectManager::ExportedObjectManager(dbus::Bus* bus,
+ const dbus::ObjectPath& path)
+ : bus_(bus), exported_object_(bus->GetExportedObject(path)),
+ weak_ptr_factory_(this) {}
+
+void ExportedObjectManager::Init(const OnInitFinish& cb) {
+ bus_->AssertOnOriginThread();
+ scoped_refptr<dbus_utils::AsyncEventSequencer> sequencer(
+ new dbus_utils::AsyncEventSequencer());
+ exported_object_->ExportMethod(
+ dbus::kObjectManagerInterface,
+ dbus::kObjectManagerGetManagedObjects,
+ base::Bind(&ExportedObjectManager::HandleGetManagedObjects,
+ weak_ptr_factory_.GetWeakPtr()),
+ sequencer->GetExportHandler(
+ dbus::kObjectManagerInterface,
+ dbus::kObjectManagerGetManagedObjects,
+ "Failed exporting GetManagedObjects method of ObjectManager",
+ false));
+}
+
+void ExportedObjectManager::ClaimInterface(
+ const dbus::ObjectPath& path,
+ const std::string& interface_name,
+ const PropertyWriter& property_writer) {
+ bus_->AssertOnOriginThread();
+ // We're sending signals that look like:
+ // org.freedesktop.DBus.ObjectManager.InterfacesAdded (
+ // OBJPATH object_path,
+ // DICT<STRING,DICT<STRING,VARIANT>> interfaces_and_properties);
+ dbus::Signal signal(dbus::kObjectManagerInterface,
+ dbus::kObjectManagerInterfacesAdded);
+ dbus::MessageWriter signal_writer(&signal);
+ dbus::MessageWriter all_interfaces(&signal);
+ dbus::MessageWriter each_interface(&signal);
+ signal_writer.AppendObjectPath(path);
+ signal_writer.OpenArray("{sa{sv}}", &all_interfaces);
+ all_interfaces.OpenDictEntry(&each_interface);
+ each_interface.AppendString(interface_name);
+ property_writer.Run(&each_interface);
+ all_interfaces.CloseContainer(&each_interface);
+ signal_writer.CloseContainer(&all_interfaces);
+ exported_object_->SendSignal(&signal);
+ registered_objects_[path][interface_name] = property_writer;
+}
+
+void ExportedObjectManager::ReleaseInterface(
+ const dbus::ObjectPath& path, const std::string& interface_name) {
+ bus_->AssertOnOriginThread();
+ auto interfaces_for_path_itr = registered_objects_.find(path);
+ CHECK(interfaces_for_path_itr != registered_objects_.end())
+ << "Attempting to signal interface removal for path " << path.value()
+ << " which was never registered.";
+ auto interfaces_for_path = interfaces_for_path_itr->second;
+ auto property_for_interface_itr = interfaces_for_path.find(interface_name);
+ CHECK(property_for_interface_itr != interfaces_for_path.end())
+ << "Attempted to remove interface " << interface_name << " from "
+ << path.value() << ", but this interface was never registered.";
+ interfaces_for_path.erase(interface_name);
+ if (interfaces_for_path.size() < 1) {
+ registered_objects_.erase(path);
+ }
+ // We're sending signals that look like:
+ // org.freedesktop.DBus.ObjectManager.InterfacesRemoved (
+ // OBJPATH object_path, ARRAY<STRING> interfaces);
+ dbus::Signal signal(dbus::kObjectManagerInterface,
+ dbus::kObjectManagerInterfacesRemoved);
+ dbus::MessageWriter signal_writer(&signal);
+ signal_writer.AppendObjectPath(path);
+ dbus::MessageWriter interface_writer(nullptr);
+ signal_writer.OpenArray("s", &interface_writer);
+ interface_writer.AppendString(interface_name);
+ signal_writer.CloseContainer(&interface_writer);
+ exported_object_->SendSignal(&signal);
+}
+
+void ExportedObjectManager::HandleGetManagedObjects(
+ dbus::MethodCall* method_call,
+ dbus::ExportedObject::ResponseSender response_sender) const {
+ // Implements the GetManagedObjects method:
+ //
+ // org.freedesktop.DBus.ObjectManager.GetManagedObjects (
+ // out DICT<OBJPATH,
+ // DICT<STRING,
+ // DICT<STRING,VARIANT>>> )
+ bus_->AssertOnOriginThread();
+ scoped_ptr<dbus::Response> response(
+ dbus::Response::FromMethodCall(method_call));
+ dbus::MessageWriter response_writer(response.get());
+ dbus::MessageWriter all_object_paths(nullptr);
+ dbus::MessageWriter each_object_path(nullptr);
+ dbus::MessageWriter all_interfaces(nullptr);
+ dbus::MessageWriter each_interface(nullptr);
+
+ response_writer.OpenArray("{oa{sa{sv}}}", &all_object_paths);
+ for (const auto path_pair : registered_objects_) {
+ const dbus::ObjectPath& path = path_pair.first;
+ const InterfaceProperties& interface2properties = path_pair.second;
+ all_object_paths.OpenDictEntry(&each_object_path);
+ each_object_path.AppendObjectPath(path);
+ each_object_path.OpenArray("{sa{sv}}", &all_interfaces);
+ for (const auto interface : interface2properties) {
+ const std::string& interface_name = interface.first;
+ const PropertyWriter& property_writer = interface.second;
+ all_interfaces.OpenDictEntry(&each_interface);
+ each_interface.AppendString(interface_name);
+ property_writer.Run(&each_interface);
+ all_interfaces.CloseContainer(&each_interface);
+ }
+ each_object_path.CloseContainer(&all_interfaces);
+ all_object_paths.CloseContainer(&each_object_path);
+ }
+ response_writer.CloseContainer(&all_object_paths);
+ response_sender.Run(response.Pass());
+}
+
+} // namespace dbus_utils
+
+} // namespace buffet
diff --git a/buffet/exported_object_manager.h b/buffet/exported_object_manager.h
new file mode 100644
index 0000000..14a0b1a
--- /dev/null
+++ b/buffet/exported_object_manager.h
@@ -0,0 +1,118 @@
+// 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.
+
+#ifndef EXPORTED_OBJECT_MANAGER_H_
+#define EXPORTED_OBJECT_MANAGER_H_
+
+#include <map>
+#include <string>
+
+#include <base/memory/weak_ptr.h>
+#include <dbus/bus.h>
+#include <dbus/exported_object.h>
+#include <dbus/object_path.h>
+
+#include "buffet/exported_property_set.h"
+
+namespace buffet {
+
+namespace dbus_utils {
+
+// ExportedObjectManager is a delegate that implements the
+// org.freedesktop.DBus.ObjectManager interface on behalf of another
+// object. It handles sending signals when new interfaces are added.
+//
+// This class is very similar to the ExportedPropertySet class, except that
+// it allows objects to expose an object manager interface rather than the
+// properties interface.
+//
+// Example usage:
+//
+// class ExampleObjectManager {
+// public:
+// ExampleObjectManager(dbus::Bus* bus)
+// : object_manager_(bus, "/my/objects/path") { }
+//
+// void Init(const OnInitFinish& cb) { object_manager_.Init(cb); }
+// void ClaimInterface(const dbus::ObjectPath& path,
+// const std::string& interface_name,
+// const PropertyWriter& writer) {
+// object_manager_->ClaimInterface(...);
+// }
+// void ReleaseInterface(const dbus::ObjectPath& path,
+// const std::string& interface_name) {
+// object_manager_->ReleaseInterface(...);
+// }
+//
+// private:
+// ExportedObjectManager object_manager_;
+// };
+//
+// class MyObjectClaimingAnInterface {
+// public:
+// MyObjectClaimingAnInterface(ExampleObjectManager* object_manager)
+// : object_manager_(object_manager) {}
+//
+// void OnInitFinish(bool success) {
+// if (!success) { /* handle that */ }
+// object_manager_->ClaimInterface(
+// my_path_, my_interface_, my_properties_.GetWriter());
+// }
+//
+// private:
+// struct Properties : public ExportedPropertySet {
+// public:
+// /* Lots of interesting properties. */
+// };
+//
+// Properties my_properties_;
+// ExampleObjectManager* object_manager_;
+// };
+class ExportedObjectManager {
+ public:
+ // Writes a dictionary of property name to property value variants to writer.
+ typedef base::Callback<void(dbus::MessageWriter* writer)> PropertyWriter;
+ typedef base::Callback<void(bool success)> OnInitFinish;
+ typedef std::map<std::string, PropertyWriter> InterfaceProperties;
+
+ ExportedObjectManager(dbus::Bus* bus, const dbus::ObjectPath& path);
+
+ // Registers methods implementing the ObjectManager interface on the object
+ // exported on the path given in the constructor. Must be called on the
+ // origin thread.
+ void Init(const OnInitFinish& cb);
+
+ // Trigger a signal that |path| has added an interface |interface_name|
+ // with properties as given by |writer|.
+ void ClaimInterface(const dbus::ObjectPath& path,
+ const std::string& interface_name,
+ const PropertyWriter& writer);
+
+ // Trigger a signal that |path| has removed an interface |interface_name|.
+ void ReleaseInterface(const dbus::ObjectPath& path,
+ const std::string& interface_name);
+
+ private:
+ void HandleGetManagedObjects(
+ dbus::MethodCall* method_call,
+ dbus::ExportedObject::ResponseSender response_sender) const;
+
+ // Both |bus_| and |exported_object_| outlive *this.
+ dbus::Bus* const bus_;
+ dbus::ExportedObject* const exported_object_;
+ // Tracks all objects currently known to the ExportedObjectManager.
+ std::map<dbus::ObjectPath, InterfaceProperties> registered_objects_;
+
+ // We're going to register DBus callbacks that will outlive ourselves.
+ // These callbacks get scheduled on the origin thread.
+ base::WeakPtrFactory<ExportedObjectManager> weak_ptr_factory_;
+ friend class ExportedObjectManagerTest;
+ DISALLOW_COPY_AND_ASSIGN(ExportedObjectManager);
+};
+
+} // namespace dbus_utils
+
+} // namespace buffet
+
+#endif // EXPORTED_OBJECT_MANAGER_H_
diff --git a/buffet/exported_object_manager_unittest.cc b/buffet/exported_object_manager_unittest.cc
new file mode 100644
index 0000000..696b76f
--- /dev/null
+++ b/buffet/exported_object_manager_unittest.cc
@@ -0,0 +1,205 @@
+// 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 "buffet/exported_object_manager.h"
+
+#include <base/bind.h>
+#include <dbus/mock_bus.h>
+#include <dbus/mock_exported_object.h>
+#include <dbus/object_manager.h>
+#include <dbus/object_path.h>
+#include <gtest/gtest.h>
+
+using ::testing::AnyNumber;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::_;
+
+namespace buffet {
+
+namespace dbus_utils {
+
+namespace {
+
+const dbus::ObjectPath kTestPath(std::string("/test/om_path"));
+const dbus::ObjectPath kClaimedTestPath(std::string("/test/claimed_path"));
+const std::string kClaimedInterface("claimed.interface");
+const std::string kTestPropertyName("PropertyName");
+const std::string kTestPropertyValue("PropertyValue");
+
+void WriteTestPropertyDict(dbus::MessageWriter* writer) {
+ dbus::MessageWriter all_properties(nullptr);
+ dbus::MessageWriter each_property(nullptr);
+ writer->OpenArray("{sv}", &all_properties);
+ all_properties.OpenDictEntry(&each_property);
+ each_property.AppendString(kTestPropertyName);
+ each_property.AppendVariantOfString(kTestPropertyValue);
+ all_properties.CloseContainer(&each_property);
+ writer->CloseContainer(&all_properties);
+}
+
+void ReadTestPropertyDict(dbus::MessageReader* reader) {
+ dbus::MessageReader all_properties(nullptr);
+ dbus::MessageReader each_property(nullptr);
+ ASSERT_TRUE(reader->PopArray(&all_properties));
+ ASSERT_TRUE(all_properties.PopDictEntry(&each_property));
+ std::string property_name;
+ std::string property_value;
+ ASSERT_TRUE(each_property.PopString(&property_name));
+ ASSERT_TRUE(each_property.PopVariantOfString(&property_value));
+ EXPECT_FALSE(each_property.HasMoreData());
+ EXPECT_FALSE(all_properties.HasMoreData());
+ EXPECT_EQ(property_name, kTestPropertyName);
+ EXPECT_EQ(property_value, kTestPropertyValue);
+}
+
+void VerifyInterfaceClaimSignal(dbus::Signal* signal) {
+ EXPECT_EQ(signal->GetInterface(),
+ std::string(dbus::kObjectManagerInterface));
+ EXPECT_EQ(signal->GetMember(),
+ std::string(dbus::kObjectManagerInterfacesAdded));
+ // org.freedesktop.DBus.ObjectManager.InterfacesAdded (
+ // OBJPATH object_path,
+ // DICT<STRING,DICT<STRING,VARIANT>> interfaces_and_properties);
+ dbus::MessageReader reader(signal);
+ dbus::MessageReader all_interfaces(nullptr);
+ dbus::MessageReader each_interface(nullptr);
+ dbus::ObjectPath path;
+ ASSERT_TRUE(reader.PopObjectPath(&path));
+ ASSERT_TRUE(reader.PopArray(&all_interfaces));
+ ASSERT_TRUE(all_interfaces.PopDictEntry(&each_interface));
+ std::string interface_name;
+ ASSERT_TRUE(each_interface.PopString(&interface_name));
+ ReadTestPropertyDict(&each_interface);
+ EXPECT_FALSE(each_interface.HasMoreData());
+ EXPECT_FALSE(all_interfaces.HasMoreData());
+ EXPECT_FALSE(reader.HasMoreData());
+ EXPECT_EQ(interface_name, kClaimedInterface);
+ EXPECT_EQ(path, kClaimedTestPath);
+}
+
+void VerifyInterfaceDropSignal(dbus::Signal* signal) {
+ EXPECT_EQ(signal->GetInterface(),
+ std::string(dbus::kObjectManagerInterface));
+ EXPECT_EQ(signal->GetMember(),
+ std::string(dbus::kObjectManagerInterfacesRemoved));
+ // org.freedesktop.DBus.ObjectManager.InterfacesRemoved (
+ // OBJPATH object_path, ARRAY<STRING> interfaces);
+ dbus::MessageReader reader(signal);
+ dbus::MessageReader each_interface(nullptr);
+ dbus::ObjectPath path;
+ ASSERT_TRUE(reader.PopObjectPath(&path));
+ ASSERT_TRUE(reader.PopArray(&each_interface));
+ std::string interface_name;
+ ASSERT_TRUE(each_interface.PopString(&interface_name));
+ EXPECT_FALSE(each_interface.HasMoreData());
+ EXPECT_FALSE(reader.HasMoreData());
+ EXPECT_EQ(interface_name, kClaimedInterface);
+ EXPECT_EQ(path, kClaimedTestPath);
+}
+
+} // namespace
+
+class ExportedObjectManagerTest: public ::testing::Test {
+ public:
+ virtual void SetUp() {
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SYSTEM;
+ bus_ = new dbus::MockBus(options);
+ // By default, don't worry about threading assertions.
+ EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber());
+ EXPECT_CALL(*bus_, AssertOnDBusThread()).Times(AnyNumber());
+ // Use a mock exported object.
+ mock_exported_object_ = new dbus::MockExportedObject(
+ bus_.get(), kTestPath);
+ EXPECT_CALL(*bus_, GetExportedObject(kTestPath))
+ .Times(1).WillOnce(Return(mock_exported_object_.get()));
+ om_.reset(new ExportedObjectManager(bus_.get(), kTestPath));
+ property_writer_ = base::Bind(&WriteTestPropertyDict);
+ response_storer_ = base::Bind(&ExportedObjectManagerTest::StoreResponse,
+ base::Unretained(this));
+ }
+
+ void StoreResponse(scoped_ptr<dbus::Response> method_response) {
+ last_response_.reset(method_response.release());
+ }
+
+ void CallHandleGetManagedObjects(
+ dbus::MethodCall* method_call,
+ dbus::ExportedObject::ResponseSender sender) {
+ om_->HandleGetManagedObjects(method_call, response_storer_);
+ }
+
+ scoped_refptr<dbus::MockBus> bus_;
+ scoped_refptr<dbus::MockExportedObject> mock_exported_object_;
+ scoped_ptr<ExportedObjectManager> om_;
+ ExportedObjectManager::PropertyWriter property_writer_;
+ dbus::ExportedObject::ResponseSender response_storer_;
+ scoped_ptr<dbus::Response> last_response_;
+};
+
+TEST_F(ExportedObjectManagerTest, ClaimInterfaceSendsSignals) {
+ EXPECT_CALL(*mock_exported_object_, SendSignal(_))
+ .Times(1).WillOnce(Invoke(&VerifyInterfaceClaimSignal));
+ om_->ClaimInterface(kClaimedTestPath, kClaimedInterface, property_writer_);
+}
+
+TEST_F(ExportedObjectManagerTest, ReleaseInterfaceSendsSignals) {
+ InSequence dummy;
+ EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(1);
+ EXPECT_CALL(*mock_exported_object_, SendSignal(_))
+ .Times(1).WillOnce(Invoke(&VerifyInterfaceDropSignal));
+ om_->ClaimInterface(kClaimedTestPath, kClaimedInterface, property_writer_);
+ om_->ReleaseInterface(kClaimedTestPath, kClaimedInterface);
+}
+
+TEST_F(ExportedObjectManagerTest, GetManagedObjectsResponseEmptyCorrectness) {
+ dbus::MethodCall method_call(dbus::kObjectManagerInterface,
+ dbus::kObjectManagerGetManagedObjects);
+ method_call.SetSerial(123);
+ CallHandleGetManagedObjects(&method_call, response_storer_);
+ dbus::MessageReader reader(last_response_.get());
+ dbus::MessageReader all_paths(nullptr);
+ ASSERT_TRUE(reader.PopArray(&all_paths));
+ EXPECT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedObjectManagerTest, GetManagedObjectsResponseCorrectness) {
+ // org.freedesktop.DBus.ObjectManager.GetManagedObjects (
+ // out DICT<OBJPATH,
+ // DICT<STRING,
+ // DICT<STRING,VARIANT>>> )
+ dbus::MethodCall method_call(dbus::kObjectManagerInterface,
+ dbus::kObjectManagerGetManagedObjects);
+ method_call.SetSerial(123);
+ EXPECT_CALL(*mock_exported_object_, SendSignal(_)).Times(1);
+ om_->ClaimInterface(kClaimedTestPath, kClaimedInterface, property_writer_);
+ CallHandleGetManagedObjects(&method_call, response_storer_);
+ dbus::MessageReader reader(last_response_.get());
+ dbus::MessageReader all_paths(nullptr);
+ dbus::MessageReader each_path(nullptr);
+ dbus::MessageReader all_interfaces(nullptr);
+ dbus::MessageReader each_interface(nullptr);
+ ASSERT_TRUE(reader.PopArray(&all_paths));
+ ASSERT_TRUE(all_paths.PopDictEntry(&each_path));
+ dbus::ObjectPath path;
+ ASSERT_TRUE(each_path.PopObjectPath(&path));
+ ASSERT_TRUE(each_path.PopArray(&all_interfaces));
+ ASSERT_TRUE(all_interfaces.PopDictEntry(&each_interface));
+ std::string interface_name;
+ ASSERT_TRUE(each_interface.PopString(&interface_name));
+ ReadTestPropertyDict(&each_interface);
+ EXPECT_FALSE(each_interface.HasMoreData());
+ EXPECT_FALSE(all_interfaces.HasMoreData());
+ EXPECT_FALSE(each_path.HasMoreData());
+ EXPECT_FALSE(all_paths.HasMoreData());
+ EXPECT_FALSE(reader.HasMoreData());
+ EXPECT_EQ(path, kClaimedTestPath);
+ EXPECT_EQ(interface_name, kClaimedInterface);
+}
+
+} // namespace dbus_utils
+
+} // namespace buffet
diff --git a/buffet/exported_property_set.cc b/buffet/exported_property_set.cc
index f6f7037..ba6b2c7 100644
--- a/buffet/exported_property_set.cc
+++ b/buffet/exported_property_set.cc
@@ -84,28 +84,33 @@
GetBadArgsError(method_call, "Too many arguments to GetAll."));
return;
}
- auto property_map_itr = properties_.find(interface_name);
- if (property_map_itr == properties_.end()) {
- response_sender.Run(
- GetBadArgsError(method_call, "No such interface on object."));
- return;
- }
scoped_ptr<dbus::Response> response(
dbus::Response::FromMethodCall(method_call));
dbus::MessageWriter resp_writer(response.get());
- dbus::MessageWriter dict_writer(nullptr);
- resp_writer.OpenArray("{sv}", &dict_writer);
- for (const auto& kv : property_map_itr->second) {
- dbus::MessageWriter entry_writer(nullptr);
- dict_writer.OpenDictEntry(&entry_writer);
- entry_writer.AppendString(kv.first);
- kv.second->AppendValueToWriter(&entry_writer);
- dict_writer.CloseContainer(&entry_writer);
- }
- resp_writer.CloseContainer(&dict_writer);
+ WritePropertiesDictToMessage(interface_name, &resp_writer);
response_sender.Run(response.Pass());
}
+void ExportedPropertySet::WritePropertiesDictToMessage(
+ const std::string& interface_name,
+ dbus::MessageWriter* writer) {
+ dbus::MessageWriter dict_writer(nullptr);
+ writer->OpenArray("{sv}", &dict_writer);
+ auto property_map_itr = properties_.find(interface_name);
+ if (property_map_itr != properties_.end()) {
+ for (const auto& kv : property_map_itr->second) {
+ dbus::MessageWriter entry_writer(nullptr);
+ dict_writer.OpenDictEntry(&entry_writer);
+ entry_writer.AppendString(kv.first);
+ kv.second->AppendValueToWriter(&entry_writer);
+ dict_writer.CloseContainer(&entry_writer);
+ }
+ } else {
+ LOG(WARNING) << "No properties found for interface interface_name";
+ }
+ writer->CloseContainer(&dict_writer);
+}
+
void ExportedPropertySet::HandleGet(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
diff --git a/buffet/exported_property_set.h b/buffet/exported_property_set.h
index c464a8b..8e535b6 100644
--- a/buffet/exported_property_set.h
+++ b/buffet/exported_property_set.h
@@ -11,7 +11,6 @@
#include <base/memory/weak_ptr.h>
#include <dbus/exported_object.h>
#include <dbus/message.h>
-#include <gtest/gtest_prod.h>
namespace buffet {
@@ -105,6 +104,8 @@
// are exported to the DBus object. |cb| will be called on the origin
// thread.
void Init(const OnInitFinish& cb);
+ base::Callback<void(dbus::MessageWriter* writer)> GetPropertyWriter(
+ const std::string& interface);
protected:
void RegisterProperty(const std::string& interface_name,
@@ -112,6 +113,11 @@
ExportedPropertyBase* exported_property);
private:
+ // Used to write the dictionary of string->variant to a message.
+ // This dictionary represents the property name/value pairs for the
+ // given interface.
+ void WritePropertiesDictToMessage(const std::string& interface_name,
+ dbus::MessageWriter* writer);
void HandleGetAll(dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender);
void HandleGet(dbus::MethodCall* method_call,
diff --git a/buffet/exported_property_set_unittest.cc b/buffet/exported_property_set_unittest.cc
index 959a46a..7687371 100644
--- a/buffet/exported_property_set_unittest.cc
+++ b/buffet/exported_property_set_unittest.cc
@@ -201,11 +201,22 @@
}
TEST_F(ExportedPropertySetTest, GetAllInvalidInterface) {
- dbus::MethodCall method_call(dbus::kPropertiesInterface,
- dbus::kPropertiesGetAll);
+ dbus::MethodCall method_call(
+ dbus::kPropertiesInterface, dbus::kPropertiesGetAll);
+ method_call.SetSerial(123);
dbus::MessageWriter writer(&method_call);
writer.AppendString("org.chromium.BadInterface");
- AssertGetAllReturnsError(&method_call);
+ auto response_sender = base::Bind(&ExportedPropertySetTest::StoreResponse,
+ base::Unretained(this));
+ p_->CallHandleGetAll(&method_call, response_sender);
+ dbus::MessageReader response_reader(last_response_.get());
+ dbus::MessageReader dict_reader(nullptr);
+ ASSERT_TRUE(response_reader.PopArray(&dict_reader));
+ // The response should just be a an empty array, since there are no properties
+ // on this interface. The spec doesn't say much about error conditions here,
+ // so I'm going to assume this is a valid implementation.
+ ASSERT_FALSE(dict_reader.HasMoreData());
+ ASSERT_FALSE(response_reader.HasMoreData());
}
TEST_F(ExportedPropertySetTest, GetAllExtraArgs) {