diff options
author | Zeno Albisser <zeno.albisser@theqtcompany.com> | 2014-12-05 15:04:29 +0100 |
---|---|---|
committer | Andras Becsi <andras.becsi@theqtcompany.com> | 2014-12-09 10:49:28 +0100 |
commit | af6588f8d723931a298c995fa97259bb7f7deb55 (patch) | |
tree | 060ca707847ba1735f01af2372e0d5e494dc0366 /chromium/dbus | |
parent | 2fff84d821cc7b1c785f6404e0f8091333283e74 (diff) | |
download | qtwebengine-chromium-af6588f8d723931a298c995fa97259bb7f7deb55.tar.gz |
BASELINE: Update chromium to 40.0.2214.28 and ninja to 1.5.3.
Change-Id: I759465284fd64d59ad120219cbe257f7402c4181
Reviewed-by: Andras Becsi <andras.becsi@theqtcompany.com>
Diffstat (limited to 'chromium/dbus')
30 files changed, 804 insertions, 166 deletions
diff --git a/chromium/dbus/BUILD.gn b/chromium/dbus/BUILD.gn index 347bae0fbdf..830bd19406c 100644 --- a/chromium/dbus/BUILD.gn +++ b/chromium/dbus/BUILD.gn @@ -25,11 +25,14 @@ component("dbus") { "object_proxy.h", "property.cc", "property.h", + "scoped_dbus_error.cc", "scoped_dbus_error.h", "string_util.cc", "string_util.h", "values_util.cc", "values_util.h", + "util.cc", + "util.h", ] defines = [ @@ -41,18 +44,19 @@ component("dbus") { "//third_party/protobuf:protobuf_lite", ] - direct_dependent_configs = [ + public_configs = [ "//build/config/linux:dbus", ] } -proto_library("dbus_test_proto") { +proto_library("test_proto") { sources = [ "test_proto.proto" ] } # This target contains mocks that can be used to write unit tests without # issuing actual D-Bus calls. -source_set("dbus_test_support") { +source_set("test_support") { + testonly = true sources = [ "mock_bus.cc", "mock_bus.h", @@ -90,13 +94,14 @@ test("dbus_unittests") { "string_util_unittest.cc", "test_service.cc", "test_service.h", + "util_unittest.cc", "values_util_unittest.cc", ] deps = [ ":dbus", - ":dbus_test_proto", - ":dbus_test_support", + ":test_proto", + ":test_support", "//base/test:test_support", "//testing/gmock", "//testing/gtest", @@ -109,6 +114,7 @@ test("dbus_unittests") { } executable("dbus_test_server") { + testonly = true sources = [ "test_server.cc", "test_service.cc", diff --git a/chromium/dbus/bus.cc b/chromium/dbus/bus.cc index 28257f8c508..e9e77a5dcd4 100644 --- a/chromium/dbus/bus.cc +++ b/chromium/dbus/bus.cc @@ -84,13 +84,13 @@ class Watch : public base::MessagePumpLibevent::Watcher { private: // Implement MessagePumpLibevent::Watcher. - virtual void OnFileCanReadWithoutBlocking(int file_descriptor) OVERRIDE { + virtual void OnFileCanReadWithoutBlocking(int file_descriptor) override { const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_READABLE); CHECK(success) << "Unable to allocate memory"; } // Implement MessagePumpLibevent::Watcher. - virtual void OnFileCanWriteWithoutBlocking(int file_descriptor) OVERRIDE { + virtual void OnFileCanWriteWithoutBlocking(int file_descriptor) override { const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_WRITABLE); CHECK(success) << "Unable to allocate memory"; } @@ -267,7 +267,7 @@ bool Bus::RemoveObjectProxyWithOptions(const std::string& service_name, if (iter != object_proxy_table_.end()) { scoped_refptr<ObjectProxy> object_proxy = iter->second; object_proxy_table_.erase(iter); - // Object is present. Remove it now and Detach in the DBus thread. + // Object is present. Remove it now and Detach on the DBus thread. GetDBusTaskRunner()->PostTask( FROM_HERE, base::Bind(&Bus::RemoveObjectProxyInternal, @@ -350,17 +350,54 @@ ObjectManager* Bus::GetObjectManager(const std::string& service_name, return object_manager.get(); } -void Bus::RemoveObjectManager(const std::string& service_name, - const ObjectPath& object_path) { +bool Bus::RemoveObjectManager(const std::string& service_name, + const ObjectPath& object_path, + const base::Closure& callback) { AssertOnOriginThread(); + DCHECK(!callback.is_null()); const ObjectManagerTable::key_type key(service_name + object_path.value()); ObjectManagerTable::iterator iter = object_manager_table_.find(key); if (iter == object_manager_table_.end()) - return; + return false; + // ObjectManager is present. Remove it now and CleanUp on the DBus thread. scoped_refptr<ObjectManager> object_manager = iter->second; object_manager_table_.erase(iter); + + GetDBusTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&Bus::RemoveObjectManagerInternal, + this, object_manager, callback)); + + return true; +} + +void Bus::RemoveObjectManagerInternal( + scoped_refptr<dbus::ObjectManager> object_manager, + const base::Closure& callback) { + AssertOnDBusThread(); + DCHECK(object_manager.get()); + + object_manager->CleanUp(); + + // The ObjectManager has to be deleted on the origin thread since it was + // created there. + GetOriginTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&Bus::RemoveObjectManagerInternalHelper, + this, object_manager, callback)); +} + +void Bus::RemoveObjectManagerInternalHelper( + scoped_refptr<dbus::ObjectManager> object_manager, + const base::Closure& callback) { + AssertOnOriginThread(); + DCHECK(object_manager.get()); + + // Release the object manager and run the callback. + object_manager = NULL; + callback.Run(); } void Bus::GetManagedObjects() { @@ -460,6 +497,12 @@ void Bus::ShutdownAndBlock() { iter->second->Detach(); } + // Clean up the object managers. + for (ObjectManagerTable::iterator iter = object_manager_table_.begin(); + iter != object_manager_table_.end(); ++iter) { + iter->second->CleanUp(); + } + // Release object proxies and exported objects here. We should do this // here rather than in the destructor to avoid memory leaks due to // cyclic references. diff --git a/chromium/dbus/bus.h b/chromium/dbus/bus.h index 647b1b72607..544d31fc169 100644 --- a/chromium/dbus/bus.h +++ b/chromium/dbus/bus.h @@ -354,9 +354,15 @@ class CHROME_DBUS_EXPORT Bus : public base::RefCountedThreadSafe<Bus> { // will return a new object, method calls on any remaining copies of the // previous object are not permitted. // + // This method will asynchronously clean up any match rules that have been + // added for the object manager and invoke |callback| when the operation is + // complete. If this method returns false, then |callback| is never called. + // The |callback| argument must not be null. + // // Must be called in the origin thread. - virtual void RemoveObjectManager(const std::string& service_name, - const ObjectPath& object_path); + virtual bool RemoveObjectManager(const std::string& service_name, + const ObjectPath& object_path, + const base::Closure& callback); // Instructs all registered object managers to retrieve their set of managed // objects from their respective remote objects. There is no need to call this @@ -601,6 +607,14 @@ class CHROME_DBUS_EXPORT Bus : public base::RefCountedThreadSafe<Bus> { void RemoveObjectProxyInternal(scoped_refptr<dbus::ObjectProxy> object_proxy, const base::Closure& callback); + // Helper functions used for RemoveObjectManager(). + void RemoveObjectManagerInternal( + scoped_refptr<dbus::ObjectManager> object_manager, + const base::Closure& callback); + void RemoveObjectManagerInternalHelper( + scoped_refptr<dbus::ObjectManager> object_manager, + const base::Closure& callback); + // Helper function used for UnregisterExportedObject(). void UnregisterExportedObjectInternal( scoped_refptr<dbus::ExportedObject> exported_object); diff --git a/chromium/dbus/dbus.gyp b/chromium/dbus/dbus.gyp index 4d90497c936..daac2e26a3e 100644 --- a/chromium/dbus/dbus.gyp +++ b/chromium/dbus/dbus.gyp @@ -41,9 +41,12 @@ 'object_proxy.h', 'property.cc', 'property.h', + 'scoped_dbus_error.cc', 'scoped_dbus_error.h', 'string_util.cc', 'string_util.h', + 'util.cc', + 'util.h', 'values_util.cc', 'values_util.h', ], @@ -109,6 +112,7 @@ 'string_util_unittest.cc', 'test_service.cc', 'test_service.h', + 'util_unittest.cc', 'values_util_unittest.cc', ], 'include_dirs': [ diff --git a/chromium/dbus/dbus_export.h b/chromium/dbus/dbus_export.h index 0eda41a26fb..7c96d1738f6 100644 --- a/chromium/dbus/dbus_export.h +++ b/chromium/dbus/dbus_export.h @@ -10,16 +10,11 @@ // NOTE: We haven't used DBUS_EXPORT because it would conflict with the version // from /usr/include/dbus-1.0/dbus/dbus-macros.h. -#if defined(COMPONENT_BUILD) #if defined(WIN32) +#error dbus support is not currently expected to work on windows +#endif // defined(WIN32) -#if defined(DBUS_IMPLEMENTATION) -#define CHROME_DBUS_EXPORT __declspec(dllexport) -#else -#define CHROME_DBUS_EXPORT __declspec(dllimport) -#endif // defined(DBUS_IMPLEMENTATION) - -#else // !defined(WIN32) +#if defined(COMPONENT_BUILD) #if defined(DBUS_IMPLEMENTATION) #define CHROME_DBUS_EXPORT __attribute__((visibility("default"))) @@ -27,8 +22,6 @@ #define CHROME_DBUS_EXPORT #endif -#endif // defined(WIN32) - #else // !defined(COMPONENT_BUILD) #define CHROME_DBUS_EXPORT diff --git a/chromium/dbus/dbus_statistics_unittest.cc b/chromium/dbus/dbus_statistics_unittest.cc index 9a2e2a17435..0c0dd034972 100644 --- a/chromium/dbus/dbus_statistics_unittest.cc +++ b/chromium/dbus/dbus_statistics_unittest.cc @@ -15,11 +15,11 @@ class DBusStatisticsTest : public testing::Test { DBusStatisticsTest() { } - virtual void SetUp() OVERRIDE { + virtual void SetUp() override { statistics::Initialize(); } - virtual void TearDown() OVERRIDE { + virtual void TearDown() override { statistics::Shutdown(); } diff --git a/chromium/dbus/end_to_end_async_unittest.cc b/chromium/dbus/end_to_end_async_unittest.cc index fb8030bf4cb..15f410eadc0 100644 --- a/chromium/dbus/end_to_end_async_unittest.cc +++ b/chromium/dbus/end_to_end_async_unittest.cc @@ -9,6 +9,7 @@ #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/stl_util.h" #include "base/test/test_timeouts.h" #include "base/threading/thread.h" @@ -77,7 +78,8 @@ class EndToEndAsyncTest : public testing::Test { base::Bind(&EndToEndAsyncTest::OnConnected, base::Unretained(this))); // Wait until the object proxy is connected to the signal. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop()); + run_loop_->Run(); // Connect to the "Test2" signal of "org.chromium.TestInterface" from // the remote object. There was a bug where we were emitting error @@ -92,7 +94,8 @@ class EndToEndAsyncTest : public testing::Test { base::Bind(&EndToEndAsyncTest::OnConnected, base::Unretained(this))); // Wait until the object proxy is connected to the signal. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop()); + run_loop_->Run(); // Create a second object proxy for the root object. root_object_proxy_ = bus_->GetObjectProxy("org.chromium.TestService", @@ -109,7 +112,8 @@ class EndToEndAsyncTest : public testing::Test { base::Bind(&EndToEndAsyncTest::OnConnected, base::Unretained(this))); // Wait until the root object proxy is connected to the signal. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop()); + run_loop_->Run(); } virtual void TearDown() { @@ -172,7 +176,8 @@ class EndToEndAsyncTest : public testing::Test { // Wait for the give number of responses. void WaitForResponses(size_t num_responses) { while (response_strings_.size() < num_responses) { - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); } } @@ -188,13 +193,14 @@ class EndToEndAsyncTest : public testing::Test { } else { response_strings_.push_back(std::string()); } - message_loop_.Quit(); + run_loop_->Quit(); }; // Wait for the given number of errors. void WaitForErrors(size_t num_errors) { while (error_names_.size() < num_errors) { - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); } } @@ -208,7 +214,7 @@ class EndToEndAsyncTest : public testing::Test { } else { error_names_.push_back(std::string()); } - message_loop_.Quit(); + run_loop_->Quit(); } // Called when the "Test" signal is received, in the main thread. @@ -216,7 +222,7 @@ class EndToEndAsyncTest : public testing::Test { void OnTestSignal(Signal* signal) { MessageReader reader(signal); ASSERT_TRUE(reader.PopString(&test_signal_string_)); - message_loop_.Quit(); + run_loop_->Quit(); } // Called when the "Test" signal is received, in the main thread, by @@ -225,13 +231,13 @@ class EndToEndAsyncTest : public testing::Test { void OnRootTestSignal(Signal* signal) { MessageReader reader(signal); ASSERT_TRUE(reader.PopString(&root_test_signal_string_)); - message_loop_.Quit(); + run_loop_->Quit(); } // Called when the "Test2" signal is received, in the main thread. void OnTest2Signal(Signal* signal) { MessageReader reader(signal); - message_loop_.Quit(); + run_loop_->Quit(); } // Called when connected to the signal. @@ -239,22 +245,24 @@ class EndToEndAsyncTest : public testing::Test { const std::string& signal_name, bool success) { ASSERT_TRUE(success); - message_loop_.Quit(); + run_loop_->Quit(); } // Called when the connection with dbus-daemon is disconnected. void OnDisconnected() { - message_loop_.Quit(); + run_loop_->Quit(); ++on_disconnected_call_count_; } // Wait for the hey signal to be received. void WaitForTestSignal() { // OnTestSignal() will quit the message loop. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); } base::MessageLoop message_loop_; + scoped_ptr<base::RunLoop> run_loop_; std::vector<std::string> response_strings_; std::vector<std::string> error_names_; scoped_ptr<base::Thread> dbus_thread_; @@ -537,10 +545,11 @@ TEST_F(EndToEndAsyncTest, EmptyResponseCallback) { timeout_ms, ObjectProxy::EmptyResponseCallback()); // Post a delayed task to quit the message loop. + run_loop_.reset(new base::RunLoop); message_loop_.PostDelayedTask(FROM_HERE, - base::MessageLoop::QuitClosure(), + run_loop_->QuitClosure(), TestTimeouts::tiny_timeout()); - message_loop_.Run(); + run_loop_->Run(); // We cannot tell if the empty callback is called, but at least we can // check if the test does not crash. } @@ -584,7 +593,8 @@ TEST_F(EndToEndAsyncTest, DisconnectedSignal) { base::Bind(&Bus::ClosePrivateConnection, base::Unretained(bus_.get()))); // OnDisconnected callback quits message loop. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); EXPECT_EQ(1, on_disconnected_call_count_); } @@ -608,7 +618,8 @@ class SignalMultipleHandlerTest : public EndToEndAsyncTest { base::Bind(&SignalMultipleHandlerTest::OnAdditionalConnected, base::Unretained(this))); // Wait until the object proxy is connected to the signal. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); } protected: @@ -617,7 +628,7 @@ class SignalMultipleHandlerTest : public EndToEndAsyncTest { void OnAdditionalTestSignal(Signal* signal) { MessageReader reader(signal); ASSERT_TRUE(reader.PopString(&additional_test_signal_string_)); - message_loop_.Quit(); + run_loop_->Quit(); } // Called when connected to the signal. @@ -625,7 +636,7 @@ class SignalMultipleHandlerTest : public EndToEndAsyncTest { const std::string& signal_name, bool success) { ASSERT_TRUE(success); - message_loop_.Quit(); + run_loop_->Quit(); } // Text message from "Test" signal delivered to additional handler. diff --git a/chromium/dbus/exported_object.cc b/chromium/dbus/exported_object.cc index 1a4fbb50eb4..107d2e5dd17 100644 --- a/chromium/dbus/exported_object.cc +++ b/chromium/dbus/exported_object.cc @@ -15,6 +15,7 @@ #include "dbus/message.h" #include "dbus/object_path.h" #include "dbus/scoped_dbus_error.h" +#include "dbus/util.h" namespace dbus { @@ -23,15 +24,6 @@ namespace { // Used for success ratio histograms. 1 for success, 0 for failure. const int kSuccessRatioHistogramMaxValue = 2; -// Gets the absolute method name by concatenating the interface name and -// the method name. Used for building keys for method_table_ in -// ExportedObject. -std::string GetAbsoluteMethodName( - const std::string& interface_name, - const std::string& method_name) { - return interface_name + "." + method_name; -} - } // namespace ExportedObject::ExportedObject(Bus* bus, @@ -53,7 +45,7 @@ bool ExportedObject::ExportMethodAndBlock( // Check if the method is already exported. const std::string absolute_method_name = - GetAbsoluteMethodName(interface_name, method_name); + GetAbsoluteMemberName(interface_name, method_name); if (method_table_.find(absolute_method_name) != method_table_.end()) { LOG(ERROR) << absolute_method_name << " is already exported"; return false; @@ -203,7 +195,7 @@ DBusHandlerResult ExportedObject::HandleMessage( } // Check if we know about the method. - const std::string absolute_method_name = GetAbsoluteMethodName( + const std::string absolute_method_name = GetAbsoluteMemberName( interface, member); MethodTable::const_iterator iter = method_table_.find(absolute_method_name); if (iter == method_table_.end()) { diff --git a/chromium/dbus/message.cc b/chromium/dbus/message.cc index c9219b7e52b..ddcf85f4f26 100644 --- a/chromium/dbus/message.cc +++ b/chromium/dbus/message.cc @@ -935,6 +935,16 @@ Message::DataType MessageReader::GetDataType() { return static_cast<Message::DataType>(dbus_type); } +std::string MessageReader::GetDataSignature() { + std::string signature; + char* raw_signature = dbus_message_iter_get_signature(&raw_message_iter_); + if (raw_signature) { + signature = raw_signature; + dbus_free(raw_signature); + } + return signature; +} + bool MessageReader::CheckDataType(int dbus_type) { const int actual_type = dbus_message_iter_get_arg_type(&raw_message_iter_); if (actual_type != dbus_type) { diff --git a/chromium/dbus/message.h b/chromium/dbus/message.h index db3456acd0c..780e6c5b466 100644 --- a/chromium/dbus/message.h +++ b/chromium/dbus/message.h @@ -463,6 +463,11 @@ class CHROME_DBUS_EXPORT MessageReader { // end of the message. Message::DataType GetDataType(); + // Get the DBus signature of the value at the current iterator position. + // An empty string will be returned if the iterator points to the end of + // the message. + std::string GetDataSignature(); + private: // Returns true if the data type at the current iterator position // matches the given D-Bus type, such as DBUS_TYPE_BYTE. diff --git a/chromium/dbus/message_unittest.cc b/chromium/dbus/message_unittest.cc index 0c944d9d617..0eaea8395f5 100644 --- a/chromium/dbus/message_unittest.cc +++ b/chromium/dbus/message_unittest.cc @@ -24,6 +24,7 @@ TEST(MessageTest, AppendAndPopByte) { MessageReader reader(message.get()); ASSERT_TRUE(reader.HasMoreData()); // Should have data to read. ASSERT_EQ(Message::BYTE, reader.GetDataType()); + ASSERT_EQ("y", reader.GetDataSignature()); bool bool_value = false; // Should fail as the type is not bool here. @@ -70,17 +71,29 @@ TEST(MessageTest, AppendAndPopBasicDataTypes) { MessageReader reader(message.get()); ASSERT_TRUE(reader.HasMoreData()); + ASSERT_EQ("y", reader.GetDataSignature()); ASSERT_TRUE(reader.PopByte(&byte_value)); + ASSERT_EQ("b", reader.GetDataSignature()); ASSERT_TRUE(reader.PopBool(&bool_value)); + ASSERT_EQ("n", reader.GetDataSignature()); ASSERT_TRUE(reader.PopInt16(&int16_value)); + ASSERT_EQ("q", reader.GetDataSignature()); ASSERT_TRUE(reader.PopUint16(&uint16_value)); + ASSERT_EQ("i", reader.GetDataSignature()); ASSERT_TRUE(reader.PopInt32(&int32_value)); + ASSERT_EQ("u", reader.GetDataSignature()); ASSERT_TRUE(reader.PopUint32(&uint32_value)); + ASSERT_EQ("x", reader.GetDataSignature()); ASSERT_TRUE(reader.PopInt64(&int64_value)); + ASSERT_EQ("t", reader.GetDataSignature()); ASSERT_TRUE(reader.PopUint64(&uint64_value)); + ASSERT_EQ("d", reader.GetDataSignature()); ASSERT_TRUE(reader.PopDouble(&double_value)); + ASSERT_EQ("s", reader.GetDataSignature()); ASSERT_TRUE(reader.PopString(&string_value)); + ASSERT_EQ("o", reader.GetDataSignature()); ASSERT_TRUE(reader.PopObjectPath(&object_path_value)); + ASSERT_EQ("", reader.GetDataSignature()); ASSERT_FALSE(reader.HasMoreData()); // 0, 1, 2, 3, 4, 5, 6, 7, 8, "string", "/object/path" should be returned. @@ -120,6 +133,8 @@ TEST(MessageTest, AppendAndPopFileDescriptor) { MessageReader reader(message.get()); ASSERT_TRUE(reader.HasMoreData()); + ASSERT_EQ(Message::UNIX_FD, reader.GetDataType()); + ASSERT_EQ("h", reader.GetDataSignature()); ASSERT_TRUE(reader.PopFileDescriptor(&fd_value)); ASSERT_FALSE(reader.HasMoreData()); // Descriptor is not valid until explicitly checked. @@ -171,17 +186,29 @@ TEST(MessageTest, AppendAndPopVariantDataTypes) { MessageReader reader(message.get()); ASSERT_TRUE(reader.HasMoreData()); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfByte(&byte_value)); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfBool(&bool_value)); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfInt16(&int16_value)); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfUint16(&uint16_value)); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfInt32(&int32_value)); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfUint32(&uint32_value)); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfInt64(&int64_value)); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfUint64(&uint64_value)); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfDouble(&double_value)); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfString(&string_value)); + ASSERT_EQ("v", reader.GetDataSignature()); ASSERT_TRUE(reader.PopVariantOfObjectPath(&object_path_value)); + ASSERT_EQ("", reader.GetDataSignature()); ASSERT_FALSE(reader.HasMoreData()); // 0, 1, 2, 3, 4, 5, 6, 7, 8, "string", "/object/path" should be returned. @@ -210,6 +237,7 @@ TEST(MessageTest, ArrayOfBytes) { MessageReader reader(message.get()); const uint8* output_bytes = NULL; size_t length = 0; + ASSERT_EQ("ay", reader.GetDataSignature()); ASSERT_TRUE(reader.PopArrayOfBytes(&output_bytes, &length)); ASSERT_FALSE(reader.HasMoreData()); ASSERT_EQ(3U, length); @@ -227,6 +255,7 @@ TEST(MessageTest, ArrayOfBytes_Empty) { MessageReader reader(message.get()); const uint8* output_bytes = NULL; size_t length = 0; + ASSERT_EQ("ay", reader.GetDataSignature()); ASSERT_TRUE(reader.PopArrayOfBytes(&output_bytes, &length)); ASSERT_FALSE(reader.HasMoreData()); ASSERT_EQ(0U, length); @@ -245,6 +274,7 @@ TEST(MessageTest, ArrayOfStrings) { MessageReader reader(message.get()); std::vector<std::string> output_strings; + ASSERT_EQ("as", reader.GetDataSignature()); ASSERT_TRUE(reader.PopArrayOfStrings(&output_strings)); ASSERT_FALSE(reader.HasMoreData()); ASSERT_EQ(4U, output_strings.size()); @@ -265,6 +295,7 @@ TEST(MessageTest, ArrayOfObjectPaths) { MessageReader reader(message.get()); std::vector<ObjectPath> output_object_paths; + ASSERT_EQ("ao", reader.GetDataSignature()); ASSERT_TRUE(reader.PopArrayOfObjectPaths(&output_object_paths)); ASSERT_FALSE(reader.HasMoreData()); ASSERT_EQ(3U, output_object_paths.size()); @@ -283,6 +314,7 @@ TEST(MessageTest, ProtoBuf) { MessageReader reader(message.get()); TestProto receive_message; + ASSERT_EQ("ay", reader.GetDataSignature()); ASSERT_TRUE(reader.PopArrayOfBytesAsProto(&receive_message)); EXPECT_EQ(receive_message.text(), send_message.text()); EXPECT_EQ(receive_message.number(), send_message.number()); @@ -304,6 +336,7 @@ TEST(MessageTest, OpenArrayAndPopArray) { MessageReader reader(message.get()); ASSERT_EQ(Message::ARRAY, reader.GetDataType()); + ASSERT_EQ("as", reader.GetDataSignature()); MessageReader array_reader(NULL); ASSERT_TRUE(reader.PopArray(&array_reader)); ASSERT_FALSE(reader.HasMoreData()); // Should not have more data to read. @@ -397,11 +430,13 @@ TEST(MessageTest, CreateComplexMessageAndReadIt) { message->ToString()); MessageReader reader(message.get()); + ASSERT_EQ("av", reader.GetDataSignature()); MessageReader array_reader(NULL); ASSERT_TRUE(reader.PopArray(&array_reader)); // The first value in the array. bool bool_value = false; + ASSERT_EQ("v", array_reader.GetDataSignature()); ASSERT_TRUE(array_reader.PopVariantOfBool(&bool_value)); EXPECT_EQ(true, bool_value); @@ -411,6 +446,7 @@ TEST(MessageTest, CreateComplexMessageAndReadIt) { ASSERT_TRUE(array_reader.PopVariant(&variant_reader)); { MessageReader struct_reader(NULL); + ASSERT_EQ("(si)", variant_reader.GetDataSignature()); ASSERT_TRUE(variant_reader.PopStruct(&struct_reader)); std::string string_value; ASSERT_TRUE(struct_reader.PopString(&string_value)); @@ -429,6 +465,7 @@ TEST(MessageTest, CreateComplexMessageAndReadIt) { ASSERT_TRUE(array_reader.PopVariant(&variant_reader)); { MessageReader dict_array_reader(NULL); + ASSERT_EQ("a{sx}", variant_reader.GetDataSignature()); ASSERT_TRUE(variant_reader.PopArray(&dict_array_reader)); { MessageReader dict_entry_reader(NULL); diff --git a/chromium/dbus/mock_object_proxy.h b/chromium/dbus/mock_object_proxy.h index fcad8607248..7c61b47b428 100644 --- a/chromium/dbus/mock_object_proxy.h +++ b/chromium/dbus/mock_object_proxy.h @@ -25,10 +25,21 @@ class MockObjectProxy : public ObjectProxy { // uncopyable. This is a workaround which defines |MockCallMethodAndBlock| as // a mock method and makes |CallMethodAndBlock| call the mocked method. // Use |MockCallMethodAndBlock| for setting/testing expectations. + MOCK_METHOD3(MockCallMethodAndBlockWithErrorDetails, + Response*(MethodCall* method_call, + int timeout_ms, + ScopedDBusError* error)); + virtual scoped_ptr<Response> CallMethodAndBlockWithErrorDetails( + MethodCall* method_call, + int timeout_ms, + ScopedDBusError* error) override { + return scoped_ptr<Response>( + MockCallMethodAndBlockWithErrorDetails(method_call, timeout_ms, error)); + } MOCK_METHOD2(MockCallMethodAndBlock, Response*(MethodCall* method_call, int timeout_ms)); virtual scoped_ptr<Response> CallMethodAndBlock(MethodCall* method_call, - int timeout_ms) OVERRIDE { + int timeout_ms) override { return scoped_ptr<Response>(MockCallMethodAndBlock(method_call, timeout_ms)); } diff --git a/chromium/dbus/mock_unittest.cc b/chromium/dbus/mock_unittest.cc index bede2740d40..bfdc5a30c28 100644 --- a/chromium/dbus/mock_unittest.cc +++ b/chromium/dbus/mock_unittest.cc @@ -7,11 +7,13 @@ #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "dbus/message.h" #include "dbus/mock_bus.h" #include "dbus/mock_exported_object.h" #include "dbus/mock_object_proxy.h" #include "dbus/object_path.h" +#include "dbus/scoped_dbus_error.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -43,6 +45,10 @@ class MockTest : public testing::Test { // CreateMockProxyResponse() to return responses. EXPECT_CALL(*mock_proxy_.get(), MockCallMethodAndBlock(_, _)) .WillRepeatedly(Invoke(this, &MockTest::CreateMockProxyResponse)); + EXPECT_CALL(*mock_proxy_.get(), + MockCallMethodAndBlockWithErrorDetails(_, _, _)) + .WillRepeatedly( + Invoke(this, &MockTest::CreateMockProxyResponseWithErrorDetails)); // Set an expectation so mock_proxy's CallMethod() will use // HandleMockProxyResponseWithMessageLoop() to return responses. @@ -72,12 +78,13 @@ class MockTest : public testing::Test { MessageReader reader(response); ASSERT_TRUE(reader.PopString(&response_string_)); } - message_loop_.Quit(); + run_loop_->Quit(); }; protected: std::string response_string_; base::MessageLoop message_loop_; + scoped_ptr<base::RunLoop> run_loop_; scoped_refptr<MockBus> mock_bus_; scoped_refptr<MockObjectProxy> mock_proxy_; @@ -102,6 +109,12 @@ class MockTest : public testing::Test { return NULL; } + Response* CreateMockProxyResponseWithErrorDetails( + MethodCall* method_call, int timeout_ms, ScopedDBusError* error) { + dbus_set_error(error->get(), DBUS_ERROR_NOT_SUPPORTED, "Not implemented"); + return NULL; + } + // Creates a response and runs the given response callback in the // message loop with the response. Used to implement for |mock_proxy_|. void HandleMockProxyResponseWithMessageLoop( @@ -125,7 +138,7 @@ class MockTest : public testing::Test { } }; -// This test demonstrates how to mock a synchronos method call using the +// This test demonstrates how to mock a synchronous method call using the // mock classes. TEST_F(MockTest, CallMethodAndBlock) { const char kHello[] = "Hello"; @@ -153,7 +166,29 @@ TEST_F(MockTest, CallMethodAndBlock) { EXPECT_EQ(kHello, text_message); } -// This test demonstrates how to mock an asynchronos method call using the +TEST_F(MockTest, CallMethodAndBlockWithErrorDetails) { + // Get an object proxy from the mock bus. + ObjectProxy* proxy = mock_bus_->GetObjectProxy( + "org.chromium.TestService", + ObjectPath("/org/chromium/TestObject")); + + // Create a method call. + MethodCall method_call("org.chromium.TestInterface", "Echo"); + + ScopedDBusError error; + // Call the method. + scoped_ptr<Response> response( + proxy->CallMethodAndBlockWithErrorDetails( + &method_call, ObjectProxy::TIMEOUT_USE_DEFAULT, &error)); + + // Check the response. + ASSERT_FALSE(response.get()); + ASSERT_TRUE(error.is_set()); + EXPECT_STREQ(DBUS_ERROR_NOT_SUPPORTED, error.name()); + EXPECT_STREQ("Not implemented", error.message()); +} + +// This test demonstrates how to mock an asynchronous method call using the // mock classes. TEST_F(MockTest, CallMethod) { const char kHello[] = "hello"; @@ -169,12 +204,13 @@ TEST_F(MockTest, CallMethod) { writer.AppendString(kHello); // Call the method. + run_loop_.reset(new base::RunLoop); proxy->CallMethod(&method_call, ObjectProxy::TIMEOUT_USE_DEFAULT, base::Bind(&MockTest::OnResponse, base::Unretained(this))); // Run the message loop to let OnResponse be called. - message_loop_.Run(); + run_loop_->Run(); EXPECT_EQ(kHello, response_string_); } diff --git a/chromium/dbus/object_manager.cc b/chromium/dbus/object_manager.cc index d8eb569cf72..f754bb693fd 100644 --- a/chromium/dbus/object_manager.cc +++ b/chromium/dbus/object_manager.cc @@ -5,11 +5,18 @@ #include "dbus/object_manager.h" #include "base/bind.h" +#include "base/location.h" #include "base/logging.h" +#include "base/metrics/histogram.h" +#include "base/strings/stringprintf.h" +#include "base/task_runner_util.h" #include "dbus/bus.h" +#include "dbus/dbus_statistics.h" #include "dbus/message.h" #include "dbus/object_proxy.h" #include "dbus/property.h" +#include "dbus/scoped_dbus_error.h" +#include "dbus/util.h" namespace dbus { @@ -26,33 +33,27 @@ ObjectManager::ObjectManager(Bus* bus, : bus_(bus), service_name_(service_name), object_path_(object_path), + setup_success_(false), + cleanup_called_(false), weak_ptr_factory_(this) { DVLOG(1) << "Creating ObjectManager for " << service_name_ << " " << object_path_.value(); - DCHECK(bus_); + bus_->AssertOnOriginThread(); object_proxy_ = bus_->GetObjectProxy(service_name_, object_path_); object_proxy_->SetNameOwnerChangedCallback( base::Bind(&ObjectManager::NameOwnerChanged, weak_ptr_factory_.GetWeakPtr())); - object_proxy_->ConnectToSignal( - kObjectManagerInterface, - kObjectManagerInterfacesAdded, - base::Bind(&ObjectManager::InterfacesAddedReceived, - weak_ptr_factory_.GetWeakPtr()), - base::Bind(&ObjectManager::InterfacesAddedConnected, - weak_ptr_factory_.GetWeakPtr())); - - object_proxy_->ConnectToSignal( - kObjectManagerInterface, - kObjectManagerInterfacesRemoved, - base::Bind(&ObjectManager::InterfacesRemovedReceived, - weak_ptr_factory_.GetWeakPtr()), - base::Bind(&ObjectManager::InterfacesRemovedConnected, - weak_ptr_factory_.GetWeakPtr())); - - GetManagedObjects(); + // Set up a match rule and a filter function to handle PropertiesChanged + // signals from the service. This is important to avoid any race conditions + // that might cause us to miss PropertiesChanged signals once all objects are + // initialized via GetManagedObjects. + base::PostTaskAndReplyWithResult( + bus_->GetDBusTaskRunner(), + FROM_HERE, + base::Bind(&ObjectManager::SetupMatchRuleAndFilter, this), + base::Bind(&ObjectManager::OnSetupMatchRuleAndFilterComplete, this)); } ObjectManager::~ObjectManager() { @@ -144,6 +145,205 @@ void ObjectManager::GetManagedObjects() { weak_ptr_factory_.GetWeakPtr())); } +void ObjectManager::CleanUp() { + DCHECK(bus_); + bus_->AssertOnDBusThread(); + DCHECK(!cleanup_called_); + + cleanup_called_ = true; + + if (!setup_success_) + return; + + if (!bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this)) + LOG(ERROR) << "Failed to remove filter function"; + + ScopedDBusError error; + bus_->RemoveMatch(match_rule_, error.get()); + if (error.is_set()) + LOG(ERROR) << "Failed to remove match rule: " << match_rule_; + + match_rule_.clear(); +} + +void ObjectManager::InitializeObjects() { + DCHECK(bus_); + DCHECK(object_proxy_); + DCHECK(setup_success_); + + // |object_proxy_| is no longer valid if the Bus was shut down before this + // call. Don't initiate any other action from the origin thread. + if (cleanup_called_) + return; + + object_proxy_->ConnectToSignal( + kObjectManagerInterface, + kObjectManagerInterfacesAdded, + base::Bind(&ObjectManager::InterfacesAddedReceived, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&ObjectManager::InterfacesAddedConnected, + weak_ptr_factory_.GetWeakPtr())); + + object_proxy_->ConnectToSignal( + kObjectManagerInterface, + kObjectManagerInterfacesRemoved, + base::Bind(&ObjectManager::InterfacesRemovedReceived, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&ObjectManager::InterfacesRemovedConnected, + weak_ptr_factory_.GetWeakPtr())); + + GetManagedObjects(); +} + +bool ObjectManager::SetupMatchRuleAndFilter() { + DCHECK(bus_); + DCHECK(!setup_success_); + bus_->AssertOnDBusThread(); + + if (cleanup_called_) + return false; + + if (!bus_->Connect() || !bus_->SetUpAsyncOperations()) + return false; + + service_name_owner_ = + bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS); + + const std::string match_rule = + base::StringPrintf( + "type='signal', sender='%s', interface='%s', member='%s'", + service_name_.c_str(), + kPropertiesInterface, + kPropertiesChanged); + + if (!bus_->AddFilterFunction(&ObjectManager::HandleMessageThunk, this)) { + LOG(ERROR) << "ObjectManager failed to add filter function"; + return false; + } + + ScopedDBusError error; + bus_->AddMatch(match_rule, error.get()); + if (error.is_set()) { + LOG(ERROR) << "ObjectManager failed to add match rule \"" << match_rule + << "\". Got " << error.name() << ": " << error.message(); + bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this); + return false; + } + + match_rule_ = match_rule; + setup_success_ = true; + + return true; +} + +void ObjectManager::OnSetupMatchRuleAndFilterComplete(bool success) { + LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value() + << ": Failed to set up match rule."; + if (success) + InitializeObjects(); +} + +// static +DBusHandlerResult ObjectManager::HandleMessageThunk(DBusConnection* connection, + DBusMessage* raw_message, + void* user_data) { + ObjectManager* self = reinterpret_cast<ObjectManager*>(user_data); + return self->HandleMessage(connection, raw_message); +} + +DBusHandlerResult ObjectManager::HandleMessage(DBusConnection* connection, + DBusMessage* raw_message) { + DCHECK(bus_); + bus_->AssertOnDBusThread(); + + if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + // raw_message will be unrefed on exit of the function. Increment the + // reference so we can use it in Signal. + dbus_message_ref(raw_message); + scoped_ptr<Signal> signal( + Signal::FromRawMessage(raw_message)); + + const std::string interface = signal->GetInterface(); + const std::string member = signal->GetMember(); + + statistics::AddReceivedSignal(service_name_, interface, member); + + // Only handle the PropertiesChanged signal. + const std::string absolute_signal_name = + GetAbsoluteMemberName(interface, member); + const std::string properties_changed_signal_name = + GetAbsoluteMemberName(kPropertiesInterface, kPropertiesChanged); + if (absolute_signal_name != properties_changed_signal_name) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + + VLOG(1) << "Signal received: " << signal->ToString(); + + // Make sure that the signal originated from the correct sender. + std::string sender = signal->GetSender(); + if (service_name_owner_ != sender) { + LOG(ERROR) << "Rejecting a message from a wrong sender."; + UMA_HISTOGRAM_COUNTS("DBus.RejectedSignalCount", 1); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + } + + const ObjectPath path = signal->GetPath(); + + if (bus_->HasDBusThread()) { + // Post a task to run the method in the origin thread. Transfer ownership of + // |signal| to NotifyPropertiesChanged, which will handle the clean up. + Signal* released_signal = signal.release(); + bus_->GetOriginTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&ObjectManager::NotifyPropertiesChanged, + this, path, + released_signal)); + } else { + // If the D-Bus thread is not used, just call the callback on the + // current thread. Transfer the ownership of |signal| to + // NotifyPropertiesChanged. + NotifyPropertiesChanged(path, signal.release()); + } + + // We don't return DBUS_HANDLER_RESULT_HANDLED for signals because other + // objects may be interested in them. (e.g. Signals from org.freedesktop.DBus) + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +void ObjectManager::NotifyPropertiesChanged( + const dbus::ObjectPath object_path, + Signal* signal) { + DCHECK(bus_); + bus_->AssertOnOriginThread(); + + NotifyPropertiesChangedHelper(object_path, signal); + + // Delete the message on the D-Bus thread. See comments in HandleMessage. + bus_->GetDBusTaskRunner()->PostTask( + FROM_HERE, + base::Bind(&base::DeletePointer<Signal>, signal)); +} + +void ObjectManager::NotifyPropertiesChangedHelper( + const dbus::ObjectPath object_path, + Signal* signal) { + DCHECK(bus_); + bus_->AssertOnOriginThread(); + + MessageReader reader(signal); + std::string interface; + if (!reader.PopString(&interface)) { + LOG(WARNING) << "Property changed signal has wrong parameters: " + << "expected interface name: " << signal->ToString(); + return; + } + + PropertySet* properties = GetProperties(object_path, interface); + if (properties) + properties->ChangedReceived(signal); +} + void ObjectManager::OnGetManagedObjects(Response* response) { if (response != NULL) { MessageReader reader(response); @@ -257,7 +457,6 @@ void ObjectManager::AddInterface(const ObjectPath& object_path, property_set = object->properties_map[interface_name] = interface->CreateProperties(object->object_proxy, object_path, interface_name); - property_set->ConnectSignals(); } else property_set = piter->second; @@ -297,6 +496,8 @@ void ObjectManager::RemoveInterface(const ObjectPath& object_path, void ObjectManager::NameOwnerChanged(const std::string& old_owner, const std::string& new_owner) { + service_name_owner_ = new_owner; + if (!old_owner.empty()) { ObjectMap::iterator iter = object_map_.begin(); while (iter != object_map_.end()) { diff --git a/chromium/dbus/object_manager.h b/chromium/dbus/object_manager.h index 0d3ae5d0616..23a88cdc893 100644 --- a/chromium/dbus/object_manager.h +++ b/chromium/dbus/object_manager.h @@ -79,7 +79,7 @@ // Example: // dbus::PropertySet* CreateProperties(dbus::ObjectProxy* object_proxy, // const std::string& interface_name) -// OVERRIDE { +// override { // Properties* properties = new Properties( // object_proxy, interface_name, // base::Bind(&PropertyChanged, @@ -223,12 +223,47 @@ public: // a need to call this manually. void GetManagedObjects(); + // Cleans up any match rules and filter functions added by this ObjectManager. + // The Bus object will take care of this so you don't have to do it manually. + // + // BLOCKING CALL. + void CleanUp(); + protected: virtual ~ObjectManager(); private: friend class base::RefCountedThreadSafe<ObjectManager>; + // Connects the InterfacesAdded and InterfacesRemoved signals and calls + // GetManagedObjects. Called from OnSetupMatchRuleAndFilterComplete. + void InitializeObjects(); + + // Called from the constructor to add a match rule for PropertiesChanged + // signals on the DBus thread and set up a corresponding filter function. + bool SetupMatchRuleAndFilter(); + + // Called on the origin thread once the match rule and filter have been set + // up. |success| is false, if an error occurred during set up; it's true + // otherwise. + void OnSetupMatchRuleAndFilterComplete(bool success); + + // Called by dbus:: when a message is received. This is used to filter + // PropertiesChanged signals from the correct sender and relay the event to + // the correct PropertySet. + static DBusHandlerResult HandleMessageThunk(DBusConnection* connection, + DBusMessage* raw_message, + void* user_data); + DBusHandlerResult HandleMessage(DBusConnection* connection, + DBusMessage* raw_message); + + // Called when a PropertiesChanged signal is received from the sender. + // This method notifies the relevant PropertySet that it should update its + // properties based on the received signal. Called from HandleMessage. + void NotifyPropertiesChanged(const dbus::ObjectPath object_path, + Signal* signal); + void NotifyPropertiesChangedHelper(const dbus::ObjectPath object_path, + Signal* signal); // Called by dbus:: in response to the GetManagedObjects() method call. void OnGetManagedObjects(Response* response); @@ -281,8 +316,12 @@ public: Bus* bus_; std::string service_name_; + std::string service_name_owner_; + std::string match_rule_; ObjectPath object_path_; ObjectProxy* object_proxy_; + bool setup_success_; + bool cleanup_called_; // Maps the name of an interface to the implementation class used for // instantiating PropertySet structures for that interface's properties. diff --git a/chromium/dbus/object_manager_unittest.cc b/chromium/dbus/object_manager_unittest.cc index 3e53095b12e..cd02c5e13c1 100644 --- a/chromium/dbus/object_manager_unittest.cc +++ b/chromium/dbus/object_manager_unittest.cc @@ -10,6 +10,7 @@ #include "base/basictypes.h" #include "base/bind.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" #include "dbus/bus.h" @@ -27,7 +28,7 @@ class ObjectManagerTest : public testing::Test, public ObjectManager::Interface { public: - ObjectManagerTest() { + ObjectManagerTest() : timeout_expired_(false) { } struct Properties : public PropertySet { @@ -50,7 +51,7 @@ class ObjectManagerTest virtual PropertySet* CreateProperties( ObjectProxy* object_proxy, const ObjectPath& object_path, - const std::string& interface_name) OVERRIDE { + const std::string& interface_name) override { Properties* properties = new Properties( object_proxy, interface_name, base::Bind(&ObjectManagerTest::OnPropertyChanged, @@ -89,7 +90,6 @@ class ObjectManagerTest ObjectPath("/org/chromium/TestService")); object_manager_->RegisterInterface("org.chromium.TestInterface", this); - object_manager_->GetManagedObjects(); WaitForObject(); } @@ -105,33 +105,55 @@ class ObjectManagerTest // Stopping a thread is considered an IO operation, so do this after // allowing IO. test_service_->Stop(); + + base::RunLoop().RunUntilIdle(); } void MethodCallback(Response* response) { method_callback_called_ = true; - message_loop_.Quit(); + run_loop_->Quit(); + } + + // Called from the PropertiesChangedAsObjectsReceived test case. The test will + // not run the message loop if it receives the expected PropertiesChanged + // signal before the timeout. This method immediately fails the test. + void PropertiesChangedTestTimeout() { + timeout_expired_ = true; + run_loop_->Quit(); + + FAIL() << "Never received PropertiesChanged"; } protected: // Called when an object is added. virtual void ObjectAdded(const ObjectPath& object_path, - const std::string& interface_name) OVERRIDE { + const std::string& interface_name) override { added_objects_.push_back(std::make_pair(object_path, interface_name)); - message_loop_.Quit(); + run_loop_->Quit(); } // Called when an object is removed. virtual void ObjectRemoved(const ObjectPath& object_path, - const std::string& interface_name) OVERRIDE { + const std::string& interface_name) override { removed_objects_.push_back(std::make_pair(object_path, interface_name)); - message_loop_.Quit(); + run_loop_->Quit(); } // Called when a property value is updated. void OnPropertyChanged(const ObjectPath& object_path, const std::string& name) { + // Store the value of the "Name" property if that's the one that + // changed. + Properties* properties = static_cast<Properties*>( + object_manager_->GetProperties( + object_path, + "org.chromium.TestInterface")); + if (name == properties->name.name()) + last_name_value_ = properties->name.value(); + + // Store the updated property. updated_properties_.push_back(name); - message_loop_.Quit(); + run_loop_->Quit(); } static const size_t kExpectedObjects = 1; @@ -139,8 +161,10 @@ class ObjectManagerTest void WaitForObject() { while (added_objects_.size() < kExpectedObjects || - updated_properties_.size() < kExpectedProperties) - message_loop_.Run(); + updated_properties_.size() < kExpectedProperties) { + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + } for (size_t i = 0; i < kExpectedObjects; ++i) added_objects_.erase(added_objects_.begin()); for (size_t i = 0; i < kExpectedProperties; ++i) @@ -148,14 +172,17 @@ class ObjectManagerTest } void WaitForRemoveObject() { - while (removed_objects_.size() < kExpectedObjects) - message_loop_.Run(); + while (removed_objects_.size() < kExpectedObjects) { + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + } for (size_t i = 0; i < kExpectedObjects; ++i) removed_objects_.erase(removed_objects_.begin()); } void WaitForMethodCallback() { - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); method_callback_called_ = false; } @@ -177,11 +204,15 @@ class ObjectManagerTest } base::MessageLoop message_loop_; + scoped_ptr<base::RunLoop> run_loop_; scoped_ptr<base::Thread> dbus_thread_; scoped_refptr<Bus> bus_; ObjectManager* object_manager_; scoped_ptr<TestService> test_service_; + std::string last_name_value_; + bool timeout_expired_; + std::vector<std::pair<ObjectPath, std::string> > added_objects_; std::vector<std::pair<ObjectPath, std::string> > removed_objects_; std::vector<std::string> updated_properties_; @@ -350,4 +381,38 @@ TEST_F(ObjectManagerTest, OwnershipLostAndRegained) { ASSERT_EQ(1U, object_paths.size()); } +TEST_F(ObjectManagerTest, PropertiesChangedAsObjectsReceived) { + // Remove the existing object manager. + object_manager_->UnregisterInterface("org.chromium.TestInterface"); + run_loop_.reset(new base::RunLoop); + EXPECT_TRUE(bus_->RemoveObjectManager( + "org.chromium.TestService", + ObjectPath("/org/chromium/TestService"), + run_loop_->QuitClosure())); + run_loop_->Run(); + + PerformAction("SetSendImmediatePropertiesChanged", + ObjectPath("/org/chromium/TestService")); + + object_manager_ = bus_->GetObjectManager( + "org.chromium.TestService", + ObjectPath("/org/chromium/TestService")); + object_manager_->RegisterInterface("org.chromium.TestInterface", this); + + // The newly created object manager should call GetManagedObjects immediately + // after setting up the match rule for PropertiesChanged. We should process + // the PropertiesChanged event right after that. If we don't receive it within + // 2 seconds, then fail the test. + message_loop_.PostDelayedTask( + FROM_HERE, + base::Bind(&ObjectManagerTest::PropertiesChangedTestTimeout, + base::Unretained(this)), + base::TimeDelta::FromSeconds(2)); + + while (last_name_value_ != "ChangedTestServiceName" && !timeout_expired_) { + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + } +} + } // namespace dbus diff --git a/chromium/dbus/object_proxy.cc b/chromium/dbus/object_proxy.cc index 5167cab7603..59584f2ce34 100644 --- a/chromium/dbus/object_proxy.cc +++ b/chromium/dbus/object_proxy.cc @@ -18,12 +18,14 @@ #include "dbus/object_path.h" #include "dbus/object_proxy.h" #include "dbus/scoped_dbus_error.h" +#include "dbus/util.h" namespace dbus { namespace { const char kErrorServiceUnknown[] = "org.freedesktop.DBus.Error.ServiceUnknown"; +const char kErrorObjectUnknown[] = "org.freedesktop.DBus.Error.UnknownObject"; // Used for success ratio histograms. 1 for success, 0 for failure. const int kSuccessRatioHistogramMaxValue = 2; @@ -40,15 +42,6 @@ const char kDBusSystemObjectAddress[] = "org.freedesktop.DBus"; // The NameOwnerChanged member in |kDBusSystemObjectInterface|. const char kNameOwnerChangedMember[] = "NameOwnerChanged"; -// Gets the absolute signal name by concatenating the interface name and -// the signal name. Used for building keys for method_table_ in -// ObjectProxy. -std::string GetAbsoluteSignalName( - const std::string& interface_name, - const std::string& signal_name) { - return interface_name + "." + signal_name; -} - // An empty function used for ObjectProxy::EmptyResponseCallback(). void EmptyResponseCallbackBody(Response* /*response*/) { } @@ -73,8 +66,8 @@ ObjectProxy::~ObjectProxy() { // Originally we tried to make |method_call| a const reference, but we // gave up as dbus_connection_send_with_reply_and_block() takes a // non-const pointer of DBusMessage as the second parameter. -scoped_ptr<Response> ObjectProxy::CallMethodAndBlock(MethodCall* method_call, - int timeout_ms) { +scoped_ptr<Response> ObjectProxy::CallMethodAndBlockWithErrorDetails( + MethodCall* method_call, int timeout_ms, ScopedDBusError* error) { bus_->AssertOnDBusThread(); if (!bus_->Connect() || @@ -84,12 +77,10 @@ scoped_ptr<Response> ObjectProxy::CallMethodAndBlock(MethodCall* method_call, DBusMessage* request_message = method_call->raw_message(); - ScopedDBusError error; - // Send the message synchronously. const base::TimeTicks start_time = base::TimeTicks::Now(); DBusMessage* response_message = - bus_->SendWithReplyAndBlock(request_message, timeout_ms, error.get()); + bus_->SendWithReplyAndBlock(request_message, timeout_ms, error->get()); // Record if the method call is successful, or not. 1 if successful. UMA_HISTOGRAM_ENUMERATION("DBus.SyncMethodCallSuccess", response_message ? 1 : 0, @@ -101,8 +92,8 @@ scoped_ptr<Response> ObjectProxy::CallMethodAndBlock(MethodCall* method_call, if (!response_message) { LogMethodCallFailure(method_call->GetInterface(), method_call->GetMember(), - error.is_set() ? error.name() : "unknown error type", - error.is_set() ? error.message() : ""); + error->is_set() ? error->name() : "unknown error type", + error->is_set() ? error->message() : ""); return scoped_ptr<Response>(); } // Record time spent for the method call. Don't include failures. @@ -112,6 +103,12 @@ scoped_ptr<Response> ObjectProxy::CallMethodAndBlock(MethodCall* method_call, return Response::FromRawMessage(response_message); } +scoped_ptr<Response> ObjectProxy::CallMethodAndBlock(MethodCall* method_call, + int timeout_ms) { + ScopedDBusError error; + return CallMethodAndBlockWithErrorDetails(method_call, timeout_ms, &error); +} + void ObjectProxy::CallMethod(MethodCall* method_call, int timeout_ms, ResponseCallback callback) { @@ -420,7 +417,7 @@ bool ObjectProxy::ConnectToSignalInternal(const std::string& interface_name, return false; const std::string absolute_signal_name = - GetAbsoluteSignalName(interface_name, signal_name); + GetAbsoluteMemberName(interface_name, signal_name); // Add a match rule so the signal goes through HandleMessage(). const std::string match_rule = @@ -487,7 +484,7 @@ DBusHandlerResult ObjectProxy::HandleMessage( statistics::AddReceivedSignal(service_name_, interface, member); // Check if we know about the signal. - const std::string absolute_signal_name = GetAbsoluteSignalName( + const std::string absolute_signal_name = GetAbsoluteMemberName( interface, member); MethodTable::const_iterator iter = method_table_.find(absolute_signal_name); if (iter == method_table_.end()) { @@ -561,12 +558,20 @@ void ObjectProxy::LogMethodCallFailure( const base::StringPiece& method_name, const base::StringPiece& error_name, const base::StringPiece& error_message) const { - if (ignore_service_unknown_errors_ && error_name == kErrorServiceUnknown) + if (ignore_service_unknown_errors_ && + (error_name == kErrorServiceUnknown || error_name == kErrorObjectUnknown)) return; - LOG(ERROR) << "Failed to call method: " - << interface_name << "." << method_name - << ": object_path= " << object_path_.value() - << ": " << error_name << ": " << error_message; + logging::LogSeverity severity = logging::LOG_ERROR; + // "UnknownObject" indicates that an object or service is no longer available, + // e.g. a Shill network service has gone out of range. Treat these as warnings + // not errors. + if (error_name == kErrorObjectUnknown) + severity = logging::LOG_WARNING; + std::ostringstream msg; + msg << "Failed to call method: " << interface_name << "." << method_name + << ": object_path= " << object_path_.value() + << ": " << error_name << ": " << error_message; + logging::LogAtLevel(severity, msg.str()); } void ObjectProxy::OnCallMethodError(const std::string& interface_name, diff --git a/chromium/dbus/object_proxy.h b/chromium/dbus/object_proxy.h index 48c9c03208a..1400328fda0 100644 --- a/chromium/dbus/object_proxy.h +++ b/chromium/dbus/object_proxy.h @@ -25,13 +25,14 @@ class Bus; class ErrorResponse; class MethodCall; class Response; +class ScopedDBusError; class Signal; // ObjectProxy is used to communicate with remote objects, mainly for // calling methods of these objects. // // ObjectProxy is a ref counted object, to ensure that |this| of the -// object is is alive when callbacks referencing |this| are called; the +// object is alive when callbacks referencing |this| are called; the // bus always holds at least one of those references so object proxies // always last as long as the bus that created them. class CHROME_DBUS_EXPORT ObjectProxy @@ -46,7 +47,8 @@ class CHROME_DBUS_EXPORT ObjectProxy // Options to be OR-ed together when calling Bus::GetObjectProxyWithOptions(). // Set the IGNORE_SERVICE_UNKNOWN_ERRORS option to silence logging of - // org.freedesktop.DBus.Error.ServiceUnknown errors. + // org.freedesktop.DBus.Error.ServiceUnknown errors and + // org.freedesktop.DBus.Error.ObjectUnknown errors. enum Options { DEFAULT_OPTIONS = 0, IGNORE_SERVICE_UNKNOWN_ERRORS = 1 << 0 @@ -90,6 +92,16 @@ class CHROME_DBUS_EXPORT ObjectProxy OnConnectedCallback; // Calls the method of the remote object and blocks until the response + // is returned. Returns NULL on error with the error details specified + // in the |error| object. + // + // BLOCKING CALL. + virtual scoped_ptr<Response> CallMethodAndBlockWithErrorDetails( + MethodCall* method_call, + int timeout_ms, + ScopedDBusError* error); + + // Calls the method of the remote object and blocks until the response // is returned. Returns NULL on error. // // BLOCKING CALL. @@ -304,7 +316,7 @@ class CHROME_DBUS_EXPORT ObjectProxy const bool ignore_service_unknown_errors_; - // Known name owner of the well-known bus name represnted by |service_name_|. + // Known name owner of the well-known bus name represented by |service_name_|. std::string service_name_owner_; DISALLOW_COPY_AND_ASSIGN(ObjectProxy); diff --git a/chromium/dbus/object_proxy_unittest.cc b/chromium/dbus/object_proxy_unittest.cc index 1cf667c3465..246058ddd5d 100644 --- a/chromium/dbus/object_proxy_unittest.cc +++ b/chromium/dbus/object_proxy_unittest.cc @@ -15,7 +15,7 @@ namespace { class ObjectProxyTest : public testing::Test { protected: - virtual void SetUp() OVERRIDE { + virtual void SetUp() override { Bus::Options bus_options; bus_options.bus_type = Bus::SESSION; bus_options.connection_type = Bus::PRIVATE; @@ -25,7 +25,7 @@ class ObjectProxyTest : public testing::Test { "org.chromium.TestService", ObjectPath("/org/chromium/TestObject")); } - virtual void TearDown() OVERRIDE { + virtual void TearDown() override { bus_->ShutdownAndBlock(); } diff --git a/chromium/dbus/property.cc b/chromium/dbus/property.cc index 774719f8bf8..9475a0728a6 100644 --- a/chromium/dbus/property.cc +++ b/chromium/dbus/property.cc @@ -136,7 +136,7 @@ void PropertySet::GetAll() { void PropertySet::OnGetAll(Response* response) { if (!response) { - LOG(WARNING) << "GetAll request failed."; + LOG(WARNING) << "GetAll request failed for: " << interface_; return; } @@ -163,7 +163,8 @@ void PropertySet::Set(PropertyBase* property, SetCallback callback) { callback)); } -void PropertySet::OnSet(PropertyBase* property, SetCallback callback, +void PropertySet::OnSet(PropertyBase* property, + SetCallback callback, Response* response) { LOG_IF(WARNING, !response) << property->name() << ": Set: failed."; if (!callback.is_null()) @@ -477,4 +478,19 @@ void Property<std::vector<uint8> >::AppendSetValueToWriter( writer->CloseContainer(&variant_writer); } +template class Property<uint8>; +template class Property<bool>; +template class Property<int16>; +template class Property<uint16>; +template class Property<int32>; +template class Property<uint32>; +template class Property<int64>; +template class Property<uint64>; +template class Property<double>; +template class Property<std::string>; +template class Property<ObjectPath>; +template class Property<std::vector<std::string> >; +template class Property<std::vector<ObjectPath> >; +template class Property<std::vector<uint8> >; + } // namespace dbus diff --git a/chromium/dbus/property.h b/chromium/dbus/property.h index f6baffc8a73..a20e0ad466f 100644 --- a/chromium/dbus/property.h +++ b/chromium/dbus/property.h @@ -409,67 +409,81 @@ class CHROME_DBUS_EXPORT Property : public PropertyBase { template <> Property<uint8>::Property(); template <> bool Property<uint8>::PopValueFromReader(MessageReader* reader); template <> void Property<uint8>::AppendSetValueToWriter(MessageWriter* writer); +extern template class Property<uint8>; template <> Property<bool>::Property(); template <> bool Property<bool>::PopValueFromReader(MessageReader* reader); template <> void Property<bool>::AppendSetValueToWriter(MessageWriter* writer); +extern template class Property<bool>; template <> Property<int16>::Property(); template <> bool Property<int16>::PopValueFromReader(MessageReader* reader); template <> void Property<int16>::AppendSetValueToWriter(MessageWriter* writer); +extern template class Property<int16>; template <> Property<uint16>::Property(); template <> bool Property<uint16>::PopValueFromReader(MessageReader* reader); template <> void Property<uint16>::AppendSetValueToWriter( MessageWriter* writer); +extern template class Property<uint16>; template <> Property<int32>::Property(); template <> bool Property<int32>::PopValueFromReader(MessageReader* reader); template <> void Property<int32>::AppendSetValueToWriter(MessageWriter* writer); +extern template class Property<int32>; template <> Property<uint32>::Property(); template <> bool Property<uint32>::PopValueFromReader(MessageReader* reader); template <> void Property<uint32>::AppendSetValueToWriter( MessageWriter* writer); +extern template class Property<uint32>; template <> Property<int64>::Property(); template <> bool Property<int64>::PopValueFromReader(MessageReader* reader); template <> void Property<int64>::AppendSetValueToWriter(MessageWriter* writer); +extern template class Property<int64>; template <> Property<uint64>::Property(); template <> bool Property<uint64>::PopValueFromReader(MessageReader* reader); template <> void Property<uint64>::AppendSetValueToWriter( MessageWriter* writer); +extern template class Property<uint64>; template <> Property<double>::Property(); template <> bool Property<double>::PopValueFromReader(MessageReader* reader); template <> void Property<double>::AppendSetValueToWriter( MessageWriter* writer); +extern template class Property<double>; template <> bool Property<std::string>::PopValueFromReader( MessageReader* reader); template <> void Property<std::string>::AppendSetValueToWriter( MessageWriter* writer); +extern template class Property<std::string>; template <> bool Property<ObjectPath>::PopValueFromReader( MessageReader* reader); template <> void Property<ObjectPath>::AppendSetValueToWriter( MessageWriter* writer); +extern template class Property<ObjectPath>; template <> bool Property<std::vector<std::string> >::PopValueFromReader( MessageReader* reader); template <> void Property<std::vector<std::string> >::AppendSetValueToWriter( MessageWriter* writer); +extern template class Property<std::vector<std::string> >; template <> bool Property<std::vector<ObjectPath> >::PopValueFromReader( MessageReader* reader); template <> void Property<std::vector<ObjectPath> >::AppendSetValueToWriter( MessageWriter* writer); +extern template class Property<std::vector<ObjectPath> >; template <> bool Property<std::vector<uint8> >::PopValueFromReader( MessageReader* reader); template <> void Property<std::vector<uint8> >::AppendSetValueToWriter( MessageWriter* writer); +extern template class Property<std::vector<uint8> >; } // namespace dbus diff --git a/chromium/dbus/property_unittest.cc b/chromium/dbus/property_unittest.cc index e078c0044d1..879a9a60665 100644 --- a/chromium/dbus/property_unittest.cc +++ b/chromium/dbus/property_unittest.cc @@ -11,6 +11,7 @@ #include "base/bind.h" #include "base/logging.h" #include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" #include "dbus/bus.h" @@ -105,20 +106,22 @@ class PropertyTest : public testing::Test { // waited for. void PropertyCallback(const std::string& id, bool success) { last_callback_ = id; - message_loop_.Quit(); + run_loop_->Quit(); } protected: // Called when a property value is updated. void OnPropertyChanged(const std::string& name) { updated_properties_.push_back(name); - message_loop_.Quit(); + run_loop_->Quit(); } // Waits for the given number of updates. void WaitForUpdates(size_t num_updates) { - while (updated_properties_.size() < num_updates) - message_loop_.Run(); + while (updated_properties_.size() < num_updates) { + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + } for (size_t i = 0; i < num_updates; ++i) updated_properties_.erase(updated_properties_.begin()); } @@ -136,11 +139,13 @@ class PropertyTest : public testing::Test { // other; you can set this to whatever you wish. void WaitForCallback(const std::string& id) { while (last_callback_ != id) { - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); } } base::MessageLoop message_loop_; + scoped_ptr<base::RunLoop> run_loop_; scoped_ptr<base::Thread> dbus_thread_; scoped_refptr<Bus> bus_; ObjectProxy* object_proxy_; diff --git a/chromium/dbus/scoped_dbus_error.cc b/chromium/dbus/scoped_dbus_error.cc new file mode 100644 index 00000000000..fa049718ef0 --- /dev/null +++ b/chromium/dbus/scoped_dbus_error.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2012 The Chromium 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 "dbus/scoped_dbus_error.h" + +namespace dbus { + +ScopedDBusError::ScopedDBusError() { + dbus_error_init(&error_); +} + +ScopedDBusError::~ScopedDBusError() { + dbus_error_free(&error_); +} + +bool ScopedDBusError::is_set() const { + return dbus_error_is_set(&error_); +} + +} // namespace dbus diff --git a/chromium/dbus/scoped_dbus_error.h b/chromium/dbus/scoped_dbus_error.h index c15c44397b9..1484dbb81b4 100644 --- a/chromium/dbus/scoped_dbus_error.h +++ b/chromium/dbus/scoped_dbus_error.h @@ -7,21 +7,20 @@ #include <dbus/dbus.h> +#include "dbus/dbus_export.h" + namespace dbus { // Utility class to ensure that DBusError is freed. -class ScopedDBusError { +class CHROME_DBUS_EXPORT ScopedDBusError { public: - ScopedDBusError() { - dbus_error_init(&error_); - } - - ~ScopedDBusError() { - dbus_error_free(&error_); - } + // Do not inline methods that call dbus_error_xxx() functions. + // See http://crbug.com/416628 + ScopedDBusError(); + ~ScopedDBusError(); DBusError* get() { return &error_; } - bool is_set() const { return dbus_error_is_set(&error_); } + bool is_set() const; const char* name() { return error_.name; } const char* message() { return error_.message; } diff --git a/chromium/dbus/signal_sender_verification_unittest.cc b/chromium/dbus/signal_sender_verification_unittest.cc index 1d8f6e1d64e..c8589f8b641 100644 --- a/chromium/dbus/signal_sender_verification_unittest.cc +++ b/chromium/dbus/signal_sender_verification_unittest.cc @@ -8,6 +8,7 @@ #include "base/metrics/histogram.h" #include "base/metrics/histogram_samples.h" #include "base/metrics/statistics_recorder.h" +#include "base/run_loop.h" #include "base/test/test_timeouts.h" #include "base/threading/platform_thread.h" #include "base/threading/thread_restrictions.h" @@ -65,7 +66,8 @@ class SignalSenderVerificationTest : public testing::Test { base::Bind(&SignalSenderVerificationTest::OnConnected, base::Unretained(this))); // Wait until the object proxy is connected to the signal. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); // Start the test service, using the D-Bus thread. TestService::Options options; @@ -85,8 +87,10 @@ class SignalSenderVerificationTest : public testing::Test { ASSERT_FALSE(test_service2_->has_ownership()); // The name should be owned and known at this point. - if (!on_name_owner_changed_called_) - message_loop_.Run(); + if (!on_name_owner_changed_called_) { + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + } ASSERT_FALSE(latest_name_owner_.empty()); } @@ -117,7 +121,7 @@ class SignalSenderVerificationTest : public testing::Test { void OnOwnershipInternal() { on_ownership_called_ = true; - message_loop_.Quit(); + run_loop_->Quit(); } void OnNameOwnerChanged(bool* called_flag, @@ -125,7 +129,7 @@ class SignalSenderVerificationTest : public testing::Test { const std::string& new_owner) { latest_name_owner_ = new_owner; *called_flag = true; - message_loop_.Quit(); + run_loop_->Quit(); } // Called when the "Test" signal is received, in the main thread. @@ -133,7 +137,7 @@ class SignalSenderVerificationTest : public testing::Test { void OnTestSignal(Signal* signal) { MessageReader reader(signal); ASSERT_TRUE(reader.PopString(&test_signal_string_)); - message_loop_.Quit(); + run_loop_->Quit(); } // Called when connected to the signal. @@ -141,14 +145,15 @@ class SignalSenderVerificationTest : public testing::Test { const std::string& signal_name, bool success) { ASSERT_TRUE(success); - message_loop_.Quit(); + run_loop_->Quit(); } protected: // Wait for the hey signal to be received. void WaitForTestSignal() { // OnTestSignal() will quit the message loop. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); } // Stopping a thread is considered an IO operation, so we need to fiddle with @@ -160,6 +165,7 @@ class SignalSenderVerificationTest : public testing::Test { } base::MessageLoop message_loop_; + scoped_ptr<base::RunLoop> run_loop_; scoped_ptr<base::Thread> dbus_thread_; scoped_refptr<Bus> bus_; ObjectProxy* object_proxy_; @@ -186,7 +192,8 @@ TEST_F(SignalSenderVerificationTest, TestSignalAccepted) { ASSERT_EQ(kMessage, test_signal_string_); } -TEST_F(SignalSenderVerificationTest, TestSignalRejected) { +// Disabled, http://crbug.com/407063 . +TEST_F(SignalSenderVerificationTest, DISABLED_TestSignalRejected) { // To make sure the histogram instance is created. UMA_HISTOGRAM_COUNTS("DBus.RejectedSignalCount", 0); base::HistogramBase* reject_signal_histogram = @@ -223,7 +230,8 @@ TEST_F(SignalSenderVerificationTest, TestOwnerChanged) { ASSERT_FALSE(latest_name_owner_.empty()); test_service_->ShutdownAndBlock(); // OnNameOwnerChanged will PostTask to quit the message loop. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); // latest_name_owner_ should be empty as the owner is gone. ASSERT_TRUE(latest_name_owner_.empty()); @@ -235,9 +243,12 @@ TEST_F(SignalSenderVerificationTest, TestOwnerChanged) { base::Unretained(this), true)); // Both of OnNameOwnerChanged() and OnOwnership() should quit the MessageLoop, // but there's no expected order of those 2 event. - message_loop_.Run(); - if (!on_name_owner_changed_called_ || !on_ownership_called_) - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + if (!on_name_owner_changed_called_ || !on_ownership_called_) { + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + } ASSERT_TRUE(on_name_owner_changed_called_); ASSERT_TRUE(on_ownership_called_); @@ -258,7 +269,8 @@ TEST_F(SignalSenderVerificationTest, TestOwnerStealing) { ASSERT_FALSE(latest_name_owner_.empty()); test_service_->ShutdownAndBlock(); // OnNameOwnerChanged will PostTask to quit the message loop. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); // latest_name_owner_ should be empty as the owner is gone. ASSERT_TRUE(latest_name_owner_.empty()); // Reset the flag as NameOwnerChanged is already received in setup. @@ -275,7 +287,8 @@ TEST_F(SignalSenderVerificationTest, TestOwnerStealing) { ASSERT_TRUE(stealable_test_service.has_ownership()); // OnNameOwnerChanged will PostTask to quit the message loop. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); // Send a signal to check that the service is correctly owned. const char kMessage[] = "hello, world"; @@ -294,9 +307,12 @@ TEST_F(SignalSenderVerificationTest, TestOwnerStealing) { base::Unretained(this), true)); // Both of OnNameOwnerChanged() and OnOwnership() should quit the MessageLoop, // but there's no expected order of those 2 event. - message_loop_.Run(); - if (!on_name_owner_changed_called_ || !on_ownership_called_) - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + if (!on_name_owner_changed_called_ || !on_ownership_called_) { + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + } ASSERT_TRUE(on_name_owner_changed_called_); ASSERT_TRUE(on_ownership_called_); @@ -334,7 +350,8 @@ TEST_F(SignalSenderVerificationTest, DISABLED_TestMultipleObjects) { base::Bind(&SignalSenderVerificationTest::OnConnected, base::Unretained(this))); // Wait until the object proxy is connected to the signal. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); // Send the test signal from the exported object. test_service_->SendTestSignal(kMessage); @@ -348,7 +365,8 @@ TEST_F(SignalSenderVerificationTest, DISABLED_TestMultipleObjects) { ASSERT_FALSE(latest_name_owner_.empty()); test_service_->ShutdownAndBlock(); // OnNameOwnerChanged will PostTask to quit the message loop. - message_loop_.Run(); + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); // latest_name_owner_ should be empty as the owner is gone. ASSERT_TRUE(latest_name_owner_.empty()); @@ -361,8 +379,10 @@ TEST_F(SignalSenderVerificationTest, DISABLED_TestMultipleObjects) { // Both of OnNameOwnerChanged() and OnOwnership() should quit the MessageLoop, // but there's no expected order of those 2 event. while (!on_name_owner_changed_called_ || !second_name_owner_changed_called || - !on_ownership_called_) - message_loop_.Run(); + !on_ownership_called_) { + run_loop_.reset(new base::RunLoop); + run_loop_->Run(); + } ASSERT_TRUE(on_name_owner_changed_called_); ASSERT_TRUE(second_name_owner_changed_called); ASSERT_TRUE(on_ownership_called_); diff --git a/chromium/dbus/test_service.cc b/chromium/dbus/test_service.cc index 96fa8bced98..1ec20702467 100644 --- a/chromium/dbus/test_service.cc +++ b/chromium/dbus/test_service.cc @@ -24,7 +24,7 @@ void EmptyCallback(bool /* success */) { namespace dbus { // Echo, SlowEcho, AsyncEcho, BrokenMethod, GetAll, Get, Set, PerformAction, -// GetManagedObjects. +// GetManagedObjects const int TestService::kNumMethodsToExport = 9; TestService::Options::Options() @@ -39,7 +39,11 @@ TestService::TestService(const Options& options) request_ownership_options_(options.request_ownership_options), dbus_task_runner_(options.dbus_task_runner), on_name_obtained_(false, false), - num_exported_methods_(0) { + num_exported_methods_(0), + send_immediate_properties_changed_(false), + has_ownership_(false), + exported_object_(NULL), + exported_object_manager_(NULL) { } TestService::~TestService() { @@ -160,6 +164,10 @@ void TestService::ReleaseOwnershipInternal( callback); } +void TestService::SetSendImmediatePropertiesChanged() { + send_immediate_properties_changed_ = true; +} + void TestService::OnExported(const std::string& interface_name, const std::string& method_name, bool success) { @@ -471,11 +479,13 @@ void TestService::PerformAction( return; } - if (action == "AddObject") + if (action == "AddObject") { AddObject(object_path); - else if (action == "RemoveObject") + } else if (action == "RemoveObject") { RemoveObject(object_path); - else if (action == "ReleaseOwnership") { + } else if (action == "SetSendImmediatePropertiesChanged") { + SetSendImmediatePropertiesChanged(); + } if (action == "ReleaseOwnership") { ReleaseOwnership(base::Bind(&TestService::PerformActionResponse, base::Unretained(this), method_call, response_sender)); @@ -556,6 +566,9 @@ void TestService::GetManagedObjects( writer.CloseContainer(&array_writer); response_sender.Run(response.Pass()); + + if (send_immediate_properties_changed_) + SendPropertyChangedSignal("ChangedTestServiceName"); } void TestService::AddPropertiesToWriter(MessageWriter* writer) { diff --git a/chromium/dbus/test_service.h b/chromium/dbus/test_service.h index 523864cafd0..cc7d5211f91 100644 --- a/chromium/dbus/test_service.h +++ b/chromium/dbus/test_service.h @@ -106,7 +106,7 @@ class TestService : public base::Thread { bool success); // base::Thread override. - virtual void Run(base::MessageLoop* message_loop) OVERRIDE; + virtual void Run(base::MessageLoop* message_loop) override; // // Exported methods. @@ -173,6 +173,10 @@ class TestService : public base::Thread { // Helper function for ReleaseOwnership(). void ReleaseOwnershipInternal(base::Closure callback); + // Configures the test service to send a PropertiesChanged signal for the + // "Name" property immediately after a call to GetManagedObjects. + void SetSendImmediatePropertiesChanged(); + // Sends the response on completion of the performed action. void PerformActionResponse( MethodCall* method_call, @@ -197,6 +201,10 @@ class TestService : public base::Thread { // The number of methods actually exported. int num_exported_methods_; + // True if a PropertiesChanged signal for the "Name" property should be sent + // immediately following a call to GetManagedObjects. + bool send_immediate_properties_changed_; + // True iff this instance has successfully acquired the name ownership. bool has_ownership_; diff --git a/chromium/dbus/util.cc b/chromium/dbus/util.cc new file mode 100644 index 00000000000..26e5c71629f --- /dev/null +++ b/chromium/dbus/util.cc @@ -0,0 +1,14 @@ +// Copyright 2014 The Chromium 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 "dbus/util.h" + +namespace dbus { + +std::string GetAbsoluteMemberName(const std::string& interface_name, + const std::string& member_name) { + return interface_name + "." + member_name; +} + +} // namespace dbus diff --git a/chromium/dbus/util.h b/chromium/dbus/util.h new file mode 100644 index 00000000000..b983b6fdb86 --- /dev/null +++ b/chromium/dbus/util.h @@ -0,0 +1,28 @@ +// Copyright 2014 The Chromium 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 DBUS_UTIL_H_ +#define DBUS_UTIL_H_ + +#include <string> + +#include "dbus/dbus_export.h" + +namespace dbus { + +// Returns the absolute name of a member by concatanating |interface_name| and +// |member_name|. e.g.: +// GetAbsoluteMemberName( +// "org.freedesktop.DBus.Properties", +// "PropertiesChanged") +// +// => "org.freedesktop.DBus.Properties.PropertiesChanged" +// +CHROME_DBUS_EXPORT std::string GetAbsoluteMemberName( + const std::string& interface_name, + const std::string& member_name); + +} // namespace dbus + +#endif // DBUS_UTIL_H_ diff --git a/chromium/dbus/util_unittest.cc b/chromium/dbus/util_unittest.cc new file mode 100644 index 00000000000..4bf6efa2aee --- /dev/null +++ b/chromium/dbus/util_unittest.cc @@ -0,0 +1,16 @@ +// Copyright 2014 The Chromium 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 "dbus/util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace dbus { + +TEST(UtilTest, GetAbsoluteMemberName) { + EXPECT_EQ("InterfaceName.MemberName", + GetAbsoluteMemberName("InterfaceName", "MemberName")); + EXPECT_EQ(".", GetAbsoluteMemberName("", "")); +} + +} // namespace dbus |