// 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 extern "C" { // To test static functions. #include "cras_bt_io.c" #include "utlist.h" } static struct cras_bt_device* fake_device = reinterpret_cast(0x123); static unsigned int cras_iodev_add_node_called; static unsigned int cras_iodev_rm_node_called; static unsigned int cras_iodev_free_format_called; static unsigned int cras_iodev_free_resources_called; static unsigned int cras_iodev_set_active_node_called; static unsigned int cras_iodev_list_add_output_called; static unsigned int cras_iodev_list_rm_output_called; static unsigned int cras_iodev_list_add_input_called; static unsigned int cras_iodev_list_rm_input_called; static unsigned int cras_bt_device_set_active_profile_called; static unsigned int cras_bt_device_set_active_profile_val; static int cras_bt_device_get_active_profile_ret; static int cras_bt_device_switch_profile_enable_dev_called; static int cras_bt_device_switch_profile_called; static int cras_bt_device_can_switch_to_a2dp_ret; static int cras_bt_device_has_a2dp_ret; static int is_utf8_string_ret_value; void ResetStubData() { cras_iodev_add_node_called = 0; cras_iodev_rm_node_called = 0; cras_iodev_free_format_called = 0; cras_iodev_free_resources_called = 0; cras_iodev_set_active_node_called = 0; cras_iodev_list_add_output_called = 0; cras_iodev_list_rm_output_called = 0; cras_iodev_list_add_input_called = 0; cras_iodev_list_rm_input_called = 0; cras_bt_device_set_active_profile_called = 0; cras_bt_device_set_active_profile_val = 0; cras_bt_device_get_active_profile_ret = 0; cras_bt_device_switch_profile_enable_dev_called = 0; cras_bt_device_switch_profile_called = 0; cras_bt_device_can_switch_to_a2dp_ret = 0; cras_bt_device_has_a2dp_ret = 0; is_utf8_string_ret_value = 1; } namespace { class BtIoBasicSuite : public testing::Test { protected: virtual void SetUp() { ResetStubData(); SetUpIodev(&iodev_, CRAS_STREAM_OUTPUT); SetUpIodev(&iodev2_, CRAS_STREAM_OUTPUT); iodev_.active_node = &node_; iodev2_.active_node = &node2_; update_supported_formats_called_ = 0; frames_queued_called_ = 0; delay_frames_called_ = 0; get_buffer_called_ = 0; put_buffer_called_ = 0; configure_dev_called_ = 0; close_dev_called_ = 0; } virtual void TearDown() {} static void SetUpIodev(struct cras_iodev* d, enum CRAS_STREAM_DIRECTION dir) { d->direction = dir; d->update_supported_formats = update_supported_formats; d->frames_queued = frames_queued; d->delay_frames = delay_frames; d->get_buffer = get_buffer; d->put_buffer = put_buffer; d->configure_dev = configure_dev; d->close_dev = close_dev; d->supported_rates = NULL; d->supported_channel_counts = NULL; d->supported_formats = NULL; } // Stub functions for the iodev structure. static int update_supported_formats(struct cras_iodev* iodev) { free(iodev->supported_rates); free(iodev->supported_channel_counts); free(iodev->supported_formats); iodev->supported_rates = (size_t*)calloc(2, sizeof(*iodev->supported_rates)); iodev->supported_rates[0] = 48000; iodev->supported_rates[1] = 0; iodev->supported_channel_counts = (size_t*)calloc(2, sizeof(*iodev->supported_channel_counts)); iodev->supported_channel_counts[0] = 2; iodev->supported_channel_counts[1] = 0; iodev->supported_formats = (snd_pcm_format_t*)calloc(2, sizeof(*iodev->supported_formats)); iodev->supported_formats[0] = SND_PCM_FORMAT_S16_LE; iodev->supported_formats[1] = (snd_pcm_format_t)0; update_supported_formats_called_++; return 0; } static int frames_queued(const cras_iodev* iodev, struct timespec* tstamp) { frames_queued_called_++; return 0; } static int delay_frames(const cras_iodev* iodev) { delay_frames_called_++; return 0; } static int get_buffer(cras_iodev* iodev, struct cras_audio_area** area, unsigned int* num) { get_buffer_called_++; return 0; } static int put_buffer(cras_iodev* iodev, unsigned int num) { put_buffer_called_++; return 0; } static int configure_dev(cras_iodev* iodev) { configure_dev_called_++; return 0; } static int close_dev(cras_iodev* iodev) { free(iodev->format); iodev->format = NULL; close_dev_called_++; return 0; } static struct cras_iodev* bt_iodev; static struct cras_iodev iodev_; static struct cras_iodev iodev2_; static struct cras_ionode node_; static struct cras_ionode node2_; static unsigned int update_supported_formats_called_; static unsigned int frames_queued_called_; static unsigned int delay_frames_called_; static unsigned int get_buffer_called_; static unsigned int put_buffer_called_; static unsigned int configure_dev_called_; static unsigned int close_dev_called_; }; struct cras_iodev* BtIoBasicSuite::bt_iodev; struct cras_iodev BtIoBasicSuite::iodev_; struct cras_iodev BtIoBasicSuite::iodev2_; struct cras_ionode BtIoBasicSuite::node_; struct cras_ionode BtIoBasicSuite::node2_; unsigned int BtIoBasicSuite::update_supported_formats_called_; unsigned int BtIoBasicSuite::frames_queued_called_; unsigned int BtIoBasicSuite::delay_frames_called_; unsigned int BtIoBasicSuite::get_buffer_called_; unsigned int BtIoBasicSuite::put_buffer_called_; unsigned int BtIoBasicSuite::configure_dev_called_; unsigned int BtIoBasicSuite::close_dev_called_; TEST_F(BtIoBasicSuite, CreateBtIo) { struct cras_audio_area* fake_area; struct cras_audio_format fake_fmt; struct timespec tstamp; unsigned fr; bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE); EXPECT_NE((void*)NULL, bt_iodev); EXPECT_EQ(&iodev_, active_profile_dev(bt_iodev)); EXPECT_EQ(1, cras_iodev_list_add_output_called); bt_iodev->open_dev(bt_iodev); bt_iodev->format = &fake_fmt; bt_iodev->update_supported_formats(bt_iodev); EXPECT_EQ(1, update_supported_formats_called_); bt_iodev->state = CRAS_IODEV_STATE_OPEN; bt_iodev->configure_dev(bt_iodev); EXPECT_EQ(1, configure_dev_called_); bt_iodev->frames_queued(bt_iodev, &tstamp); EXPECT_EQ(1, frames_queued_called_); bt_iodev->get_buffer(bt_iodev, &fake_area, &fr); EXPECT_EQ(1, get_buffer_called_); bt_iodev->put_buffer(bt_iodev, fr); EXPECT_EQ(1, put_buffer_called_); bt_iodev->close_dev(bt_iodev); EXPECT_EQ(1, close_dev_called_); EXPECT_EQ(1, cras_iodev_free_format_called); cras_bt_io_destroy(bt_iodev); EXPECT_EQ(1, cras_iodev_free_resources_called); EXPECT_EQ(1, cras_iodev_list_rm_output_called); free(iodev_.supported_rates); free(iodev_.supported_channel_counts); free(iodev_.supported_formats); } TEST_F(BtIoBasicSuite, SwitchProfileOnOpenDevForInputDev) { ResetStubData(); iodev_.direction = CRAS_STREAM_INPUT; bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); cras_bt_device_get_active_profile_ret = CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE; bt_iodev->open_dev(bt_iodev); EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_HSP_AUDIOGATEWAY | CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY, cras_bt_device_set_active_profile_val); EXPECT_EQ(1, cras_bt_device_switch_profile_enable_dev_called); cras_bt_io_destroy(bt_iodev); } TEST_F(BtIoBasicSuite, NoSwitchProfileOnOpenDevForInputDevAlreadyOnHfp) { ResetStubData(); iodev_.direction = CRAS_STREAM_INPUT; bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); /* No need to switch profile if already on HFP. */ cras_bt_device_get_active_profile_ret = CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY; bt_iodev->open_dev(bt_iodev); EXPECT_EQ(0, cras_bt_device_switch_profile_enable_dev_called); cras_bt_io_destroy(bt_iodev); } TEST_F(BtIoBasicSuite, SwitchProfileOnCloseInputDev) { ResetStubData(); iodev_.direction = CRAS_STREAM_INPUT; bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); bt_iodev->state = CRAS_IODEV_STATE_OPEN; cras_bt_device_get_active_profile_ret = CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY | CRAS_BT_DEVICE_PROFILE_HSP_AUDIOGATEWAY; cras_bt_device_has_a2dp_ret = 1; bt_iodev->close_dev(bt_iodev); EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE, cras_bt_device_set_active_profile_val); EXPECT_EQ(1, cras_bt_device_switch_profile_called); cras_bt_io_destroy(bt_iodev); } TEST_F(BtIoBasicSuite, NoSwitchProfileOnCloseInputDevNoSupportA2dp) { ResetStubData(); iodev_.direction = CRAS_STREAM_INPUT; bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); bt_iodev->state = CRAS_IODEV_STATE_OPEN; cras_bt_device_get_active_profile_ret = CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY | CRAS_BT_DEVICE_PROFILE_HSP_AUDIOGATEWAY; cras_bt_device_has_a2dp_ret = 0; bt_iodev->close_dev(bt_iodev); EXPECT_EQ(0, cras_bt_device_switch_profile_called); cras_bt_io_destroy(bt_iodev); } TEST_F(BtIoBasicSuite, NoSwitchProfileOnCloseInputDevInCloseState) { ResetStubData(); iodev_.direction = CRAS_STREAM_INPUT; bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); bt_iodev->state = CRAS_IODEV_STATE_CLOSE; cras_bt_device_get_active_profile_ret = CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY | CRAS_BT_DEVICE_PROFILE_HSP_AUDIOGATEWAY; cras_bt_device_has_a2dp_ret = 1; bt_iodev->close_dev(bt_iodev); EXPECT_EQ(0, cras_bt_device_switch_profile_called); cras_bt_io_destroy(bt_iodev); } TEST_F(BtIoBasicSuite, SwitchProfileOnAppendA2dpDev) { ResetStubData(); bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); cras_bt_device_can_switch_to_a2dp_ret = 1; cras_bt_io_append(bt_iodev, &iodev2_, CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE); EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE, cras_bt_device_set_active_profile_val); EXPECT_EQ(0, cras_bt_device_switch_profile_enable_dev_called); EXPECT_EQ(1, cras_bt_device_switch_profile_called); cras_bt_io_destroy(bt_iodev); } TEST_F(BtIoBasicSuite, NoSwitchProfileOnAppendHfpDev) { ResetStubData(); bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE); cras_bt_device_can_switch_to_a2dp_ret = 1; cras_bt_io_append(bt_iodev, &iodev2_, CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); EXPECT_EQ(0, cras_bt_device_switch_profile_enable_dev_called); cras_bt_io_destroy(bt_iodev); } TEST_F(BtIoBasicSuite, CreateSetDeviceActiveProfileToA2DP) { ResetStubData(); cras_bt_device_get_active_profile_ret = CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY; cras_bt_device_can_switch_to_a2dp_ret = 1; bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE); EXPECT_EQ(1, cras_bt_device_set_active_profile_called); EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE, cras_bt_device_set_active_profile_val); cras_bt_io_destroy(bt_iodev); } TEST_F(BtIoBasicSuite, CreateNoSetDeviceActiveProfileToA2DP) { ResetStubData(); cras_bt_device_get_active_profile_ret = CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY; cras_bt_device_can_switch_to_a2dp_ret = 0; bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE); EXPECT_EQ(0, cras_bt_device_set_active_profile_called); cras_bt_io_destroy(bt_iodev); } TEST_F(BtIoBasicSuite, CreateSetDeviceActiveProfileToHFP) { ResetStubData(); cras_bt_device_get_active_profile_ret = 0; bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY); EXPECT_EQ(CRAS_BT_DEVICE_PROFILE_HFP_AUDIOGATEWAY | CRAS_BT_DEVICE_PROFILE_HSP_AUDIOGATEWAY, cras_bt_device_set_active_profile_val); cras_bt_io_destroy(bt_iodev); } TEST_F(BtIoBasicSuite, CreateDeviceWithInvalidUTF8Name) { ResetStubData(); strcpy(iodev_.info.name, "Something BT"); iodev_.info.name[0] = 0xfe; is_utf8_string_ret_value = 0; bt_iodev = cras_bt_io_create(fake_device, &iodev_, CRAS_BT_DEVICE_PROFILE_A2DP_SOURCE); ASSERT_STREQ("BLUETOOTH", bt_iodev->active_node->name); cras_bt_io_destroy(bt_iodev); } } // namespace int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } extern "C" { // Cras iodev void cras_iodev_add_node(struct cras_iodev* iodev, struct cras_ionode* node) { cras_iodev_add_node_called++; DL_APPEND(iodev->nodes, node); } void cras_iodev_rm_node(struct cras_iodev* iodev, struct cras_ionode* node) { cras_iodev_rm_node_called++; DL_DELETE(iodev->nodes, node); } void cras_iodev_free_format(struct cras_iodev* iodev) { cras_iodev_free_format_called++; } void cras_iodev_set_active_node(struct cras_iodev* iodev, struct cras_ionode* node) { cras_iodev_set_active_node_called++; iodev->active_node = node; } int cras_iodev_set_node_attr(struct cras_ionode* ionode, enum ionode_attr attr, int value) { return 0; } void cras_iodev_free_resources(struct cras_iodev* iodev) { cras_iodev_free_resources_called++; } // From iodev list. int cras_iodev_list_add_output(struct cras_iodev* output) { cras_iodev_list_add_output_called++; return 0; } int cras_iodev_list_rm_output(struct cras_iodev* dev) { cras_iodev_list_rm_output_called++; return 0; } int cras_iodev_list_add_input(struct cras_iodev* output) { cras_iodev_list_add_input_called++; return 0; } int cras_iodev_list_rm_input(struct cras_iodev* dev) { cras_iodev_list_rm_input_called++; return 0; } // From bt device unsigned int cras_bt_device_get_active_profile( const struct cras_bt_device* device) { return cras_bt_device_get_active_profile_ret; } void cras_bt_device_set_active_profile(struct cras_bt_device* device, unsigned int profile) { cras_bt_device_set_active_profile_called++; cras_bt_device_set_active_profile_val = profile; } int cras_bt_device_has_a2dp(struct cras_bt_device* device) { return cras_bt_device_has_a2dp_ret; } int cras_bt_device_can_switch_to_a2dp(struct cras_bt_device* device) { return cras_bt_device_can_switch_to_a2dp_ret; } int cras_bt_device_switch_profile(struct cras_bt_device* device, struct cras_iodev* bt_iodev) { cras_bt_device_switch_profile_called++; return 0; } int cras_bt_device_switch_profile_enable_dev(struct cras_bt_device* device, struct cras_iodev* bt_iodev) { cras_bt_device_switch_profile_enable_dev_called++; return 0; } const char* cras_bt_device_object_path(const struct cras_bt_device* device) { return "/fake/object/path"; } int cras_bt_device_get_stable_id(const struct cras_bt_device* device) { return 123; } int cras_bt_device_get_use_hardware_volume(struct cras_bt_device* device) { return 1; } int is_utf8_string(const char* string) { return is_utf8_string_ret_value; } int cras_iodev_default_no_stream_playback(struct cras_iodev* odev, int enable) { return 0; } int cras_iodev_frames_queued(struct cras_iodev* iodev, struct timespec* hw_tstamp) { return 0; } unsigned int cras_iodev_default_frames_to_play_in_sleep( struct cras_iodev* odev, unsigned int* hw_level, struct timespec* hw_tstamp) { return 0; } int hfp_iodev_is_hsp(struct cras_iodev* iodev) { return 0; } } // extern "C"