blob: 8b8d0997408bd0099e3eace2f3641ce24e15d86f [file] [log] [blame]
// Copyright 2018 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 "bluetooth/newblued/newblue_daemon.h"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <base/memory/ref_counted.h>
#include <base/message_loop/message_loop.h>
#include <chromeos/dbus/service_constants.h>
#include <dbus/mock_bus.h>
#include <dbus/mock_exported_object.h>
#include <dbus/mock_object_manager.h>
#include <dbus/mock_object_proxy.h>
#include <dbus/object_manager.h>
#include <gtest/gtest.h>
#include "bluetooth/newblued/mock_libnewblue.h"
#include "bluetooth/newblued/mock_newblue.h"
#include "bluetooth/newblued/util.h"
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::Return;
using ::testing::SaveArg;
namespace bluetooth {
namespace {
constexpr char kTestSender[] = ":1.1";
constexpr int kTestSerial = 10;
constexpr char kTestDeviceAddress[] = "06:05:04:03:02:01";
constexpr char kTestDeviceObjectPath[] =
"/org/bluez/hci0/dev_06_05_04_03_02_01";
void SaveResponse(std::unique_ptr<dbus::Response>* saved_response,
std::unique_ptr<dbus::Response> response) {
*saved_response = std::move(response);
}
} // namespace
class NewblueDaemonTest : public ::testing::Test {
public:
using MethodHandlerMap =
std::map<std::string, dbus::ExportedObject::MethodCallCallback*>;
void SetUp() override {
bus_ = new dbus::MockBus(dbus::Bus::Options());
EXPECT_CALL(*bus_, GetDBusTaskRunner())
.WillRepeatedly(Return(message_loop_.task_runner().get()));
EXPECT_CALL(*bus_, AssertOnOriginThread()).Times(AnyNumber());
auto libnewblue = std::make_unique<MockLibNewblue>();
libnewblue_ = libnewblue.get();
auto newblue = std::make_unique<MockNewblue>(std::move(libnewblue));
newblue_ = newblue.get();
newblue_daemon_ = std::make_unique<NewblueDaemon>(std::move(newblue));
SetupBluezObjectProxy();
SetupBluezObjectManager();
// Force MessageLoop to run all pending tasks as an effect of instantiating
// MockObjectManager. This is needed to avoid memory leak as pending tasks
// hold pointers.
message_loop_.RunUntilIdle();
}
// The mocked dbus::ExportedObject::ExportMethod needs to call its callback.
void StubExportMethod(
const std::string& interface_name,
const std::string& method_name,
dbus::ExportedObject::MethodCallCallback method_call_callback,
dbus::ExportedObject::OnExportedCallback on_exported_callback) {
on_exported_callback.Run(interface_name, method_name, true /* success */);
}
protected:
dbus::ExportedObject::MethodCallCallback* GetMethodHandler(
MethodHandlerMap method_handlers, const std::string& method_name) {
return base::ContainsKey(method_handlers, method_name)
? method_handlers[method_name]
: &dummy_method_handler_;
}
// Expects that the standard methods on org.freedesktop.DBus.Properties
// interface are exported.
void ExpectPropertiesMethodsExported(
scoped_refptr<dbus::MockExportedObject> exported_object) {
EXPECT_CALL(*exported_object, ExportMethod(dbus::kPropertiesInterface,
dbus::kPropertiesGet, _, _))
.Times(1);
EXPECT_CALL(*exported_object, ExportMethod(dbus::kPropertiesInterface,
dbus::kPropertiesSet, _, _))
.Times(1);
EXPECT_CALL(*exported_object, ExportMethod(dbus::kPropertiesInterface,
dbus::kPropertiesGetAll, _, _))
.Times(1);
}
// Expects that the methods on org.bluez.Device1 interface are exported.
void ExpectDeviceMethodsExported(
scoped_refptr<dbus::MockExportedObject> exported_object) {
EXPECT_CALL(*exported_object,
ExportMethod(bluetooth_device::kBluetoothDeviceInterface,
bluetooth_device::kPair, _, _))
.Times(1);
EXPECT_CALL(*exported_object,
ExportMethod(bluetooth_device::kBluetoothDeviceInterface,
bluetooth_device::kCancelPairing, _, _))
.Times(1);
EXPECT_CALL(*exported_object,
ExportMethod(bluetooth_device::kBluetoothDeviceInterface,
bluetooth_device::kConnect, _, _))
.Times(1);
}
scoped_refptr<dbus::MockExportedObject> SetupExportedRootObject() {
dbus::ObjectPath root_path(
newblue_object_manager::kNewblueObjectManagerServicePath);
scoped_refptr<dbus::MockExportedObject> exported_root_object =
new dbus::MockExportedObject(bus_.get(), root_path);
EXPECT_CALL(*bus_, GetExportedObject(root_path))
.WillRepeatedly(Return(exported_root_object.get()));
return exported_root_object;
}
scoped_refptr<dbus::MockExportedObject> SetupExportedAgentManagerObject() {
dbus::ObjectPath agent_manager_path(
bluetooth_agent_manager::kBluetoothAgentManagerServicePath);
scoped_refptr<dbus::MockExportedObject> exported_agent_manager_object =
new dbus::MockExportedObject(bus_.get(), agent_manager_path);
EXPECT_CALL(*bus_, GetExportedObject(agent_manager_path))
.WillRepeatedly(Return(exported_agent_manager_object.get()));
return exported_agent_manager_object;
}
void SetupBluezObjectProxy() {
dbus::ObjectPath object_path(
bluez_object_manager::kBluezObjectManagerServicePath);
bluez_object_proxy_ = new dbus::MockObjectProxy(
bus_.get(), bluez_object_manager::kBluezObjectManagerServiceName,
object_path);
EXPECT_CALL(*bus_, GetObjectProxy(
bluez_object_manager::kBluezObjectManagerServiceName,
object_path))
.WillRepeatedly(Return(bluez_object_proxy_.get()));
}
void SetupBluezObjectManager() {
dbus::ObjectPath object_path(
bluez_object_manager::kBluezObjectManagerServicePath);
bluez_object_manager_ = new dbus::MockObjectManager(
bus_.get(), bluez_object_manager::kBluezObjectManagerServiceName,
object_path);
EXPECT_CALL(*bus_, GetObjectManager(
bluez_object_manager::kBluezObjectManagerServiceName,
object_path))
.WillRepeatedly(Return(bluez_object_manager_.get()));
}
void ExpectTestInit(
scoped_refptr<dbus::MockExportedObject> exported_root_object) {
EXPECT_CALL(*bus_,
RequestOwnershipAndBlock(
newblue_object_manager::kNewblueObjectManagerServiceName,
dbus::Bus::REQUIRE_PRIMARY))
.WillOnce(Return(true));
// Standard methods on org.freedesktop.DBus.ObjectManager interface should
// be exported.
EXPECT_CALL(*exported_root_object,
ExportMethod(dbus::kObjectManagerInterface,
dbus::kObjectManagerGetManagedObjects, _, _))
.Times(1);
// Standard methods on org.freedesktop.DBus.Properties interface should be
// exported.
ExpectPropertiesMethodsExported(exported_root_object);
}
void TestInit(scoped_refptr<dbus::MockExportedObject> exported_root_object) {
ExpectTestInit(exported_root_object);
EXPECT_CALL(*newblue_, Init()).WillOnce(Return(true));
EXPECT_CALL(*newblue_, ListenReadyForUp(_)).WillOnce(Return(true));
EXPECT_TRUE(newblue_daemon_->Init(
bus_, nullptr /* no need to access the delegator */));
}
void TestAdapterBringUp(
scoped_refptr<dbus::MockExportedObject> exported_adapter_object,
MethodHandlerMap adapter_method_handlers) {
// Some properties are expected to be exported on the adapter object.
EXPECT_CALL(
*exported_adapter_object,
ExportMethod(dbus::kPropertiesInterface, dbus::kPropertiesGetAll, _, _))
.Times(AnyNumber());
EXPECT_CALL(
*exported_adapter_object,
ExportMethod(dbus::kPropertiesInterface, dbus::kPropertiesGet, _, _))
.Times(AnyNumber());
EXPECT_CALL(
*exported_adapter_object,
ExportMethod(dbus::kPropertiesInterface, dbus::kPropertiesSet, _, _))
.Times(AnyNumber());
// org.bluez.Adapter1 methods
EXPECT_CALL(*exported_adapter_object,
ExportMethod(bluetooth_adapter::kBluetoothAdapterInterface,
bluetooth_adapter::kStartDiscovery, _, _))
.WillOnce(DoAll(
SaveArg<2>(GetMethodHandler(adapter_method_handlers,
bluetooth_adapter::kStartDiscovery)),
Invoke(this, &NewblueDaemonTest::StubExportMethod)));
EXPECT_CALL(*exported_adapter_object,
ExportMethod(bluetooth_adapter::kBluetoothAdapterInterface,
bluetooth_adapter::kStopDiscovery, _, _))
.WillOnce(DoAll(
SaveArg<2>(GetMethodHandler(adapter_method_handlers,
bluetooth_adapter::kStopDiscovery)),
Invoke(this, &NewblueDaemonTest::StubExportMethod)));
EXPECT_CALL(*newblue_, BringUp()).WillOnce(Return(true));
newblue_daemon_->OnHciReadyForUp();
}
base::MessageLoop message_loop_;
scoped_refptr<dbus::MockBus> bus_;
scoped_refptr<dbus::MockObjectProxy> bluez_object_proxy_;
scoped_refptr<dbus::MockObjectManager> bluez_object_manager_;
std::unique_ptr<NewblueDaemon> newblue_daemon_;
MockNewblue* newblue_;
MockLibNewblue* libnewblue_;
dbus::ExportedObject::MethodCallCallback dummy_method_handler_;
};
TEST_F(NewblueDaemonTest, InitFailed) {
scoped_refptr<dbus::MockExportedObject> exported_root_object =
SetupExportedRootObject();
scoped_refptr<dbus::MockExportedObject> exported_agent_manager_object =
SetupExportedAgentManagerObject();
// Newblue::Init() fails
ExpectTestInit(exported_root_object);
EXPECT_CALL(*newblue_, Init()).WillOnce(Return(false));
EXPECT_FALSE(newblue_daemon_->Init(
bus_, nullptr /* no need to access the delegator */));
// Newblue::ListenReadyForUp() fails
ExpectTestInit(exported_root_object);
EXPECT_CALL(*newblue_, Init()).WillOnce(Return(true));
EXPECT_CALL(*newblue_, ListenReadyForUp(_)).WillOnce(Return(false));
EXPECT_FALSE(newblue_daemon_->Init(
bus_, nullptr /* no need to access the delegator */));
// Shutdown now to make sure ExportedObjectManagerWrapper is destructed first
// before the mocked objects.
newblue_daemon_->Shutdown();
}
TEST_F(NewblueDaemonTest, InitSuccessAndBringUp) {
scoped_refptr<dbus::MockExportedObject> exported_root_object =
SetupExportedRootObject();
scoped_refptr<dbus::MockExportedObject> exported_agent_manager_object =
SetupExportedAgentManagerObject();
dbus::ObjectPath adapter_object_path(kAdapterObjectPath);
scoped_refptr<dbus::MockExportedObject> exported_adapter_object =
new dbus::MockExportedObject(bus_.get(), adapter_object_path);
EXPECT_CALL(*bus_, GetExportedObject(adapter_object_path))
.WillOnce(Return(exported_adapter_object.get()));
TestInit(exported_root_object);
MethodHandlerMap adapter_method_handlers;
TestAdapterBringUp(exported_adapter_object, adapter_method_handlers);
EXPECT_CALL(*exported_adapter_object, Unregister()).Times(1);
EXPECT_CALL(*exported_root_object, Unregister()).Times(1);
// Shutdown now to make sure ExportedObjectManagerWrapper is destructed first
// before the mocked objects.
newblue_daemon_->Shutdown();
}
TEST_F(NewblueDaemonTest, DiscoveryAPI) {
scoped_refptr<dbus::MockExportedObject> exported_root_object =
SetupExportedRootObject();
scoped_refptr<dbus::MockExportedObject> exported_agent_manager_object =
SetupExportedAgentManagerObject();
dbus::ObjectPath adapter_object_path(kAdapterObjectPath);
scoped_refptr<dbus::MockExportedObject> exported_adapter_object =
new dbus::MockExportedObject(bus_.get(), adapter_object_path);
EXPECT_CALL(*bus_, GetExportedObject(adapter_object_path))
.WillOnce(Return(exported_adapter_object.get()));
TestInit(exported_root_object);
dbus::ExportedObject::MethodCallCallback start_discovery_handler;
dbus::ExportedObject::MethodCallCallback stop_discovery_handler;
MethodHandlerMap adapter_method_handlers;
adapter_method_handlers[bluetooth_adapter::kStartDiscovery] =
&start_discovery_handler;
adapter_method_handlers[bluetooth_adapter::kStopDiscovery] =
&stop_discovery_handler;
TestAdapterBringUp(exported_adapter_object, adapter_method_handlers);
ASSERT_FALSE(start_discovery_handler.is_null());
ASSERT_FALSE(stop_discovery_handler.is_null());
// StartDiscovery
dbus::MethodCall start_discovery_method_call(
bluetooth_adapter::kBluetoothAdapterInterface,
bluetooth_adapter::kStartDiscovery);
start_discovery_method_call.SetPath(dbus::ObjectPath(kAdapterObjectPath));
start_discovery_method_call.SetSender(kTestSender);
start_discovery_method_call.SetSerial(kTestSerial);
std::unique_ptr<dbus::Response> start_discovery_response;
Newblue::DeviceDiscoveredCallback on_device_discovered;
EXPECT_CALL(*newblue_, StartDiscovery(_))
.WillOnce(DoAll(SaveArg<0>(&on_device_discovered), Return(true)));
start_discovery_handler.Run(
&start_discovery_method_call,
base::Bind(&SaveResponse, &start_discovery_response));
EXPECT_EQ("", start_discovery_response->GetErrorName());
ASSERT_FALSE(on_device_discovered.is_null());
// Device discovered.
dbus::ObjectPath device_object_path(kTestDeviceObjectPath);
scoped_refptr<dbus::MockExportedObject> exported_device_object =
new dbus::MockExportedObject(bus_.get(), device_object_path);
EXPECT_CALL(*bus_, GetExportedObject(device_object_path))
.WillOnce(Return(exported_device_object.get()));
ExpectDeviceMethodsExported(exported_device_object);
ExpectPropertiesMethodsExported(exported_device_object);
Device device(kTestDeviceAddress);
on_device_discovered.Run(device);
// StopDiscovery
dbus::MethodCall stop_discovery_method_call(
bluetooth_adapter::kBluetoothAdapterInterface,
bluetooth_adapter::kStopDiscovery);
stop_discovery_method_call.SetPath(dbus::ObjectPath(kAdapterObjectPath));
stop_discovery_method_call.SetSender(kTestSender);
stop_discovery_method_call.SetSerial(kTestSerial);
std::unique_ptr<dbus::Response> stop_discovery_response;
EXPECT_CALL(*newblue_, StopDiscovery()).WillOnce(Return(true));
stop_discovery_handler.Run(
&stop_discovery_method_call,
base::Bind(&SaveResponse, &stop_discovery_response));
EXPECT_EQ("", stop_discovery_response->GetErrorName());
EXPECT_CALL(*exported_adapter_object, Unregister()).Times(1);
EXPECT_CALL(*exported_root_object, Unregister()).Times(1);
// Shutdown now to make sure ExportedObjectManagerWrapper is destructed first
// before the mocked objects.
newblue_daemon_->Shutdown();
}
} // namespace bluetooth