// Copyright (c) 2011 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 #include #include #include "base/bind.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "base/stl_util.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" #include "dbus/bus.h" #include "dbus/message.h" #include "dbus/object_proxy.h" #include "dbus/test_service.h" #include "testing/gtest/include/gtest/gtest.h" // The end-to-end test exercises the asynchronos APIs in ObjectProxy and // ExportedObject. class EndToEndAsyncTest : public testing::Test { public: EndToEndAsyncTest() { } void SetUp() { // Make the main thread not to allow IO. base::ThreadRestrictions::SetIOAllowed(false); // Start the test service; test_service_.reset(new dbus::TestService); test_service_->StartService(); test_service_->WaitUntilServiceIsStarted(); // Start the D-Bus thread. dbus_thread_.reset(new base::Thread("D-Bus Thread")); base::Thread::Options thread_options; thread_options.message_loop_type = MessageLoop::TYPE_IO; dbus_thread_->StartWithOptions(thread_options); // Create the client. dbus::Bus::Options bus_options; bus_options.bus_type = dbus::Bus::SESSION; bus_options.connection_type = dbus::Bus::PRIVATE; bus_options.dbus_thread = dbus_thread_.get(); bus_ = new dbus::Bus(bus_options); object_proxy_ = bus_->GetObjectProxy("org.chromium.TestService", "/org/chromium/TestObject"); } void TearDown() { bus_->Shutdown(base::Bind(&EndToEndAsyncTest::OnShutdown, base::Unretained(this))); // Wait until the bus is shutdown. OnShutdown() will be called in // mesage_loop_. message_loop_.Run(); // Reset to the default. base::ThreadRestrictions::SetIOAllowed(true); // Stopping a thread is considred an IO operation, so do this after // allowing IO. test_service_->Stop(); } protected: // Calls the method asynchronosly. OnResponse() will be called once the // response is received. void CallMethod(dbus::MethodCall* method_call, int timeout_ms) { object_proxy_->CallMethod(method_call, timeout_ms, base::Bind(&EndToEndAsyncTest::OnResponse, base::Unretained(this))); } // Wait for the give number of responses. void WaitForResponses(size_t num_responses) { while (response_strings_.size() < num_responses) { message_loop_.Run(); } } // Called when the response is received. void OnResponse(dbus::Response* response) { // |response| will be deleted on exit of the function. Copy the // payload to |response_strings_|. if (response) { dbus::MessageReader reader(response); std::string response_string; ASSERT_TRUE(reader.PopString(&response_string)); response_strings_.push_back(response_string); } else { response_strings_.push_back(""); } message_loop_.Quit(); }; // Called when the shutdown is complete. void OnShutdown() { message_loop_.Quit(); } MessageLoop message_loop_; std::vector response_strings_; scoped_ptr dbus_thread_; scoped_refptr bus_; dbus::ObjectProxy* object_proxy_; scoped_ptr test_service_; }; TEST_F(EndToEndAsyncTest, Echo) { const char* kHello = "hello"; // Create the method call. dbus::MethodCall method_call("org.chromium.TestInterface", "Echo"); dbus::MessageWriter writer(&method_call); writer.AppendString(kHello); // Call the method. const int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT; CallMethod(&method_call, timeout_ms); // Check the response. WaitForResponses(1); EXPECT_EQ(kHello, response_strings_[0]); } // Call Echo method three times. TEST_F(EndToEndAsyncTest, EchoThreeTimes) { const char* kMessages[] = { "foo", "bar", "baz" }; for (size_t i = 0; i < arraysize(kMessages); ++i) { // Create the method call. dbus::MethodCall method_call("org.chromium.TestInterface", "Echo"); dbus::MessageWriter writer(&method_call); writer.AppendString(kMessages[i]); // Call the method. const int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT; CallMethod(&method_call, timeout_ms); } // Check the responses. WaitForResponses(3); // Sort as the order of the returned messages is not deterministic. std::sort(response_strings_.begin(), response_strings_.end()); EXPECT_EQ("bar", response_strings_[0]); EXPECT_EQ("baz", response_strings_[1]); EXPECT_EQ("foo", response_strings_[2]); } TEST_F(EndToEndAsyncTest, Timeout) { const char* kHello = "hello"; // Create the method call. dbus::MethodCall method_call("org.chromium.TestInterface", "SlowEcho"); dbus::MessageWriter writer(&method_call); writer.AppendString(kHello); // Call the method with timeout smaller than TestService::kSlowEchoSleepMs. const int timeout_ms = dbus::TestService::kSlowEchoSleepMs / 10; CallMethod(&method_call, timeout_ms); WaitForResponses(1); // Should fail because of timeout. ASSERT_EQ("", response_strings_[0]); } TEST_F(EndToEndAsyncTest, NonexistentMethod) { dbus::MethodCall method_call("org.chromium.TestInterface", "Nonexistent"); const int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT; CallMethod(&method_call, timeout_ms); WaitForResponses(1); // Should fail because the method is nonexistent. ASSERT_EQ("", response_strings_[0]); } TEST_F(EndToEndAsyncTest, BrokenMethod) { dbus::MethodCall method_call("org.chromium.TestInterface", "BrokenMethod"); const int timeout_ms = dbus::ObjectProxy::TIMEOUT_USE_DEFAULT; CallMethod(&method_call, timeout_ms); WaitForResponses(1); // Should fail because the method is broken. ASSERT_EQ("", response_strings_[0]); }