| // Copyright (c) 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 <gtest/gtest.h> |
| |
| extern "C" { |
| #include "cras_bt_constants.h" |
| #include "cras_bt_device.h" |
| #include "cras_bt_io.h" |
| #include "cras_bt_log.h" |
| #include "cras_iodev.h" |
| #include "cras_main_message.h" |
| |
| #define FAKE_OBJ_PATH "/obj/path" |
| } |
| |
| static const unsigned int CONN_WATCH_MAX_RETRIES = 30; |
| |
| static struct cras_iodev* cras_bt_io_create_profile_ret; |
| static struct cras_iodev* cras_bt_io_append_btio_val; |
| static struct cras_ionode* cras_bt_io_get_profile_ret; |
| static unsigned int cras_bt_io_create_called; |
| static unsigned int cras_bt_io_append_called; |
| static unsigned int cras_bt_io_remove_called; |
| static unsigned int cras_bt_io_destroy_called; |
| static enum cras_bt_device_profile cras_bt_io_create_profile_val; |
| static enum cras_bt_device_profile cras_bt_io_append_profile_val; |
| static unsigned int cras_bt_io_try_remove_ret; |
| |
| static cras_main_message* cras_main_message_send_msg; |
| static cras_message_callback cras_main_message_add_handler_callback; |
| static void* cras_main_message_add_handler_callback_data; |
| static int cras_tm_create_timer_called; |
| static int cras_tm_cancel_timer_called; |
| static int cras_a2dp_start_called; |
| static int cras_a2dp_suspend_connected_device_called; |
| static int cras_hfp_ag_remove_conflict_called; |
| static int cras_hfp_ag_start_called; |
| static int cras_hfp_ag_suspend_connected_device_called; |
| static void (*cras_tm_create_timer_cb)(struct cras_timer* t, void* data); |
| static void* cras_tm_create_timer_cb_data; |
| static int dbus_message_new_method_call_called; |
| static const char* dbus_message_new_method_call_method; |
| static struct cras_bt_device* cras_a2dp_connected_device_ret; |
| static struct cras_bt_device* cras_a2dp_suspend_connected_device_dev; |
| static struct cras_timer* cras_tm_cancel_timer_arg; |
| static struct cras_timer* cras_tm_create_timer_ret; |
| static size_t cras_iodev_set_node_plugged_called; |
| static int cras_iodev_set_node_plugged_value; |
| |
| struct MockDBusMessage { |
| int type; |
| void* value; |
| MockDBusMessage* next; |
| MockDBusMessage* recurse; |
| }; |
| |
| void ResetStubData() { |
| cras_bt_io_get_profile_ret = NULL; |
| cras_bt_io_create_called = 0; |
| cras_bt_io_append_called = 0; |
| cras_bt_io_remove_called = 0; |
| cras_bt_io_destroy_called = 0; |
| cras_bt_io_try_remove_ret = 0; |
| cras_main_message_send_msg = NULL; |
| cras_tm_create_timer_called = 0; |
| cras_tm_cancel_timer_called = 0; |
| cras_a2dp_start_called = 0; |
| cras_a2dp_suspend_connected_device_called = 0; |
| cras_hfp_ag_remove_conflict_called = 0; |
| cras_hfp_ag_start_called = 0; |
| cras_hfp_ag_suspend_connected_device_called = 0; |
| dbus_message_new_method_call_method = NULL; |
| dbus_message_new_method_call_called = 0; |
| cras_a2dp_connected_device_ret = NULL; |
| cras_iodev_set_node_plugged_called = 0; |
| } |
| |
| static void FreeMockDBusMessage(MockDBusMessage* head) { |
| if (head->next != NULL) |
| FreeMockDBusMessage(head->next); |
| if (head->recurse != NULL) |
| FreeMockDBusMessage(head->recurse); |
| if (head->type == DBUS_TYPE_STRING) |
| free((char*)head->value); |
| delete head; |
| } |
| |
| static struct MockDBusMessage* NewMockDBusConnectedMessage(long connected) { |
| MockDBusMessage* msg = new MockDBusMessage{DBUS_TYPE_ARRAY, NULL}; |
| MockDBusMessage* dict = |
| new MockDBusMessage{DBUS_TYPE_STRING, (void*)strdup("Connected")}; |
| MockDBusMessage* variant = |
| new MockDBusMessage{DBUS_TYPE_BOOLEAN, (void*)connected}; |
| |
| msg->recurse = dict; |
| dict->next = new MockDBusMessage{DBUS_TYPE_INVALID, NULL}; |
| dict->next->recurse = variant; |
| return msg; |
| } |
| |
| namespace { |
| |
| class BtDeviceTestSuite : public testing::Test { |
| protected: |
| virtual void SetUp() { |
| ResetStubData(); |
| bt_iodev1.direction = CRAS_STREAM_OUTPUT; |
| bt_iodev1.update_active_node = update_active_node; |
| bt_iodev2.direction = CRAS_STREAM_INPUT; |
| bt_iodev2.update_active_node = update_active_node; |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| d1_.update_active_node = update_active_node; |
| d2_.direction = CRAS_STREAM_OUTPUT; |
| d2_.update_active_node = update_active_node; |
| d3_.direction = CRAS_STREAM_INPUT; |
| d3_.update_active_node = update_active_node; |
| btlog = cras_bt_event_log_init(); |
| } |
| |
| virtual void TearDown() { |
| if (cras_main_message_send_msg) |
| free(cras_main_message_send_msg); |
| cras_bt_event_log_deinit(btlog); |
| } |
| |
| static void update_active_node(struct cras_iodev* iodev, |
| unsigned node_idx, |
| unsigned dev_enabled) {} |
| |
| struct cras_iodev bt_iodev1; |
| struct cras_iodev bt_iodev2; |
| struct cras_iodev d3_; |
| struct cras_iodev d2_; |
| struct cras_iodev d1_; |
| }; |
| |
| TEST(BtDeviceSuite, CreateBtDevice) { |
| struct cras_bt_device* device; |
| |
| device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); |
| EXPECT_NE((void*)NULL, device); |
| |
| device = cras_bt_device_get(FAKE_OBJ_PATH); |
| EXPECT_NE((void*)NULL, device); |
| |
| cras_bt_device_remove(device); |
| device = cras_bt_device_get(FAKE_OBJ_PATH); |
| EXPECT_EQ((void*)NULL, device); |
| } |
| |
| TEST_F(BtDeviceTestSuite, AppendRmIodev) { |
| struct cras_bt_device* device; |
| device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); |
| bt_iodev1.nodes = reinterpret_cast<struct cras_ionode*>(0x123); |
| cras_bt_io_create_profile_ret = &bt_iodev1; |
| cras_bt_device_append_iodev(device, &d1_, CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE); |
| EXPECT_EQ(1, cras_bt_io_create_called); |
| EXPECT_EQ(0, cras_bt_io_append_called); |
| EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE, cras_bt_io_create_profile_val); |
| cras_bt_device_set_active_profile(device, CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE); |
| |
| cras_bt_device_append_iodev(device, &d2_, |
| CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); |
| EXPECT_EQ(1, cras_bt_io_create_called); |
| EXPECT_EQ(1, cras_bt_io_append_called); |
| EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY, |
| cras_bt_io_append_profile_val); |
| EXPECT_EQ(&bt_iodev1, cras_bt_io_append_btio_val); |
| |
| /* Test HFP disconnected and switch to A2DP. */ |
| cras_bt_io_get_profile_ret = bt_iodev1.nodes; |
| cras_bt_io_try_remove_ret = CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE; |
| cras_bt_device_set_active_profile(device, |
| CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); |
| cras_bt_device_rm_iodev(device, &d2_); |
| EXPECT_EQ(1, cras_bt_io_remove_called); |
| EXPECT_EQ(1, cras_iodev_set_node_plugged_called); |
| EXPECT_EQ(0, cras_iodev_set_node_plugged_value); |
| |
| /* Test A2DP disconnection will cause bt_io destroy. */ |
| cras_bt_io_try_remove_ret = 0; |
| cras_bt_device_rm_iodev(device, &d1_); |
| EXPECT_EQ(1, cras_bt_io_remove_called); |
| EXPECT_EQ(1, cras_bt_io_destroy_called); |
| EXPECT_EQ(0, cras_bt_device_get_active_profile(device)); |
| EXPECT_EQ(2, cras_iodev_set_node_plugged_called); |
| EXPECT_EQ(0, cras_iodev_set_node_plugged_value); |
| cras_bt_device_remove(device); |
| } |
| |
| TEST_F(BtDeviceTestSuite, SwitchProfile) { |
| struct cras_bt_device* device; |
| |
| ResetStubData(); |
| device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); |
| cras_bt_io_create_profile_ret = &bt_iodev1; |
| cras_bt_device_append_iodev(device, &d1_, CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE); |
| cras_bt_io_create_profile_ret = &bt_iodev2; |
| cras_bt_device_append_iodev(device, &d3_, |
| CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); |
| |
| cras_bt_device_start_monitor(); |
| cras_bt_device_switch_profile_enable_dev(device, &bt_iodev1); |
| |
| /* Two bt iodevs were all active. */ |
| cras_main_message_add_handler_callback( |
| cras_main_message_send_msg, cras_main_message_add_handler_callback_data); |
| |
| /* One bt iodev was active, the other was not. */ |
| cras_bt_device_switch_profile_enable_dev(device, &bt_iodev2); |
| cras_main_message_add_handler_callback( |
| cras_main_message_send_msg, cras_main_message_add_handler_callback_data); |
| |
| /* Output bt iodev wasn't active, close the active input iodev. */ |
| cras_bt_device_switch_profile(device, &bt_iodev2); |
| cras_main_message_add_handler_callback( |
| cras_main_message_send_msg, cras_main_message_add_handler_callback_data); |
| cras_bt_device_remove(device); |
| } |
| |
| TEST_F(BtDeviceTestSuite, SetDeviceConnectedA2dpOnly) { |
| struct cras_bt_device* device; |
| struct MockDBusMessage *msg_root, *cur; |
| ResetStubData(); |
| |
| device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); |
| EXPECT_NE((void*)NULL, device); |
| |
| cras_bt_device_set_supported_profiles(device, |
| CRAS_BT_DEVICE_PROFILE_A2DP_SINK); |
| |
| cur = msg_root = NewMockDBusConnectedMessage(1); |
| cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); |
| |
| /* Schedule another timer, if A2DP not yet configured. */ |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| |
| /* ConnectProfile must not be called, since this is A2DP only case. */ |
| EXPECT_EQ(0, dbus_message_new_method_call_called); |
| |
| cras_bt_device_a2dp_configured(device); |
| |
| /* Prepate the iodev created by cras_a2dp_start. */ |
| cras_bt_io_create_profile_ret = &bt_iodev1; |
| cras_bt_device_append_iodev(device, &d1_, CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE); |
| |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| EXPECT_EQ(1, cras_hfp_ag_remove_conflict_called); |
| EXPECT_EQ(1, cras_a2dp_start_called); |
| EXPECT_EQ(1, cras_iodev_set_node_plugged_called); |
| EXPECT_EQ(1, cras_iodev_set_node_plugged_value); |
| |
| cras_bt_device_remove(device); |
| FreeMockDBusMessage(msg_root); |
| } |
| |
| TEST_F(BtDeviceTestSuite, SetDeviceConnectedHfpHspOnly) { |
| struct cras_bt_device* device; |
| struct MockDBusMessage *msg_root, *cur; |
| |
| ResetStubData(); |
| |
| device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); |
| EXPECT_NE((void*)NULL, device); |
| |
| cras_bt_device_set_supported_profiles( |
| device, CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | |
| CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); |
| |
| cur = msg_root = NewMockDBusConnectedMessage(1); |
| cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); |
| |
| /* Schedule another timer, if HFP AG not yet intialized. */ |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| |
| /* ConnectProfile must not be called, since this is HFP only case. */ |
| EXPECT_EQ(0, dbus_message_new_method_call_called); |
| |
| cras_bt_device_audio_gateway_initialized(device); |
| |
| /* Prepate the iodev created by ag initialization. */ |
| cras_bt_io_create_profile_ret = &bt_iodev2; |
| cras_bt_device_append_iodev(device, &d3_, |
| CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); |
| |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| EXPECT_EQ(1, cras_hfp_ag_remove_conflict_called); |
| EXPECT_EQ(1, cras_hfp_ag_start_called); |
| EXPECT_EQ(1, cras_iodev_set_node_plugged_called); |
| EXPECT_EQ(1, cras_iodev_set_node_plugged_value); |
| |
| cras_bt_device_remove(device); |
| FreeMockDBusMessage(msg_root); |
| } |
| |
| TEST_F(BtDeviceTestSuite, SetDeviceConnectedA2dpHfpHsp) { |
| struct cras_bt_device* device; |
| struct MockDBusMessage *msg_root, *cur; |
| |
| ResetStubData(); |
| |
| device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); |
| EXPECT_NE((void*)NULL, device); |
| |
| cras_bt_device_set_supported_profiles( |
| device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK | |
| CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | |
| CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); |
| |
| cur = msg_root = NewMockDBusConnectedMessage(1); |
| cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); |
| |
| /* Schedule another timer, if not HFP nor A2DP is ready. */ |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| |
| /* ConnectProfile must not be called, since the first profile connection |
| * should be initiated by Bluez. |
| */ |
| EXPECT_EQ(0, dbus_message_new_method_call_called); |
| |
| cras_bt_device_audio_gateway_initialized(device); |
| |
| /* Schedule another timer, because A2DP is not ready. */ |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(3, cras_tm_create_timer_called); |
| EXPECT_EQ(0, cras_hfp_ag_start_called); |
| |
| /* ConnectProfile should be called to connect A2DP, since HFP is connected */ |
| EXPECT_EQ(1, dbus_message_new_method_call_called); |
| EXPECT_STREQ("ConnectProfile", dbus_message_new_method_call_method); |
| |
| cras_bt_device_a2dp_configured(device); |
| |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(3, cras_tm_create_timer_called); |
| EXPECT_EQ(1, cras_hfp_ag_remove_conflict_called); |
| EXPECT_EQ(1, cras_a2dp_start_called); |
| EXPECT_EQ(1, cras_hfp_ag_start_called); |
| |
| cras_bt_device_remove(device); |
| FreeMockDBusMessage(msg_root); |
| } |
| |
| TEST_F(BtDeviceTestSuite, DevConnectedConflictCheck) { |
| struct cras_bt_device* device; |
| struct MockDBusMessage *msg_root, *cur; |
| |
| ResetStubData(); |
| |
| device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); |
| EXPECT_NE((void*)NULL, device); |
| |
| cras_bt_device_set_supported_profiles( |
| device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK | |
| CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | |
| CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); |
| |
| cur = msg_root = NewMockDBusConnectedMessage(1); |
| cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); |
| cras_bt_device_audio_gateway_initialized(device); |
| cras_bt_device_a2dp_configured(device); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| |
| /* Fake that a different device already connected with A2DP */ |
| cras_a2dp_connected_device_ret = |
| reinterpret_cast<struct cras_bt_device*>(0x99); |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| |
| /* Expect check conflict in HFP AG and A2DP. */ |
| EXPECT_EQ(1, cras_hfp_ag_remove_conflict_called); |
| EXPECT_EQ(1, cras_a2dp_suspend_connected_device_called); |
| EXPECT_EQ(cras_a2dp_suspend_connected_device_dev, |
| cras_a2dp_connected_device_ret); |
| |
| EXPECT_EQ(1, cras_a2dp_start_called); |
| EXPECT_EQ(1, cras_hfp_ag_start_called); |
| |
| cras_bt_device_remove(device); |
| FreeMockDBusMessage(msg_root); |
| } |
| |
| TEST_F(BtDeviceTestSuite, A2dpDropped) { |
| struct cras_bt_device* device; |
| struct MockDBusMessage *msg_root, *cur; |
| |
| ResetStubData(); |
| |
| device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); |
| EXPECT_NE((void*)NULL, device); |
| |
| cras_bt_device_set_supported_profiles( |
| device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK | |
| CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | |
| CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); |
| |
| cur = msg_root = NewMockDBusConnectedMessage(1); |
| cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); |
| cras_bt_device_audio_gateway_initialized(device); |
| cras_bt_device_a2dp_configured(device); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); |
| |
| cras_bt_device_notify_profile_dropped(device, |
| CRAS_BT_DEVICE_PROFILE_A2DP_SINK); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| |
| /* Expect suspend timer is scheduled. */ |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(1, cras_a2dp_suspend_connected_device_called); |
| EXPECT_EQ(1, cras_hfp_ag_suspend_connected_device_called); |
| EXPECT_EQ(1, dbus_message_new_method_call_called); |
| EXPECT_STREQ("Disconnect", dbus_message_new_method_call_method); |
| |
| cras_bt_device_remove(device); |
| FreeMockDBusMessage(msg_root); |
| } |
| |
| TEST_F(BtDeviceTestSuite, DevConnectDisconnectBackToBack) { |
| struct cras_bt_device* device; |
| struct MockDBusMessage *msg_root, *cur; |
| |
| ResetStubData(); |
| |
| device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); |
| EXPECT_NE((void*)NULL, device); |
| |
| cras_bt_device_set_supported_profiles( |
| device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK | |
| CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | |
| CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); |
| |
| cur = msg_root = NewMockDBusConnectedMessage(1); |
| cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); |
| FreeMockDBusMessage(msg_root); |
| |
| cras_bt_device_a2dp_configured(device); |
| cras_bt_device_audio_gateway_initialized(device); |
| |
| /* Expect suspend timer is scheduled. */ |
| cras_tm_create_timer_ret = reinterpret_cast<struct cras_timer*>(0x101); |
| cras_bt_device_notify_profile_dropped(device, |
| CRAS_BT_DEVICE_PROFILE_A2DP_SINK); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| /* Another profile drop won't schedule another timer because one is |
| * already armed. */ |
| EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); |
| cras_bt_device_notify_profile_dropped(device, |
| CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| |
| cur = msg_root = NewMockDBusConnectedMessage(0); |
| cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); |
| |
| /* When BlueZ reports headset disconnection, cancel the pending timer. */ |
| EXPECT_EQ(cras_tm_cancel_timer_called, 1); |
| EXPECT_EQ(cras_tm_cancel_timer_arg, (void*)0x101); |
| FreeMockDBusMessage(msg_root); |
| |
| /* Headset connects again. */ |
| cur = msg_root = NewMockDBusConnectedMessage(1); |
| cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); |
| EXPECT_EQ(3, cras_tm_create_timer_called); |
| EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); |
| FreeMockDBusMessage(msg_root); |
| |
| /* Headset disconnects, later profile drop events shouldn't trigger |
| * suspend timer because headset is already in disconnected stats. |
| */ |
| cur = msg_root = NewMockDBusConnectedMessage(0); |
| cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); |
| FreeMockDBusMessage(msg_root); |
| |
| cras_tm_create_timer_called = 0; |
| cras_bt_device_notify_profile_dropped(device, |
| CRAS_BT_DEVICE_PROFILE_A2DP_SINK); |
| EXPECT_EQ(0, cras_tm_create_timer_called); |
| cras_bt_device_notify_profile_dropped(device, |
| CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); |
| EXPECT_EQ(0, cras_tm_create_timer_called); |
| |
| cras_bt_device_remove(device); |
| } |
| |
| TEST_F(BtDeviceTestSuite, ConnectionWatchTimeout) { |
| struct cras_bt_device* device; |
| struct MockDBusMessage *msg_root, *cur; |
| |
| ResetStubData(); |
| |
| device = cras_bt_device_create(NULL, FAKE_OBJ_PATH); |
| EXPECT_NE((void*)NULL, device); |
| |
| cras_bt_device_set_supported_profiles( |
| device, CRAS_BT_DEVICE_PROFILE_A2DP_SINK | |
| CRAS_BT_DEVICE_PROFILE_HSP_HEADSET | |
| CRAS_BT_DEVICE_PROFILE_HFP_HANDSFREE); |
| |
| cur = msg_root = NewMockDBusConnectedMessage(1); |
| cras_bt_device_update_properties(device, (DBusMessageIter*)&cur, NULL); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| EXPECT_NE((void*)NULL, cras_tm_create_timer_cb); |
| |
| cras_bt_device_a2dp_configured(device); |
| |
| for (unsigned int i = 0; i < CONN_WATCH_MAX_RETRIES; i++) { |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(i + 2, cras_tm_create_timer_called); |
| EXPECT_EQ(0, cras_a2dp_start_called); |
| EXPECT_EQ(0, cras_hfp_ag_start_called); |
| EXPECT_EQ(0, cras_hfp_ag_remove_conflict_called); |
| } |
| |
| dbus_message_new_method_call_called = 0; |
| |
| /* Expect suspend timer is scheduled. */ |
| cras_tm_create_timer_cb(NULL, cras_tm_create_timer_cb_data); |
| EXPECT_EQ(1, cras_a2dp_suspend_connected_device_called); |
| EXPECT_EQ(1, cras_hfp_ag_suspend_connected_device_called); |
| EXPECT_EQ(1, dbus_message_new_method_call_called); |
| EXPECT_STREQ("Disconnect", dbus_message_new_method_call_method); |
| |
| cras_bt_device_remove(device); |
| FreeMockDBusMessage(msg_root); |
| } |
| |
| /* Stubs */ |
| extern "C" { |
| |
| struct cras_bt_event_log* btlog; |
| |
| /* From bt_io */ |
| struct cras_iodev* cras_bt_io_create(struct cras_bt_device* device, |
| struct cras_iodev* dev, |
| enum cras_bt_device_profile profile) { |
| cras_bt_io_create_called++; |
| cras_bt_io_create_profile_val = profile; |
| return cras_bt_io_create_profile_ret; |
| } |
| void cras_bt_io_destroy(struct cras_iodev* bt_iodev) { |
| cras_bt_io_destroy_called++; |
| } |
| struct cras_ionode* cras_bt_io_get_profile( |
| struct cras_iodev* bt_iodev, |
| enum cras_bt_device_profile profile) { |
| return cras_bt_io_get_profile_ret; |
| } |
| int cras_bt_io_append(struct cras_iodev* bt_iodev, |
| struct cras_iodev* dev, |
| enum cras_bt_device_profile profile) { |
| cras_bt_io_append_called++; |
| cras_bt_io_append_profile_val = profile; |
| cras_bt_io_append_btio_val = bt_iodev; |
| return 0; |
| } |
| int cras_bt_io_on_profile(struct cras_iodev* bt_iodev, |
| enum cras_bt_device_profile profile) { |
| return 0; |
| } |
| unsigned int cras_bt_io_try_remove(struct cras_iodev* bt_iodev, |
| struct cras_iodev* dev) { |
| return cras_bt_io_try_remove_ret; |
| } |
| int cras_bt_io_remove(struct cras_iodev* bt_iodev, struct cras_iodev* dev) { |
| cras_bt_io_remove_called++; |
| return 0; |
| } |
| |
| /* From bt_adapter */ |
| struct cras_bt_adapter* cras_bt_adapter_get(const char* object_path) { |
| return NULL; |
| } |
| const char* cras_bt_adapter_address(const struct cras_bt_adapter* adapter) { |
| return NULL; |
| } |
| |
| int cras_bt_adapter_on_usb(struct cras_bt_adapter* adapter) { |
| return 1; |
| } |
| |
| /* From bt_profile */ |
| void cras_bt_profile_on_device_disconnected(struct cras_bt_device* device) {} |
| |
| /* From hfp_ag_profile */ |
| struct hfp_slc_handle* cras_hfp_ag_get_slc(struct cras_bt_device* device) { |
| return NULL; |
| } |
| |
| void cras_hfp_ag_suspend_connected_device(struct cras_bt_device* device) { |
| cras_hfp_ag_suspend_connected_device_called++; |
| } |
| |
| void cras_a2dp_suspend_connected_device(struct cras_bt_device* device) { |
| cras_a2dp_suspend_connected_device_called++; |
| cras_a2dp_suspend_connected_device_dev = device; |
| } |
| |
| void cras_a2dp_start(struct cras_bt_device* device) { |
| cras_a2dp_start_called++; |
| } |
| |
| struct cras_bt_device* cras_a2dp_connected_device() { |
| return cras_a2dp_connected_device_ret; |
| } |
| |
| int cras_hfp_ag_remove_conflict(struct cras_bt_device* device) { |
| cras_hfp_ag_remove_conflict_called++; |
| return 0; |
| } |
| |
| int cras_hfp_ag_start(struct cras_bt_device* device) { |
| cras_hfp_ag_start_called++; |
| return 0; |
| } |
| |
| void cras_hfp_ag_suspend() {} |
| |
| /* From hfp_slc */ |
| int hfp_event_speaker_gain(struct hfp_slc_handle* handle, int gain) { |
| return 0; |
| } |
| |
| /* From iodev_list */ |
| |
| int cras_iodev_open(struct cras_iodev* dev, |
| unsigned int cb_level, |
| const struct cras_audio_format* fmt) { |
| return 0; |
| } |
| |
| int cras_iodev_close(struct cras_iodev* dev) { |
| return 0; |
| } |
| |
| void cras_iodev_set_node_plugged(struct cras_ionode* ionode, int plugged) { |
| cras_iodev_set_node_plugged_called++; |
| cras_iodev_set_node_plugged_value = plugged; |
| } |
| |
| int cras_iodev_list_dev_is_enabled(const struct cras_iodev* dev) { |
| return 0; |
| } |
| |
| void cras_iodev_list_suspend_dev(struct cras_iodev* dev) {} |
| |
| void cras_iodev_list_resume_dev(struct cras_iodev* dev) {} |
| |
| void cras_iodev_list_notify_node_volume(struct cras_ionode* node) {} |
| |
| int cras_main_message_send(struct cras_main_message* msg) { |
| // cras_main_message is a local variable from caller, we should allocate |
| // memory from heap and copy its data |
| if (cras_main_message_send_msg) |
| free(cras_main_message_send_msg); |
| cras_main_message_send_msg = |
| (struct cras_main_message*)calloc(1, msg->length); |
| memcpy((void*)cras_main_message_send_msg, (void*)msg, msg->length); |
| return 0; |
| } |
| |
| int cras_main_message_add_handler(enum CRAS_MAIN_MESSAGE_TYPE type, |
| cras_message_callback callback, |
| void* callback_data) { |
| cras_main_message_add_handler_callback = callback; |
| cras_main_message_add_handler_callback_data = callback_data; |
| return 0; |
| } |
| |
| /* From cras_system_state */ |
| struct cras_tm* cras_system_state_get_tm() { |
| return NULL; |
| } |
| |
| /* From cras_tm */ |
| struct cras_timer* cras_tm_create_timer(struct cras_tm* tm, |
| unsigned int ms, |
| void (*cb)(struct cras_timer* t, |
| void* data), |
| void* cb_data) { |
| cras_tm_create_timer_called++; |
| cras_tm_create_timer_cb = cb; |
| cras_tm_create_timer_cb_data = cb_data; |
| return cras_tm_create_timer_ret; |
| } |
| |
| void cras_tm_cancel_timer(struct cras_tm* tm, struct cras_timer* t) { |
| cras_tm_cancel_timer_called++; |
| cras_tm_cancel_timer_arg = t; |
| } |
| |
| DBusMessage* dbus_message_new_method_call(const char* destination, |
| const char* path, |
| const char* iface, |
| const char* method) { |
| dbus_message_new_method_call_called++; |
| dbus_message_new_method_call_method = method; |
| return reinterpret_cast<DBusMessage*>(0x456); |
| } |
| |
| void dbus_message_unref(DBusMessage* message) {} |
| |
| dbus_bool_t dbus_message_append_args(DBusMessage* message, |
| int first_arg_type, |
| ...) { |
| return true; |
| } |
| |
| dbus_bool_t dbus_connection_send_with_reply(DBusConnection* connection, |
| DBusMessage* message, |
| DBusPendingCall** pending_return, |
| int timeout_milliseconds) { |
| return true; |
| } |
| |
| dbus_bool_t dbus_pending_call_set_notify(DBusPendingCall* pending, |
| DBusPendingCallNotifyFunction function, |
| void* user_data, |
| DBusFreeFunction free_user_data) { |
| return true; |
| } |
| |
| void dbus_message_iter_recurse(DBusMessageIter* iter, DBusMessageIter* sub) { |
| MockDBusMessage* msg = *(MockDBusMessage**)iter; |
| MockDBusMessage** cur = (MockDBusMessage**)sub; |
| *cur = msg->recurse; |
| } |
| |
| dbus_bool_t dbus_message_iter_next(DBusMessageIter* iter) { |
| MockDBusMessage** cur = (MockDBusMessage**)iter; |
| MockDBusMessage* msg = *cur; |
| *cur = msg->next; |
| return true; |
| } |
| |
| int dbus_message_iter_get_arg_type(DBusMessageIter* iter) { |
| MockDBusMessage* msg; |
| |
| if (iter == NULL) |
| return DBUS_TYPE_INVALID; |
| |
| msg = *(MockDBusMessage**)iter; |
| if (msg == NULL) |
| return DBUS_TYPE_INVALID; |
| |
| return msg->type; |
| } |
| |
| void dbus_message_iter_get_basic(DBusMessageIter* iter, void* value) { |
| MockDBusMessage* msg = *(MockDBusMessage**)iter; |
| switch (msg->type) { |
| case DBUS_TYPE_BOOLEAN: |
| memcpy(value, &msg->value, sizeof(int)); |
| break; |
| case DBUS_TYPE_STRING: |
| memcpy(value, &msg->value, sizeof(char*)); |
| break; |
| } |
| } |
| |
| } // extern "C" |
| } // namespace |
| |
| int main(int argc, char** argv) { |
| ::testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |