// 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 <brillo/dbus/dbus_signal_handler.h> #include <string> #include <brillo/bind_lambda.h> #include <brillo/dbus/dbus_param_writer.h> #include <dbus/mock_bus.h> #include <dbus/mock_object_proxy.h> #include <gmock/gmock.h> #include <gtest/gtest.h> using testing::AnyNumber; using testing::Return; using testing::SaveArg; using testing::_; namespace brillo { namespace dbus_utils { const char kTestPath[] = "/test/path"; const char kTestServiceName[] = "org.test.Object"; const char kInterface[] = "org.test.Object.TestInterface"; const char kSignal[] = "TestSignal"; class DBusSignalHandlerTest : public testing::Test { public: void SetUp() override { 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 object proxy. mock_object_proxy_ = new dbus::MockObjectProxy( bus_.get(), kTestServiceName, dbus::ObjectPath(kTestPath)); EXPECT_CALL(*bus_, GetObjectProxy(kTestServiceName, dbus::ObjectPath(kTestPath))) .WillRepeatedly(Return(mock_object_proxy_.get())); } void TearDown() override { bus_ = nullptr; } protected: template<typename SignalHandlerSink, typename... Args> void CallSignal(SignalHandlerSink* sink, Args... args) { dbus::ObjectProxy::SignalCallback signal_callback; EXPECT_CALL(*mock_object_proxy_, ConnectToSignal(kInterface, kSignal, _, _)) .WillOnce(SaveArg<2>(&signal_callback)); brillo::dbus_utils::ConnectToSignal( mock_object_proxy_.get(), kInterface, kSignal, base::Bind(&SignalHandlerSink::Handler, base::Unretained(sink)), {}); dbus::Signal signal(kInterface, kSignal); dbus::MessageWriter writer(&signal); DBusParamWriter::Append(&writer, args...); signal_callback.Run(&signal); } scoped_refptr<dbus::MockBus> bus_; scoped_refptr<dbus::MockObjectProxy> mock_object_proxy_; }; TEST_F(DBusSignalHandlerTest, ConnectToSignal) { EXPECT_CALL(*mock_object_proxy_, ConnectToSignal(kInterface, kSignal, _, _)) .Times(1); brillo::dbus_utils::ConnectToSignal( mock_object_proxy_.get(), kInterface, kSignal, base::Closure{}, {}); } TEST_F(DBusSignalHandlerTest, CallSignal_3Args) { class SignalHandlerSink { public: MOCK_METHOD3(Handler, void(int, int, double)); } sink; EXPECT_CALL(sink, Handler(10, 20, 30.5)).Times(1); CallSignal(&sink, 10, 20, 30.5); } TEST_F(DBusSignalHandlerTest, CallSignal_2Args) { class SignalHandlerSink { public: // Take string both by reference and by value to make sure this works too. MOCK_METHOD2(Handler, void(const std::string&, std::string)); } sink; EXPECT_CALL(sink, Handler(std::string{"foo"}, std::string{"bar"})).Times(1); CallSignal(&sink, std::string{"foo"}, std::string{"bar"}); } TEST_F(DBusSignalHandlerTest, CallSignal_NoArgs) { class SignalHandlerSink { public: MOCK_METHOD0(Handler, void()); } sink; EXPECT_CALL(sink, Handler()).Times(1); CallSignal(&sink); } TEST_F(DBusSignalHandlerTest, CallSignal_Error_TooManyArgs) { class SignalHandlerSink { public: MOCK_METHOD0(Handler, void()); } sink; // Handler() expects no args, but we send an int. EXPECT_CALL(sink, Handler()).Times(0); CallSignal(&sink, 5); } TEST_F(DBusSignalHandlerTest, CallSignal_Error_TooFewArgs) { class SignalHandlerSink { public: MOCK_METHOD2(Handler, void(std::string, bool)); } sink; // Handler() expects 2 args while we send it just one. EXPECT_CALL(sink, Handler(_, _)).Times(0); CallSignal(&sink, std::string{"bar"}); } TEST_F(DBusSignalHandlerTest, CallSignal_Error_TypeMismatchArgs) { class SignalHandlerSink { public: MOCK_METHOD2(Handler, void(std::string, bool)); } sink; // Handler() expects "sb" while we send it "ii". EXPECT_CALL(sink, Handler(_, _)).Times(0); CallSignal(&sink, 1, 2); } } // namespace dbus_utils } // namespace brillo