| // Copyright (c) 2012 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> |
| #include <stdio.h> |
| |
| #include <algorithm> |
| #include <map> |
| |
| extern "C" { |
| #include "audio_thread.h" |
| #include "cras_iodev.h" |
| #include "cras_iodev_list.h" |
| #include "cras_main_thread_log.h" |
| #include "cras_observer_ops.h" |
| #include "cras_ramp.h" |
| #include "cras_rstream.h" |
| #include "cras_system_state.h" |
| #include "cras_tm.h" |
| #include "stream_list.h" |
| #include "utlist.h" |
| } |
| |
| namespace { |
| |
| struct cras_server_state server_state_stub; |
| struct cras_server_state* server_state_update_begin_return; |
| int system_get_mute_return; |
| |
| /* Data for stubs. */ |
| static struct cras_observer_ops* observer_ops; |
| static int add_stream_called; |
| static int rm_stream_called; |
| static unsigned int set_node_plugged_called; |
| static cras_iodev* audio_thread_remove_streams_active_dev; |
| static cras_iodev* audio_thread_set_active_dev_val; |
| static int audio_thread_set_active_dev_called; |
| static cras_iodev* audio_thread_add_open_dev_dev; |
| static int audio_thread_add_open_dev_called; |
| static int audio_thread_rm_open_dev_called; |
| static int audio_thread_is_dev_open_ret; |
| static struct audio_thread thread; |
| static struct cras_iodev loopback_input; |
| static int cras_iodev_close_called; |
| static struct cras_iodev* cras_iodev_close_dev; |
| static struct cras_iodev mock_hotword_iodev; |
| static struct cras_iodev mock_empty_iodev[2]; |
| static stream_callback* stream_add_cb; |
| static stream_callback* stream_rm_cb; |
| static struct cras_rstream* stream_list_get_ret; |
| static int server_stream_create_called; |
| static int server_stream_destroy_called; |
| static int audio_thread_drain_stream_return; |
| static int audio_thread_drain_stream_called; |
| static int cras_tm_create_timer_called; |
| static int cras_tm_cancel_timer_called; |
| static void (*cras_tm_timer_cb)(struct cras_timer* t, void* data); |
| static void* cras_tm_timer_cb_data; |
| static struct timespec clock_gettime_retspec; |
| static struct cras_iodev* device_enabled_dev; |
| static int device_enabled_count; |
| static struct cras_iodev* device_disabled_dev; |
| static int device_disabled_count; |
| static void* device_enabled_cb_data; |
| static void* device_disabled_cb_data; |
| static struct cras_rstream* audio_thread_add_stream_stream; |
| static struct cras_iodev* audio_thread_add_stream_dev; |
| static struct cras_iodev* audio_thread_disconnect_stream_dev; |
| static int audio_thread_add_stream_called; |
| static unsigned update_active_node_called; |
| static struct cras_iodev* update_active_node_iodev_val[5]; |
| static unsigned update_active_node_node_idx_val[5]; |
| static unsigned update_active_node_dev_enabled_val[5]; |
| static int set_swap_mode_for_node_called; |
| static int set_swap_mode_for_node_enable; |
| static int cras_iodev_start_volume_ramp_called; |
| static size_t cras_observer_add_called; |
| static size_t cras_observer_remove_called; |
| static size_t cras_observer_notify_nodes_called; |
| static size_t cras_observer_notify_active_node_called; |
| static size_t cras_observer_notify_output_node_volume_called; |
| static size_t cras_observer_notify_node_left_right_swapped_called; |
| static size_t cras_observer_notify_input_node_gain_called; |
| static int cras_iodev_open_called; |
| static int cras_iodev_open_ret[8]; |
| static struct cras_audio_format cras_iodev_open_fmt; |
| static int set_mute_called; |
| static std::vector<struct cras_iodev*> set_mute_dev_vector; |
| static std::vector<unsigned int> audio_thread_dev_start_ramp_dev_vector; |
| static int audio_thread_dev_start_ramp_called; |
| static enum CRAS_IODEV_RAMP_REQUEST audio_thread_dev_start_ramp_req; |
| static std::map<int, bool> stream_list_has_pinned_stream_ret; |
| static struct cras_rstream* audio_thread_disconnect_stream_stream; |
| static int audio_thread_disconnect_stream_called; |
| static struct cras_iodev fake_sco_in_dev, fake_sco_out_dev; |
| static struct cras_ionode fake_sco_in_node, fake_sco_out_node; |
| static int server_state_hotword_pause_at_suspend; |
| |
| int dev_idx_in_vector(std::vector<unsigned int> v, unsigned int idx) { |
| return std::find(v.begin(), v.end(), idx) != v.end(); |
| } |
| |
| int device_in_vector(std::vector<struct cras_iodev*> v, |
| struct cras_iodev* dev) { |
| return std::find(v.begin(), v.end(), dev) != v.end(); |
| } |
| |
| class IoDevTestSuite : public testing::Test { |
| protected: |
| virtual void SetUp() { |
| cras_iodev_list_reset(); |
| |
| cras_iodev_close_called = 0; |
| stream_list_get_ret = 0; |
| server_stream_create_called = 0; |
| server_stream_destroy_called = 0; |
| audio_thread_drain_stream_return = 0; |
| audio_thread_drain_stream_called = 0; |
| cras_tm_create_timer_called = 0; |
| cras_tm_cancel_timer_called = 0; |
| |
| audio_thread_disconnect_stream_called = 0; |
| audio_thread_disconnect_stream_stream = NULL; |
| audio_thread_is_dev_open_ret = 0; |
| stream_list_has_pinned_stream_ret.clear(); |
| |
| sample_rates_[0] = 44100; |
| sample_rates_[1] = 48000; |
| sample_rates_[2] = 0; |
| |
| channel_counts_[0] = 2; |
| channel_counts_[1] = 0; |
| |
| fmt_.format = SND_PCM_FORMAT_S16_LE; |
| fmt_.frame_rate = 48000; |
| fmt_.num_channels = 2; |
| |
| memset(&d1_, 0, sizeof(d1_)); |
| memset(&d2_, 0, sizeof(d2_)); |
| memset(&d3_, 0, sizeof(d3_)); |
| |
| memset(&node1, 0, sizeof(node1)); |
| memset(&node2, 0, sizeof(node2)); |
| memset(&node3, 0, sizeof(node3)); |
| |
| d1_.set_volume = NULL; |
| d1_.set_capture_gain = NULL; |
| d1_.set_capture_mute = NULL; |
| d1_.update_supported_formats = NULL; |
| d1_.update_active_node = update_active_node; |
| d1_.set_swap_mode_for_node = set_swap_mode_for_node; |
| d1_.format = NULL; |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| d1_.info.idx = -999; |
| d1_.nodes = &node1; |
| d1_.active_node = &node1; |
| strcpy(d1_.info.name, "d1"); |
| d1_.supported_rates = sample_rates_; |
| d1_.supported_channel_counts = channel_counts_; |
| d2_.set_volume = NULL; |
| d2_.set_capture_gain = NULL; |
| d2_.set_capture_mute = NULL; |
| d2_.update_supported_formats = NULL; |
| d2_.update_active_node = update_active_node; |
| d2_.format = NULL; |
| d2_.direction = CRAS_STREAM_OUTPUT; |
| d2_.info.idx = -999; |
| d2_.nodes = &node2; |
| d2_.active_node = &node2; |
| strcpy(d2_.info.name, "d2"); |
| d2_.supported_rates = sample_rates_; |
| d2_.supported_channel_counts = channel_counts_; |
| d3_.set_volume = NULL; |
| d3_.set_capture_gain = NULL; |
| d3_.set_capture_mute = NULL; |
| d3_.update_supported_formats = NULL; |
| d3_.update_active_node = update_active_node; |
| d3_.format = NULL; |
| d3_.direction = CRAS_STREAM_OUTPUT; |
| d3_.info.idx = -999; |
| d3_.nodes = &node3; |
| d3_.active_node = &node3; |
| strcpy(d3_.info.name, "d3"); |
| d3_.supported_rates = sample_rates_; |
| d3_.supported_channel_counts = channel_counts_; |
| |
| loopback_input.set_volume = NULL; |
| loopback_input.set_capture_gain = NULL; |
| loopback_input.set_capture_mute = NULL; |
| loopback_input.update_supported_formats = NULL; |
| loopback_input.update_active_node = update_active_node; |
| loopback_input.format = NULL; |
| loopback_input.direction = CRAS_STREAM_INPUT; |
| loopback_input.info.idx = -999; |
| loopback_input.nodes = &node3; |
| loopback_input.active_node = &node3; |
| strcpy(loopback_input.info.name, "loopback_input"); |
| loopback_input.supported_rates = sample_rates_; |
| loopback_input.supported_channel_counts = channel_counts_; |
| |
| server_state_update_begin_return = &server_state_stub; |
| system_get_mute_return = false; |
| |
| /* Reset stub data. */ |
| add_stream_called = 0; |
| rm_stream_called = 0; |
| set_node_plugged_called = 0; |
| audio_thread_rm_open_dev_called = 0; |
| audio_thread_add_open_dev_called = 0; |
| audio_thread_set_active_dev_called = 0; |
| audio_thread_add_stream_called = 0; |
| update_active_node_called = 0; |
| cras_observer_add_called = 0; |
| cras_observer_remove_called = 0; |
| cras_observer_notify_nodes_called = 0; |
| cras_observer_notify_active_node_called = 0; |
| cras_observer_notify_output_node_volume_called = 0; |
| cras_observer_notify_node_left_right_swapped_called = 0; |
| cras_observer_notify_input_node_gain_called = 0; |
| cras_iodev_open_called = 0; |
| memset(cras_iodev_open_ret, 0, sizeof(cras_iodev_open_ret)); |
| set_mute_called = 0; |
| set_mute_dev_vector.clear(); |
| set_swap_mode_for_node_called = 0; |
| set_swap_mode_for_node_enable = 0; |
| cras_iodev_start_volume_ramp_called = 0; |
| audio_thread_dev_start_ramp_dev_vector.clear(); |
| audio_thread_dev_start_ramp_called = 0; |
| audio_thread_dev_start_ramp_req = CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK; |
| for (int i = 0; i < 5; i++) |
| update_active_node_iodev_val[i] = NULL; |
| DL_APPEND(fake_sco_in_dev.nodes, &fake_sco_in_node); |
| DL_APPEND(fake_sco_out_dev.nodes, &fake_sco_out_node); |
| fake_sco_in_node.is_sco_pcm = 0; |
| fake_sco_out_node.is_sco_pcm = 0; |
| mock_empty_iodev[0].state = CRAS_IODEV_STATE_CLOSE; |
| mock_empty_iodev[0].update_active_node = update_active_node; |
| mock_empty_iodev[1].state = CRAS_IODEV_STATE_CLOSE; |
| mock_empty_iodev[1].update_active_node = update_active_node; |
| mock_hotword_iodev.update_active_node = update_active_node; |
| server_state_hotword_pause_at_suspend = 0; |
| } |
| |
| virtual void TearDown() { |
| cras_iodev_list_reset(); |
| } |
| |
| static void set_volume_1(struct cras_iodev* iodev) { set_volume_1_called_++; } |
| |
| static void set_capture_gain_1(struct cras_iodev* iodev) { |
| set_capture_gain_1_called_++; |
| } |
| |
| static void set_capture_mute_1(struct cras_iodev* iodev) { |
| set_capture_mute_1_called_++; |
| } |
| |
| static void update_active_node(struct cras_iodev* iodev, |
| unsigned node_idx, |
| unsigned dev_enabled) { |
| int i = update_active_node_called++ % 5; |
| update_active_node_iodev_val[i] = iodev; |
| update_active_node_node_idx_val[i] = node_idx; |
| update_active_node_dev_enabled_val[i] = dev_enabled; |
| } |
| |
| static int set_swap_mode_for_node(struct cras_iodev* iodev, |
| struct cras_ionode* node, |
| int enable) { |
| set_swap_mode_for_node_called++; |
| set_swap_mode_for_node_enable = enable; |
| return 0; |
| } |
| |
| struct cras_iodev d1_; |
| struct cras_iodev d2_; |
| struct cras_iodev d3_; |
| struct cras_audio_format fmt_; |
| size_t sample_rates_[3]; |
| size_t channel_counts_[2]; |
| static int set_volume_1_called_; |
| static int set_capture_gain_1_called_; |
| static int set_capture_mute_1_called_; |
| struct cras_ionode node1, node2, node3; |
| }; |
| |
| int IoDevTestSuite::set_volume_1_called_; |
| int IoDevTestSuite::set_capture_gain_1_called_; |
| int IoDevTestSuite::set_capture_mute_1_called_; |
| |
| // Check that Init registers observer client. */ |
| TEST_F(IoDevTestSuite, InitSetup) { |
| cras_iodev_list_init(); |
| EXPECT_EQ(1, cras_observer_add_called); |
| cras_iodev_list_deinit(); |
| EXPECT_EQ(1, cras_observer_remove_called); |
| } |
| |
| /* Check that the suspend alert from cras_system will trigger suspend |
| * and resume call of all iodevs. */ |
| TEST_F(IoDevTestSuite, SetSuspendResume) { |
| struct cras_rstream rstream, rstream2, rstream3; |
| struct cras_rstream* stream_list = NULL; |
| int rc; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| memset(&rstream2, 0, sizeof(rstream2)); |
| memset(&rstream3, 0, sizeof(rstream3)); |
| |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| |
| audio_thread_add_open_dev_called = 0; |
| cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| DL_APPEND(stream_list, &rstream); |
| stream_add_cb(&rstream); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| |
| DL_APPEND(stream_list, &rstream2); |
| stream_add_cb(&rstream2); |
| EXPECT_EQ(2, audio_thread_add_stream_called); |
| |
| audio_thread_rm_open_dev_called = 0; |
| observer_ops->suspend_changed(NULL, 1); |
| EXPECT_EQ(1, audio_thread_rm_open_dev_called); |
| |
| /* Test disable/enable dev won't cause add_stream to audio_thread. */ |
| audio_thread_add_stream_called = 0; |
| cras_iodev_list_disable_dev(&d1_, false); |
| cras_iodev_list_enable_dev(&d1_); |
| EXPECT_EQ(0, audio_thread_add_stream_called); |
| |
| audio_thread_drain_stream_return = 0; |
| DL_DELETE(stream_list, &rstream2); |
| stream_rm_cb(&rstream2); |
| EXPECT_EQ(1, audio_thread_drain_stream_called); |
| |
| /* Test stream_add_cb won't cause add_stream to audio_thread. */ |
| audio_thread_add_stream_called = 0; |
| DL_APPEND(stream_list, &rstream3); |
| stream_add_cb(&rstream3); |
| EXPECT_EQ(0, audio_thread_add_stream_called); |
| |
| audio_thread_add_open_dev_called = 0; |
| audio_thread_add_stream_called = 0; |
| stream_list_get_ret = stream_list; |
| observer_ops->suspend_changed(NULL, 0); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| EXPECT_EQ(2, audio_thread_add_stream_called); |
| EXPECT_EQ(&rstream3, audio_thread_add_stream_stream); |
| |
| cras_iodev_list_deinit(); |
| EXPECT_EQ(3, cras_observer_notify_active_node_called); |
| } |
| |
| /* Check that the suspend/resume call of active iodev will be triggered and |
| * fallback device will be transciently enabled while adding a new stream whose |
| * channel count is higher than the active iodev. */ |
| TEST_F(IoDevTestSuite, ReopenDevForHigherChannels) { |
| struct cras_rstream rstream, rstream2; |
| struct cras_rstream* stream_list = NULL; |
| int rc; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| memset(&rstream2, 0, sizeof(rstream2)); |
| rstream.format = fmt_; |
| rstream2.format = fmt_; |
| rstream2.format.num_channels = 6; |
| |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| d1_.info.max_supported_channels = 2; |
| |
| audio_thread_add_open_dev_called = 0; |
| cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| DL_APPEND(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| stream_add_cb(&rstream); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| EXPECT_EQ(1, cras_iodev_open_called); |
| EXPECT_EQ(2, cras_iodev_open_fmt.num_channels); |
| |
| audio_thread_add_stream_called = 0; |
| audio_thread_add_open_dev_called = 0; |
| cras_iodev_open_called = 0; |
| |
| /* stream_list should be descending ordered by channel count. */ |
| DL_PREPEND(stream_list, &rstream2); |
| stream_list_get_ret = stream_list; |
| stream_add_cb(&rstream2); |
| /* The channel count(=6) of rstream2 exceeds d1's max_supported_channels(=2), |
| * rstream2 will be added directly to d1, which will not be re-opened. */ |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(0, audio_thread_add_open_dev_called); |
| EXPECT_EQ(0, cras_iodev_open_called); |
| |
| d1_.info.max_supported_channels = 6; |
| stream_rm_cb(&rstream2); |
| |
| audio_thread_add_stream_called = 0; |
| audio_thread_add_open_dev_called = 0; |
| cras_iodev_open_called = 0; |
| |
| stream_add_cb(&rstream2); |
| /* Added both rstreams to fallback device, then re-opened d1. */ |
| EXPECT_EQ(4, audio_thread_add_stream_called); |
| EXPECT_EQ(2, audio_thread_add_open_dev_called); |
| EXPECT_EQ(2, cras_iodev_open_called); |
| EXPECT_EQ(6, cras_iodev_open_fmt.num_channels); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| /* Check that after resume, all output devices enter ramp mute state if there is |
| * any output stream. */ |
| TEST_F(IoDevTestSuite, RampMuteAfterResume) { |
| struct cras_rstream rstream, rstream2; |
| struct cras_rstream* stream_list = NULL; |
| int rc; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| d1_.initial_ramp_request = CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d2_.direction = CRAS_STREAM_INPUT; |
| d2_.initial_ramp_request = CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK; |
| rc = cras_iodev_list_add_input(&d2_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| d2_.format = &fmt_; |
| |
| audio_thread_add_open_dev_called = 0; |
| cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| |
| rstream.direction = CRAS_STREAM_OUTPUT; |
| DL_APPEND(stream_list, &rstream); |
| stream_add_cb(&rstream); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| |
| rstream2.direction = CRAS_STREAM_INPUT; |
| DL_APPEND(stream_list, &rstream2); |
| stream_add_cb(&rstream2); |
| |
| /* Suspend and resume */ |
| observer_ops->suspend_changed(NULL, 1); |
| stream_list_get_ret = stream_list; |
| observer_ops->suspend_changed(NULL, 0); |
| |
| /* Test only output device that has stream will be muted after resume */ |
| EXPECT_EQ(d1_.initial_ramp_request, CRAS_IODEV_RAMP_REQUEST_RESUME_MUTE); |
| EXPECT_EQ(CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK, |
| d2_.initial_ramp_request); |
| |
| /* Reset d1 ramp_mute and remove output stream to test again */ |
| d1_.initial_ramp_request = CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK; |
| DL_DELETE(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| stream_rm_cb(&rstream); |
| |
| /* Suspend and resume */ |
| observer_ops->suspend_changed(NULL, 1); |
| stream_list_get_ret = stream_list; |
| observer_ops->suspend_changed(NULL, 0); |
| |
| EXPECT_EQ(CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK, |
| d1_.initial_ramp_request); |
| EXPECT_EQ(CRAS_IODEV_RAMP_REQUEST_UP_START_PLAYBACK, |
| d2_.initial_ramp_request); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, InitDevFailShouldEnableFallback) { |
| int rc; |
| struct cras_rstream rstream; |
| struct cras_rstream* stream_list = NULL; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 0)); |
| |
| cras_iodev_open_ret[0] = -5; |
| cras_iodev_open_ret[1] = 0; |
| |
| DL_APPEND(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| stream_add_cb(&rstream); |
| /* open dev called twice, one for fallback device. */ |
| EXPECT_EQ(2, cras_iodev_open_called); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, InitDevWithEchoRef) { |
| int rc; |
| struct cras_rstream rstream; |
| struct cras_rstream* stream_list = NULL; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| d1_.echo_reference_dev = &d2_; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d2_.direction = CRAS_STREAM_INPUT; |
| snprintf(d2_.active_node->name, CRAS_NODE_NAME_BUFFER_SIZE, "echo ref"); |
| rc = cras_iodev_list_add_input(&d2_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| d2_.format = &fmt_; |
| |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 0)); |
| /* No close call happened, because no stream exists. */ |
| EXPECT_EQ(0, cras_iodev_close_called); |
| |
| cras_iodev_open_ret[1] = 0; |
| |
| DL_APPEND(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| stream_add_cb(&rstream); |
| |
| EXPECT_EQ(1, cras_iodev_open_called); |
| EXPECT_EQ(1, server_stream_create_called); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| |
| DL_DELETE(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| stream_rm_cb(&rstream); |
| |
| clock_gettime_retspec.tv_sec = 11; |
| clock_gettime_retspec.tv_nsec = 0; |
| cras_tm_timer_cb(NULL, NULL); |
| |
| EXPECT_EQ(1, cras_iodev_close_called); |
| EXPECT_EQ(1, server_stream_destroy_called); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, SelectNodeOpenFailShouldScheduleRetry) { |
| struct cras_rstream rstream; |
| struct cras_rstream* stream_list = NULL; |
| int rc; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d2_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d2_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| d2_.format = &fmt_; |
| |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| DL_APPEND(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| stream_add_cb(&rstream); |
| |
| /* Select node triggers: fallback open, d1 close, d2 open, fallback close. */ |
| cras_iodev_close_called = 0; |
| cras_iodev_open_called = 0; |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d2_.info.idx, 1)); |
| EXPECT_EQ(2, cras_iodev_close_called); |
| EXPECT_EQ(2, cras_iodev_open_called); |
| EXPECT_EQ(0, cras_tm_create_timer_called); |
| EXPECT_EQ(0, cras_tm_cancel_timer_called); |
| |
| /* Test that if select to d1 and open d1 fail, fallback doesn't close. */ |
| cras_iodev_open_called = 0; |
| cras_iodev_open_ret[0] = 0; |
| cras_iodev_open_ret[1] = -5; |
| cras_iodev_open_ret[2] = 0; |
| cras_tm_timer_cb = NULL; |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| EXPECT_EQ(3, cras_iodev_close_called); |
| EXPECT_EQ(&d2_, cras_iodev_close_dev); |
| EXPECT_EQ(2, cras_iodev_open_called); |
| EXPECT_EQ(0, cras_tm_cancel_timer_called); |
| |
| /* Assert a timer is scheduled to retry open. */ |
| EXPECT_NE((void*)NULL, cras_tm_timer_cb); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| |
| audio_thread_add_stream_called = 0; |
| cras_tm_timer_cb(NULL, cras_tm_timer_cb_data); |
| EXPECT_EQ(3, cras_iodev_open_called); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| |
| /* Retry open success will close fallback dev. */ |
| EXPECT_EQ(4, cras_iodev_close_called); |
| EXPECT_EQ(0, cras_tm_cancel_timer_called); |
| |
| /* Select to d2 and fake an open failure. */ |
| cras_iodev_close_called = 0; |
| cras_iodev_open_called = 0; |
| cras_iodev_open_ret[0] = 0; |
| cras_iodev_open_ret[1] = -5; |
| cras_iodev_open_ret[2] = 0; |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d2_.info.idx, 1)); |
| EXPECT_EQ(1, cras_iodev_close_called); |
| EXPECT_EQ(&d1_, cras_iodev_close_dev); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| EXPECT_NE((void*)NULL, cras_tm_timer_cb); |
| |
| /* Select to another iodev should cancel the timer. */ |
| memset(cras_iodev_open_ret, 0, sizeof(cras_iodev_open_ret)); |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d2_.info.idx, 1)); |
| EXPECT_EQ(1, cras_tm_cancel_timer_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, InitDevFailShouldScheduleRetry) { |
| int rc; |
| struct cras_rstream rstream; |
| struct cras_rstream* stream_list = NULL; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| rstream.format = fmt_; |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 0)); |
| |
| update_active_node_called = 0; |
| cras_iodev_open_ret[0] = -5; |
| cras_iodev_open_ret[1] = 0; |
| cras_tm_timer_cb = NULL; |
| DL_APPEND(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| stream_add_cb(&rstream); |
| /* open dev called twice, one for fallback device. */ |
| EXPECT_EQ(2, cras_iodev_open_called); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(0, update_active_node_called); |
| EXPECT_EQ(&mock_empty_iodev[CRAS_STREAM_OUTPUT], audio_thread_add_stream_dev); |
| |
| EXPECT_NE((void*)NULL, cras_tm_timer_cb); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| |
| /* If retry still fail, won't schedule more retry. */ |
| cras_iodev_open_ret[2] = -5; |
| cras_tm_timer_cb(NULL, cras_tm_timer_cb_data); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| |
| mock_empty_iodev[CRAS_STREAM_OUTPUT].format = &fmt_; |
| cras_tm_timer_cb = NULL; |
| cras_iodev_open_ret[3] = -5; |
| stream_add_cb(&rstream); |
| EXPECT_NE((void*)NULL, cras_tm_timer_cb); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| |
| cras_iodev_list_rm_output(&d1_); |
| EXPECT_EQ(1, cras_tm_cancel_timer_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, PinnedStreamInitFailShouldScheduleRetry) { |
| int rc; |
| struct cras_rstream rstream; |
| struct cras_rstream* stream_list = NULL; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| |
| rstream.is_pinned = 1; |
| rstream.pinned_dev_idx = d1_.info.idx; |
| |
| cras_iodev_open_ret[0] = -5; |
| cras_iodev_open_ret[1] = 0; |
| cras_tm_timer_cb = NULL; |
| DL_APPEND(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| stream_add_cb(&rstream); |
| /* Init pinned dev fail, not proceed to add stream. */ |
| EXPECT_EQ(1, cras_iodev_open_called); |
| EXPECT_EQ(0, audio_thread_add_stream_called); |
| |
| EXPECT_NE((void*)NULL, cras_tm_timer_cb); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| |
| cras_tm_timer_cb(NULL, cras_tm_timer_cb_data); |
| EXPECT_EQ(2, cras_iodev_open_called); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| |
| cras_iodev_list_rm_output(&d1_); |
| cras_iodev_list_deinit(); |
| } |
| |
| static void device_enabled_cb(struct cras_iodev* dev, void* cb_data) { |
| device_enabled_dev = dev; |
| device_enabled_count++; |
| device_enabled_cb_data = cb_data; |
| } |
| |
| static void device_disabled_cb(struct cras_iodev* dev, void* cb_data) { |
| device_disabled_dev = dev; |
| device_disabled_count++; |
| device_disabled_cb_data = cb_data; |
| } |
| |
| TEST_F(IoDevTestSuite, SelectNode) { |
| struct cras_rstream rstream, rstream2; |
| int rc; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| memset(&rstream2, 0, sizeof(rstream2)); |
| |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| node1.idx = 1; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d2_.direction = CRAS_STREAM_OUTPUT; |
| node2.idx = 2; |
| rc = cras_iodev_list_add_output(&d2_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| d2_.format = &fmt_; |
| |
| audio_thread_add_open_dev_called = 0; |
| audio_thread_rm_open_dev_called = 0; |
| |
| device_enabled_count = 0; |
| device_disabled_count = 0; |
| |
| EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback( |
| device_enabled_cb, device_disabled_cb, (void*)0xABCD)); |
| |
| cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| |
| EXPECT_EQ(1, device_enabled_count); |
| EXPECT_EQ(1, cras_observer_notify_active_node_called); |
| EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT)); |
| |
| // There should be a disable device call for the fallback device. |
| // But no close call actually happened, because no stream exists. |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| EXPECT_EQ(1, device_disabled_count); |
| EXPECT_NE(&d1_, device_disabled_dev); |
| |
| DL_APPEND(stream_list_get_ret, &rstream); |
| stream_add_cb(&rstream); |
| |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| |
| DL_APPEND(stream_list_get_ret, &rstream2); |
| stream_add_cb(&rstream2); |
| |
| EXPECT_EQ(2, audio_thread_add_stream_called); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d2_.info.idx, 2)); |
| |
| // Additional enabled devices: fallback device, d2_. |
| EXPECT_EQ(3, device_enabled_count); |
| // Additional disabled devices: d1_, fallback device. |
| EXPECT_EQ(3, device_disabled_count); |
| EXPECT_EQ(2, audio_thread_rm_open_dev_called); |
| EXPECT_EQ(2, cras_observer_notify_active_node_called); |
| EXPECT_EQ(&d2_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT)); |
| |
| // For each stream, the stream is added for fallback device and d2_. |
| EXPECT_EQ(6, audio_thread_add_stream_called); |
| |
| EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL, NULL)); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, SelectPreviouslyEnabledNode) { |
| struct cras_rstream rstream; |
| int rc; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| node1.idx = 1; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d2_.direction = CRAS_STREAM_OUTPUT; |
| node2.idx = 2; |
| rc = cras_iodev_list_add_output(&d2_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| d2_.format = &fmt_; |
| |
| audio_thread_add_open_dev_called = 0; |
| audio_thread_rm_open_dev_called = 0; |
| device_enabled_count = 0; |
| device_disabled_count = 0; |
| |
| EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback( |
| device_enabled_cb, device_disabled_cb, (void*)0xABCD)); |
| |
| // Add an active node. |
| cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| |
| EXPECT_EQ(1, device_enabled_count); |
| EXPECT_EQ(1, cras_observer_notify_active_node_called); |
| EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT)); |
| |
| // There should be a disable device call for the fallback device. |
| EXPECT_EQ(1, device_disabled_count); |
| EXPECT_NE(&d1_, device_disabled_dev); |
| EXPECT_NE(&d2_, device_disabled_dev); |
| |
| DL_APPEND(stream_list_get_ret, &rstream); |
| stream_add_cb(&rstream); |
| |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| |
| // Add a second active node. |
| cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d2_.info.idx, 2)); |
| |
| EXPECT_EQ(2, device_enabled_count); |
| EXPECT_EQ(1, device_disabled_count); |
| EXPECT_EQ(2, cras_observer_notify_active_node_called); |
| EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT)); |
| |
| EXPECT_EQ(2, audio_thread_add_open_dev_called); |
| EXPECT_EQ(2, audio_thread_add_stream_called); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| |
| // Select the second added active node - the initially added node should get |
| // disabled. |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d2_.info.idx, 2)); |
| |
| EXPECT_EQ(2, device_enabled_count); |
| EXPECT_EQ(2, device_disabled_count); |
| EXPECT_EQ(3, cras_observer_notify_active_node_called); |
| |
| EXPECT_EQ(&d2_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT)); |
| EXPECT_EQ(&d1_, device_disabled_dev); |
| |
| EXPECT_EQ(2, audio_thread_add_stream_called); |
| EXPECT_EQ(2, audio_thread_add_open_dev_called); |
| EXPECT_EQ(1, audio_thread_rm_open_dev_called); |
| |
| EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL, NULL)); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, UpdateActiveNode) { |
| int rc; |
| |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d2_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d2_); |
| ASSERT_EQ(0, rc); |
| |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d2_.info.idx, 1)); |
| |
| EXPECT_EQ(2, update_active_node_called); |
| EXPECT_EQ(&d2_, update_active_node_iodev_val[0]); |
| EXPECT_EQ(1, update_active_node_node_idx_val[0]); |
| EXPECT_EQ(1, update_active_node_dev_enabled_val[0]); |
| |
| /* Fake the active node idx on d2_, and later assert this node is |
| * called for update_active_node when d2_ disabled. */ |
| d2_.active_node->idx = 2; |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 0)); |
| |
| EXPECT_EQ(5, update_active_node_called); |
| EXPECT_EQ(&d2_, update_active_node_iodev_val[2]); |
| EXPECT_EQ(&d1_, update_active_node_iodev_val[3]); |
| EXPECT_EQ(2, update_active_node_node_idx_val[2]); |
| EXPECT_EQ(0, update_active_node_node_idx_val[3]); |
| EXPECT_EQ(0, update_active_node_dev_enabled_val[2]); |
| EXPECT_EQ(1, update_active_node_dev_enabled_val[3]); |
| EXPECT_EQ(2, cras_observer_notify_active_node_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, SelectNonExistingNode) { |
| int rc; |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 0)); |
| EXPECT_EQ(1, d1_.is_enabled); |
| |
| /* Select non-existing node should disable all devices. */ |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, cras_make_node_id(2, 1)); |
| EXPECT_EQ(0, d1_.is_enabled); |
| EXPECT_EQ(2, cras_observer_notify_active_node_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| // Devices with the wrong direction should be rejected. |
| TEST_F(IoDevTestSuite, AddWrongDirection) { |
| int rc; |
| |
| rc = cras_iodev_list_add_input(&d1_); |
| EXPECT_EQ(-EINVAL, rc); |
| d1_.direction = CRAS_STREAM_INPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| EXPECT_EQ(-EINVAL, rc); |
| } |
| |
| // Test adding/removing an iodev to the list. |
| TEST_F(IoDevTestSuite, AddRemoveOutput) { |
| struct cras_iodev_info* dev_info; |
| int rc; |
| cras_iodev_list_init(); |
| |
| rc = cras_iodev_list_add_output(&d1_); |
| EXPECT_EQ(0, rc); |
| // Test can't insert same iodev twice. |
| rc = cras_iodev_list_add_output(&d1_); |
| EXPECT_NE(0, rc); |
| // Test insert a second output. |
| rc = cras_iodev_list_add_output(&d2_); |
| EXPECT_EQ(0, rc); |
| |
| // Test that it is removed. |
| rc = cras_iodev_list_rm_output(&d1_); |
| EXPECT_EQ(0, rc); |
| // Test that we can't remove a dev twice. |
| rc = cras_iodev_list_rm_output(&d1_); |
| EXPECT_NE(0, rc); |
| // Should be 1 dev now. |
| rc = cras_iodev_list_get_outputs(&dev_info); |
| EXPECT_EQ(1, rc); |
| free(dev_info); |
| // Passing null should return the number of outputs. |
| rc = cras_iodev_list_get_outputs(NULL); |
| EXPECT_EQ(1, rc); |
| // Remove other dev. |
| rc = cras_iodev_list_rm_output(&d2_); |
| EXPECT_EQ(0, rc); |
| // Should be 0 devs now. |
| rc = cras_iodev_list_get_outputs(&dev_info); |
| EXPECT_EQ(0, rc); |
| free(dev_info); |
| EXPECT_EQ(0, cras_observer_notify_active_node_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| // Test output_mute_changed callback. |
| TEST_F(IoDevTestSuite, OutputMuteChangedToMute) { |
| cras_iodev_list_init(); |
| |
| cras_iodev_list_add_output(&d1_); |
| cras_iodev_list_add_output(&d2_); |
| cras_iodev_list_add_output(&d3_); |
| |
| // d1_ and d2_ are enabled. |
| cras_iodev_list_enable_dev(&d1_); |
| cras_iodev_list_enable_dev(&d2_); |
| |
| // Assume d1 and d2 devices are open. |
| d1_.state = CRAS_IODEV_STATE_OPEN; |
| d2_.state = CRAS_IODEV_STATE_OPEN; |
| d3_.state = CRAS_IODEV_STATE_CLOSE; |
| |
| // Execute the callback. |
| observer_ops->output_mute_changed(NULL, 0, 1, 0); |
| |
| // d1_ and d2_ should set mute state through audio_thread_dev_start_ramp |
| // because they are both open. |
| EXPECT_EQ(2, audio_thread_dev_start_ramp_called); |
| ASSERT_TRUE( |
| dev_idx_in_vector(audio_thread_dev_start_ramp_dev_vector, d2_.info.idx)); |
| ASSERT_TRUE( |
| dev_idx_in_vector(audio_thread_dev_start_ramp_dev_vector, d1_.info.idx)); |
| EXPECT_EQ(CRAS_IODEV_RAMP_REQUEST_DOWN_MUTE, audio_thread_dev_start_ramp_req); |
| |
| // d3_ should set mute state right away without calling ramp |
| // because it is not open. |
| EXPECT_EQ(1, set_mute_called); |
| EXPECT_EQ(1, set_mute_dev_vector.size()); |
| ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_)); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| // Test output_mute_changed callback. |
| TEST_F(IoDevTestSuite, OutputMuteChangedToUnmute) { |
| cras_iodev_list_init(); |
| |
| cras_iodev_list_add_output(&d1_); |
| cras_iodev_list_add_output(&d2_); |
| cras_iodev_list_add_output(&d3_); |
| |
| // d1_ and d2_ are enabled. |
| cras_iodev_list_enable_dev(&d1_); |
| cras_iodev_list_enable_dev(&d2_); |
| |
| // Assume d1 and d2 devices are open. |
| d1_.state = CRAS_IODEV_STATE_OPEN; |
| d2_.state = CRAS_IODEV_STATE_CLOSE; |
| d3_.state = CRAS_IODEV_STATE_CLOSE; |
| |
| // Execute the callback. |
| observer_ops->output_mute_changed(NULL, 0, 0, 0); |
| |
| // d1_ should set mute state through audio_thread_dev_start_ramp. |
| EXPECT_EQ(1, audio_thread_dev_start_ramp_called); |
| ASSERT_TRUE( |
| dev_idx_in_vector(audio_thread_dev_start_ramp_dev_vector, d1_.info.idx)); |
| EXPECT_EQ(CRAS_IODEV_RAMP_REQUEST_UP_UNMUTE, audio_thread_dev_start_ramp_req); |
| |
| // d2_ and d3_ should set mute state right away because they both |
| // are closed. |
| EXPECT_EQ(2, set_mute_called); |
| EXPECT_EQ(2, set_mute_dev_vector.size()); |
| ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d2_)); |
| ASSERT_TRUE(device_in_vector(set_mute_dev_vector, &d3_)); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| // Test enable/disable an iodev. |
| TEST_F(IoDevTestSuite, EnableDisableDevice) { |
| struct cras_rstream rstream; |
| cras_iodev_list_init(); |
| device_enabled_count = 0; |
| device_disabled_count = 0; |
| memset(&rstream, 0, sizeof(rstream)); |
| |
| EXPECT_EQ(0, cras_iodev_list_add_output(&d1_)); |
| |
| EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback( |
| device_enabled_cb, device_disabled_cb, (void*)0xABCD)); |
| |
| // Enable a device, fallback should be diabled accordingly. |
| cras_iodev_list_enable_dev(&d1_); |
| EXPECT_EQ(&d1_, device_enabled_dev); |
| EXPECT_EQ((void*)0xABCD, device_enabled_cb_data); |
| EXPECT_EQ(1, device_enabled_count); |
| EXPECT_EQ(1, device_disabled_count); |
| EXPECT_EQ(&d1_, cras_iodev_list_get_first_enabled_iodev(CRAS_STREAM_OUTPUT)); |
| |
| // Connect a normal stream. |
| cras_iodev_open_called = 0; |
| stream_add_cb(&rstream); |
| EXPECT_EQ(1, cras_iodev_open_called); |
| |
| stream_list_has_pinned_stream_ret[d1_.info.idx] = 0; |
| // Disable a device. Expect dev is closed because there's no pinned stream. |
| update_active_node_called = 0; |
| cras_iodev_list_disable_dev(&d1_, false); |
| EXPECT_EQ(&d1_, device_disabled_dev); |
| EXPECT_EQ(2, device_disabled_count); |
| EXPECT_EQ((void*)0xABCD, device_disabled_cb_data); |
| |
| EXPECT_EQ(1, audio_thread_rm_open_dev_called); |
| EXPECT_EQ(1, cras_iodev_close_called); |
| EXPECT_EQ(&d1_, cras_iodev_close_dev); |
| EXPECT_EQ(1, update_active_node_called); |
| |
| EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback( |
| device_enabled_cb, device_disabled_cb, (void*)0xCDEF)); |
| EXPECT_EQ(2, cras_observer_notify_active_node_called); |
| |
| EXPECT_EQ(0, cras_iodev_list_set_device_enabled_callback(NULL, NULL, NULL)); |
| cras_iodev_list_deinit(); |
| } |
| |
| // Test adding/removing an input dev to the list. |
| TEST_F(IoDevTestSuite, AddRemoveInput) { |
| struct cras_iodev_info* dev_info; |
| int rc, i; |
| uint64_t found_mask; |
| |
| d1_.direction = CRAS_STREAM_INPUT; |
| d2_.direction = CRAS_STREAM_INPUT; |
| |
| cras_iodev_list_init(); |
| |
| // Check no devices exist initially. |
| rc = cras_iodev_list_get_inputs(NULL); |
| EXPECT_EQ(0, rc); |
| |
| rc = cras_iodev_list_add_input(&d1_); |
| EXPECT_EQ(0, rc); |
| EXPECT_GE(d1_.info.idx, 0); |
| // Test can't insert same iodev twice. |
| rc = cras_iodev_list_add_input(&d1_); |
| EXPECT_NE(0, rc); |
| // Test insert a second input. |
| rc = cras_iodev_list_add_input(&d2_); |
| EXPECT_EQ(0, rc); |
| EXPECT_GE(d2_.info.idx, 1); |
| // make sure shared state was updated. |
| EXPECT_EQ(2, server_state_stub.num_input_devs); |
| EXPECT_EQ(d2_.info.idx, server_state_stub.input_devs[0].idx); |
| EXPECT_EQ(d1_.info.idx, server_state_stub.input_devs[1].idx); |
| |
| // List the outputs. |
| rc = cras_iodev_list_get_inputs(&dev_info); |
| EXPECT_EQ(2, rc); |
| if (rc == 2) { |
| found_mask = 0; |
| for (i = 0; i < rc; i++) { |
| uint32_t idx = dev_info[i].idx; |
| EXPECT_EQ(0, (found_mask & (static_cast<uint64_t>(1) << idx))); |
| found_mask |= (static_cast<uint64_t>(1) << idx); |
| } |
| } |
| if (rc > 0) |
| free(dev_info); |
| |
| // Test that it is removed. |
| rc = cras_iodev_list_rm_input(&d1_); |
| EXPECT_EQ(0, rc); |
| // Test that we can't remove a dev twice. |
| rc = cras_iodev_list_rm_input(&d1_); |
| EXPECT_NE(0, rc); |
| // Should be 1 dev now. |
| rc = cras_iodev_list_get_inputs(&dev_info); |
| EXPECT_EQ(1, rc); |
| free(dev_info); |
| // Remove other dev. |
| rc = cras_iodev_list_rm_input(&d2_); |
| EXPECT_EQ(0, rc); |
| // Shouldn't be any devices left. |
| rc = cras_iodev_list_get_inputs(&dev_info); |
| EXPECT_EQ(0, rc); |
| free(dev_info); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| // Test adding/removing an input dev to the list without updating the server |
| // state. |
| TEST_F(IoDevTestSuite, AddRemoveInputNoSem) { |
| int rc; |
| |
| d1_.direction = CRAS_STREAM_INPUT; |
| d2_.direction = CRAS_STREAM_INPUT; |
| |
| server_state_update_begin_return = NULL; |
| cras_iodev_list_init(); |
| |
| rc = cras_iodev_list_add_input(&d1_); |
| EXPECT_EQ(0, rc); |
| EXPECT_GE(d1_.info.idx, 0); |
| rc = cras_iodev_list_add_input(&d2_); |
| EXPECT_EQ(0, rc); |
| EXPECT_GE(d2_.info.idx, 1); |
| |
| EXPECT_EQ(0, cras_iodev_list_rm_input(&d1_)); |
| EXPECT_EQ(0, cras_iodev_list_rm_input(&d2_)); |
| cras_iodev_list_deinit(); |
| } |
| |
| // Test removing the last input. |
| TEST_F(IoDevTestSuite, RemoveLastInput) { |
| struct cras_iodev_info* dev_info; |
| int rc; |
| |
| d1_.direction = CRAS_STREAM_INPUT; |
| d2_.direction = CRAS_STREAM_INPUT; |
| |
| cras_iodev_list_init(); |
| |
| rc = cras_iodev_list_add_input(&d1_); |
| EXPECT_EQ(0, rc); |
| rc = cras_iodev_list_add_input(&d2_); |
| EXPECT_EQ(0, rc); |
| |
| // Test that it is removed. |
| rc = cras_iodev_list_rm_input(&d1_); |
| EXPECT_EQ(0, rc); |
| // Add it back. |
| rc = cras_iodev_list_add_input(&d1_); |
| EXPECT_EQ(0, rc); |
| // And again. |
| rc = cras_iodev_list_rm_input(&d1_); |
| EXPECT_EQ(0, rc); |
| // Add it back. |
| rc = cras_iodev_list_add_input(&d1_); |
| EXPECT_EQ(0, rc); |
| // Remove other dev. |
| rc = cras_iodev_list_rm_input(&d2_); |
| EXPECT_EQ(0, rc); |
| // Add it back. |
| rc = cras_iodev_list_add_input(&d2_); |
| EXPECT_EQ(0, rc); |
| // Remove both. |
| rc = cras_iodev_list_rm_input(&d2_); |
| EXPECT_EQ(0, rc); |
| rc = cras_iodev_list_rm_input(&d1_); |
| EXPECT_EQ(0, rc); |
| // Shouldn't be any devices left. |
| rc = cras_iodev_list_get_inputs(&dev_info); |
| EXPECT_EQ(0, rc); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| // Test nodes changed notification is sent. |
| TEST_F(IoDevTestSuite, NodesChangedNotification) { |
| cras_iodev_list_init(); |
| EXPECT_EQ(1, cras_observer_add_called); |
| |
| cras_iodev_list_notify_nodes_changed(); |
| EXPECT_EQ(1, cras_observer_notify_nodes_called); |
| |
| cras_iodev_list_deinit(); |
| EXPECT_EQ(1, cras_observer_remove_called); |
| } |
| |
| // Test callback function for left right swap mode is set and called. |
| TEST_F(IoDevTestSuite, NodesLeftRightSwappedCallback) { |
| struct cras_iodev iodev; |
| struct cras_ionode ionode; |
| memset(&iodev, 0, sizeof(iodev)); |
| memset(&ionode, 0, sizeof(ionode)); |
| ionode.dev = &iodev; |
| cras_iodev_list_notify_node_left_right_swapped(&ionode); |
| EXPECT_EQ(1, cras_observer_notify_node_left_right_swapped_called); |
| } |
| |
| // Test callback function for volume and gain are set and called. |
| TEST_F(IoDevTestSuite, VolumeGainCallback) { |
| struct cras_iodev iodev; |
| struct cras_ionode ionode; |
| memset(&iodev, 0, sizeof(iodev)); |
| memset(&ionode, 0, sizeof(ionode)); |
| ionode.dev = &iodev; |
| cras_iodev_list_notify_node_volume(&ionode); |
| cras_iodev_list_notify_node_capture_gain(&ionode); |
| EXPECT_EQ(1, cras_observer_notify_output_node_volume_called); |
| EXPECT_EQ(1, cras_observer_notify_input_node_gain_called); |
| } |
| |
| TEST_F(IoDevTestSuite, IodevListSetNodeAttr) { |
| int rc; |
| |
| cras_iodev_list_init(); |
| |
| // The list is empty now. |
| rc = cras_iodev_list_set_node_attr(cras_make_node_id(0, 0), |
| IONODE_ATTR_PLUGGED, 1); |
| EXPECT_LE(rc, 0); |
| EXPECT_EQ(0, set_node_plugged_called); |
| |
| // Add two device, each with one node. |
| d1_.direction = CRAS_STREAM_INPUT; |
| EXPECT_EQ(0, cras_iodev_list_add_input(&d1_)); |
| node1.idx = 1; |
| EXPECT_EQ(0, cras_iodev_list_add_output(&d2_)); |
| node2.idx = 2; |
| |
| // Mismatch id |
| rc = cras_iodev_list_set_node_attr(cras_make_node_id(d2_.info.idx, 1), |
| IONODE_ATTR_PLUGGED, 1); |
| EXPECT_LT(rc, 0); |
| EXPECT_EQ(0, set_node_plugged_called); |
| |
| // Mismatch id |
| rc = cras_iodev_list_set_node_attr(cras_make_node_id(d1_.info.idx, 2), |
| IONODE_ATTR_PLUGGED, 1); |
| EXPECT_LT(rc, 0); |
| EXPECT_EQ(0, set_node_plugged_called); |
| |
| // Correct device id and node id |
| rc = cras_iodev_list_set_node_attr(cras_make_node_id(d1_.info.idx, 1), |
| IONODE_ATTR_PLUGGED, 1); |
| EXPECT_EQ(rc, 0); |
| EXPECT_EQ(1, set_node_plugged_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, SetNodeVolumeCaptureGain) { |
| int rc; |
| |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| node1.idx = 1; |
| node1.dev = &d1_; |
| |
| // Do not ramp without software volume. |
| d1_.software_volume_needed = 0; |
| cras_iodev_list_set_node_attr(cras_make_node_id(d1_.info.idx, 1), |
| IONODE_ATTR_VOLUME, 10); |
| EXPECT_EQ(1, cras_observer_notify_output_node_volume_called); |
| EXPECT_EQ(0, cras_iodev_start_volume_ramp_called); |
| |
| // Even with software volume, device with NULL ramp won't trigger ramp start. |
| d1_.software_volume_needed = 1; |
| cras_iodev_list_set_node_attr(cras_make_node_id(d1_.info.idx, 1), |
| IONODE_ATTR_VOLUME, 20); |
| EXPECT_EQ(2, cras_observer_notify_output_node_volume_called); |
| EXPECT_EQ(0, cras_iodev_start_volume_ramp_called); |
| |
| // System mute prevents volume ramp from starting |
| system_get_mute_return = true; |
| cras_iodev_list_set_node_attr(cras_make_node_id(d1_.info.idx, 1), |
| IONODE_ATTR_VOLUME, 20); |
| EXPECT_EQ(3, cras_observer_notify_output_node_volume_called); |
| EXPECT_EQ(0, cras_iodev_start_volume_ramp_called); |
| |
| // Ramp starts only when it's non-NULL, software volume is used, and |
| // system is not muted |
| system_get_mute_return = false; |
| d1_.ramp = reinterpret_cast<struct cras_ramp*>(0x1); |
| cras_iodev_list_set_node_attr(cras_make_node_id(d1_.info.idx, 1), |
| IONODE_ATTR_VOLUME, 20); |
| EXPECT_EQ(4, cras_observer_notify_output_node_volume_called); |
| EXPECT_EQ(1, cras_iodev_start_volume_ramp_called); |
| |
| d1_.direction = CRAS_STREAM_INPUT; |
| cras_iodev_list_set_node_attr(cras_make_node_id(d1_.info.idx, 1), |
| IONODE_ATTR_CAPTURE_GAIN, 15); |
| EXPECT_EQ(1, cras_observer_notify_input_node_gain_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, SetNodeSwapLeftRight) { |
| int rc; |
| |
| cras_iodev_list_init(); |
| |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| node1.idx = 1; |
| node1.dev = &d1_; |
| |
| cras_iodev_list_set_node_attr(cras_make_node_id(d1_.info.idx, 1), |
| IONODE_ATTR_SWAP_LEFT_RIGHT, 1); |
| EXPECT_EQ(1, set_swap_mode_for_node_called); |
| EXPECT_EQ(1, set_swap_mode_for_node_enable); |
| EXPECT_EQ(1, node1.left_right_swapped); |
| EXPECT_EQ(1, cras_observer_notify_node_left_right_swapped_called); |
| |
| cras_iodev_list_set_node_attr(cras_make_node_id(d1_.info.idx, 1), |
| IONODE_ATTR_SWAP_LEFT_RIGHT, 0); |
| EXPECT_EQ(2, set_swap_mode_for_node_called); |
| EXPECT_EQ(0, set_swap_mode_for_node_enable); |
| EXPECT_EQ(0, node1.left_right_swapped); |
| EXPECT_EQ(2, cras_observer_notify_node_left_right_swapped_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, AddActiveNode) { |
| int rc; |
| struct cras_rstream rstream; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| d2_.direction = CRAS_STREAM_OUTPUT; |
| d3_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| rc = cras_iodev_list_add_output(&d2_); |
| ASSERT_EQ(0, rc); |
| rc = cras_iodev_list_add_output(&d3_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| d2_.format = &fmt_; |
| d3_.format = &fmt_; |
| |
| audio_thread_add_open_dev_called = 0; |
| cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d3_.info.idx, 1)); |
| ASSERT_EQ(audio_thread_add_open_dev_called, 0); |
| ASSERT_EQ(audio_thread_rm_open_dev_called, 0); |
| |
| // If a stream is added, the device should be opened. |
| stream_add_cb(&rstream); |
| ASSERT_EQ(audio_thread_add_open_dev_called, 1); |
| audio_thread_rm_open_dev_called = 0; |
| audio_thread_drain_stream_return = 10; |
| stream_rm_cb(&rstream); |
| ASSERT_EQ(audio_thread_drain_stream_called, 1); |
| ASSERT_EQ(audio_thread_rm_open_dev_called, 0); |
| audio_thread_drain_stream_return = 0; |
| clock_gettime_retspec.tv_sec = 15; |
| clock_gettime_retspec.tv_nsec = 45; |
| stream_rm_cb(&rstream); |
| ASSERT_EQ(audio_thread_drain_stream_called, 2); |
| ASSERT_EQ(0, audio_thread_rm_open_dev_called); |
| // Stream should remain open for a while before being closed. |
| // Test it is closed after 30 seconds. |
| clock_gettime_retspec.tv_sec += 30; |
| cras_tm_timer_cb(NULL, NULL); |
| ASSERT_EQ(1, audio_thread_rm_open_dev_called); |
| |
| audio_thread_rm_open_dev_called = 0; |
| cras_iodev_list_rm_output(&d3_); |
| ASSERT_EQ(audio_thread_rm_open_dev_called, 0); |
| |
| /* Assert active devices was set to default one, when selected device |
| * removed. */ |
| cras_iodev_list_rm_output(&d1_); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, OutputDevIdleClose) { |
| int rc; |
| struct cras_rstream rstream; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| EXPECT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| |
| audio_thread_add_open_dev_called = 0; |
| cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| EXPECT_EQ(0, audio_thread_add_open_dev_called); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| |
| // If a stream is added, the device should be opened. |
| stream_add_cb(&rstream); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| |
| audio_thread_rm_open_dev_called = 0; |
| audio_thread_drain_stream_return = 0; |
| clock_gettime_retspec.tv_sec = 15; |
| stream_rm_cb(&rstream); |
| EXPECT_EQ(1, audio_thread_drain_stream_called); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| EXPECT_EQ(1, cras_tm_create_timer_called); |
| |
| // Expect no rm dev happen because idle time not yet expire, and |
| // new timer should be scheduled for the rest of the idle time. |
| clock_gettime_retspec.tv_sec += 7; |
| cras_tm_timer_cb(NULL, NULL); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| |
| // Expect d1_ be closed upon unplug, and the timer stay armed. |
| cras_iodev_list_rm_output(&d1_); |
| EXPECT_EQ(1, audio_thread_rm_open_dev_called); |
| EXPECT_EQ(0, cras_tm_cancel_timer_called); |
| |
| // When timer eventually fired expect there's no more new |
| // timer scheduled because d1_ has closed already. |
| clock_gettime_retspec.tv_sec += 4; |
| cras_tm_timer_cb(NULL, NULL); |
| EXPECT_EQ(2, cras_tm_create_timer_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, DrainTimerCancel) { |
| int rc; |
| struct cras_rstream rstream; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| EXPECT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| |
| audio_thread_add_open_dev_called = 0; |
| cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| EXPECT_EQ(0, audio_thread_add_open_dev_called); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| |
| // If a stream is added, the device should be opened. |
| stream_add_cb(&rstream); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| |
| audio_thread_rm_open_dev_called = 0; |
| audio_thread_drain_stream_return = 0; |
| clock_gettime_retspec.tv_sec = 15; |
| clock_gettime_retspec.tv_nsec = 45; |
| stream_rm_cb(&rstream); |
| EXPECT_EQ(1, audio_thread_drain_stream_called); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| |
| // Add stream again, make sure device isn't closed after timeout. |
| audio_thread_add_open_dev_called = 0; |
| stream_add_cb(&rstream); |
| EXPECT_EQ(0, audio_thread_add_open_dev_called); |
| |
| clock_gettime_retspec.tv_sec += 30; |
| cras_tm_timer_cb(NULL, NULL); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| |
| // Remove stream, and check the device is eventually closed. |
| audio_thread_rm_open_dev_called = 0; |
| audio_thread_drain_stream_called = 0; |
| stream_rm_cb(&rstream); |
| EXPECT_EQ(1, audio_thread_drain_stream_called); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| |
| clock_gettime_retspec.tv_sec += 30; |
| cras_tm_timer_cb(NULL, NULL); |
| EXPECT_EQ(1, audio_thread_rm_open_dev_called); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, RemoveThenSelectActiveNode) { |
| int rc; |
| cras_node_id_t id; |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| d2_.direction = CRAS_STREAM_OUTPUT; |
| |
| /* d1_ will be the default_output */ |
| rc = cras_iodev_list_add_output(&d1_); |
| ASSERT_EQ(0, rc); |
| rc = cras_iodev_list_add_output(&d2_); |
| ASSERT_EQ(0, rc); |
| |
| /* Test the scenario that the selected active output removed |
| * from active dev list, should be able to select back again. */ |
| id = cras_make_node_id(d2_.info.idx, 1); |
| |
| cras_iodev_list_rm_active_node(CRAS_STREAM_OUTPUT, id); |
| ASSERT_EQ(audio_thread_rm_open_dev_called, 0); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, CloseDevWithPinnedStream) { |
| int rc; |
| struct cras_rstream rstream1, rstream2; |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| d1_.info.idx = 1; |
| rc = cras_iodev_list_add_output(&d1_); |
| EXPECT_EQ(0, rc); |
| |
| memset(&rstream1, 0, sizeof(rstream1)); |
| memset(&rstream2, 0, sizeof(rstream2)); |
| rstream2.is_pinned = 1; |
| rstream2.pinned_dev_idx = d1_.info.idx; |
| |
| d1_.format = &fmt_; |
| audio_thread_add_open_dev_called = 0; |
| EXPECT_EQ(0, audio_thread_add_open_dev_called); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| |
| // Add a normal stream |
| stream_add_cb(&rstream1); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| |
| // Add a pinned stream, expect another dev open call triggered. |
| cras_iodev_open_called = 0; |
| stream_add_cb(&rstream2); |
| EXPECT_EQ(1, cras_iodev_open_called); |
| |
| // Force disable d1_ and make sure d1_ gets closed. |
| audio_thread_rm_open_dev_called = 0; |
| update_active_node_called = 0; |
| cras_iodev_close_called = 0; |
| cras_iodev_list_disable_dev(&d1_, 1); |
| EXPECT_EQ(1, audio_thread_rm_open_dev_called); |
| EXPECT_EQ(1, cras_iodev_close_called); |
| EXPECT_EQ(&d1_, cras_iodev_close_dev); |
| EXPECT_EQ(1, update_active_node_called); |
| |
| // Add back the two streams, one normal one pinned. |
| audio_thread_add_open_dev_called = 0; |
| audio_thread_rm_open_dev_called = 0; |
| cras_iodev_open_called = 0; |
| stream_add_cb(&rstream2); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| EXPECT_EQ(1, cras_iodev_open_called); |
| stream_add_cb(&rstream1); |
| |
| // Suspend d1_ and make sure d1_ gets closed. |
| update_active_node_called = 0; |
| cras_iodev_close_called = 0; |
| cras_iodev_list_suspend_dev(d1_.info.idx); |
| EXPECT_EQ(1, audio_thread_rm_open_dev_called); |
| EXPECT_EQ(1, cras_iodev_close_called); |
| EXPECT_EQ(&d1_, cras_iodev_close_dev); |
| EXPECT_EQ(1, update_active_node_called); |
| |
| cras_iodev_list_resume_dev(d1_.info.idx); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, DisableDevWithPinnedStream) { |
| int rc; |
| struct cras_rstream rstream1; |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| rc = cras_iodev_list_add_output(&d1_); |
| EXPECT_EQ(0, rc); |
| |
| memset(&rstream1, 0, sizeof(rstream1)); |
| rstream1.is_pinned = 1; |
| rstream1.pinned_dev_idx = d1_.info.idx; |
| |
| d1_.format = &fmt_; |
| audio_thread_add_open_dev_called = 0; |
| cras_iodev_list_add_active_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| EXPECT_EQ(0, audio_thread_add_open_dev_called); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| |
| // Add a pinned stream. |
| cras_iodev_open_called = 0; |
| stream_add_cb(&rstream1); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| EXPECT_EQ(1, cras_iodev_open_called); |
| |
| // Disable d1_ expect no close dev triggered because pinned stream. |
| stream_list_has_pinned_stream_ret[d1_.info.idx] = 1; |
| audio_thread_rm_open_dev_called = 0; |
| update_active_node_called = 0; |
| cras_iodev_close_called = 0; |
| cras_iodev_list_disable_dev(&d1_, 0); |
| EXPECT_EQ(0, audio_thread_rm_open_dev_called); |
| EXPECT_EQ(0, cras_iodev_close_called); |
| EXPECT_EQ(0, update_active_node_called); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, AddRemovePinnedStream) { |
| struct cras_rstream rstream; |
| |
| cras_iodev_list_init(); |
| |
| // Add 2 output devices. |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| d1_.info.idx = 1; |
| EXPECT_EQ(0, cras_iodev_list_add_output(&d1_)); |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d1_.info.idx, 0)); |
| EXPECT_EQ(2, update_active_node_called); |
| EXPECT_EQ(&d1_, update_active_node_iodev_val[0]); |
| |
| d2_.direction = CRAS_STREAM_OUTPUT; |
| d2_.info.idx = 2; |
| EXPECT_EQ(0, cras_iodev_list_add_output(&d2_)); |
| |
| d1_.format = &fmt_; |
| d2_.format = &fmt_; |
| |
| // Setup pinned stream. |
| memset(&rstream, 0, sizeof(rstream)); |
| rstream.is_pinned = 1; |
| rstream.pinned_dev_idx = d1_.info.idx; |
| |
| // Add pinned stream to d1. |
| update_active_node_called = 0; |
| EXPECT_EQ(0, stream_add_cb(&rstream)); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(&d1_, audio_thread_add_stream_dev); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| EXPECT_EQ(1, update_active_node_called); |
| // Init d1_ because of pinned stream |
| EXPECT_EQ(&d1_, update_active_node_iodev_val[0]); |
| |
| // Select d2, check pinned stream is not added to d2. |
| update_active_node_called = 0; |
| stream_list_has_pinned_stream_ret[d1_.info.idx] = 1; |
| cras_iodev_list_select_node(CRAS_STREAM_OUTPUT, |
| cras_make_node_id(d2_.info.idx, 0)); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(2, update_active_node_called); |
| // Unselect d1_ and select to d2_ |
| EXPECT_EQ(&d2_, update_active_node_iodev_val[0]); |
| EXPECT_EQ(&mock_empty_iodev[CRAS_STREAM_OUTPUT], |
| update_active_node_iodev_val[1]); |
| |
| // Remove pinned stream from d1, check d1 is closed after stream removed. |
| update_active_node_called = 0; |
| stream_list_has_pinned_stream_ret[d1_.info.idx] = 0; |
| EXPECT_EQ(0, stream_rm_cb(&rstream)); |
| EXPECT_EQ(1, cras_iodev_close_called); |
| EXPECT_EQ(&d1_, cras_iodev_close_dev); |
| EXPECT_EQ(1, update_active_node_called); |
| // close pinned device |
| EXPECT_EQ(&d1_, update_active_node_iodev_val[0]); |
| |
| // Assume dev is already opened, add pin stream should not trigger another |
| // update_active_node call, but will trigger audio_thread_add_stream. |
| audio_thread_is_dev_open_ret = 1; |
| update_active_node_called = 0; |
| EXPECT_EQ(0, stream_add_cb(&rstream)); |
| EXPECT_EQ(0, update_active_node_called); |
| EXPECT_EQ(2, audio_thread_add_stream_called); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, SuspendResumePinnedStream) { |
| struct cras_rstream rstream; |
| |
| cras_iodev_list_init(); |
| |
| // Add 2 output devices. |
| d1_.direction = CRAS_STREAM_OUTPUT; |
| EXPECT_EQ(0, cras_iodev_list_add_output(&d1_)); |
| d2_.direction = CRAS_STREAM_OUTPUT; |
| EXPECT_EQ(0, cras_iodev_list_add_output(&d2_)); |
| |
| d1_.format = &fmt_; |
| d2_.format = &fmt_; |
| |
| // Setup pinned stream. |
| memset(&rstream, 0, sizeof(rstream)); |
| rstream.is_pinned = 1; |
| rstream.pinned_dev_idx = d1_.info.idx; |
| |
| // Add pinned stream to d1. |
| EXPECT_EQ(0, stream_add_cb(&rstream)); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(&d1_, audio_thread_add_stream_dev); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| |
| DL_APPEND(stream_list_get_ret, &rstream); |
| |
| // Test for suspend |
| |
| // Device state enters no_stream after stream is disconnected. |
| d1_.state = CRAS_IODEV_STATE_NO_STREAM_RUN; |
| // Device has no pinned stream now. But this pinned stream remains in |
| // stream_list. |
| stream_list_has_pinned_stream_ret[d1_.info.idx] = 0; |
| |
| // Suspend |
| observer_ops->suspend_changed(NULL, 1); |
| |
| // Verify that stream is disconnected and d1 is closed. |
| EXPECT_EQ(1, audio_thread_disconnect_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream); |
| EXPECT_EQ(1, cras_iodev_close_called); |
| EXPECT_EQ(&d1_, cras_iodev_close_dev); |
| |
| // Test for resume |
| cras_iodev_open_called = 0; |
| audio_thread_add_stream_called = 0; |
| audio_thread_add_stream_stream = NULL; |
| d1_.state = CRAS_IODEV_STATE_CLOSE; |
| |
| // Resume |
| observer_ops->suspend_changed(NULL, 0); |
| |
| // Verify that device is opened and stream is attached to the device. |
| EXPECT_EQ(1, cras_iodev_open_called); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, HotwordStreamsAddedThenSuspendResume) { |
| struct cras_rstream rstream; |
| struct cras_rstream* stream_list = NULL; |
| cras_iodev_list_init(); |
| |
| node1.type = CRAS_NODE_TYPE_HOTWORD; |
| d1_.direction = CRAS_STREAM_INPUT; |
| EXPECT_EQ(0, cras_iodev_list_add_input(&d1_)); |
| |
| d1_.format = &fmt_; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| rstream.is_pinned = 1; |
| rstream.pinned_dev_idx = d1_.info.idx; |
| rstream.flags = HOTWORD_STREAM; |
| |
| /* Add a hotword stream. */ |
| EXPECT_EQ(0, stream_add_cb(&rstream)); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(&d1_, audio_thread_add_stream_dev); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| |
| DL_APPEND(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| |
| /* Suspend hotword streams, verify the existing stream disconnects |
| * from the hotword device and connects to the empty iodev. */ |
| EXPECT_EQ(0, cras_iodev_list_suspend_hotword_streams()); |
| EXPECT_EQ(1, audio_thread_disconnect_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream); |
| EXPECT_EQ(&d1_, audio_thread_disconnect_stream_dev); |
| EXPECT_EQ(2, audio_thread_add_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| EXPECT_EQ(&mock_hotword_iodev, audio_thread_add_stream_dev); |
| |
| /* Resume hotword streams, verify the stream disconnects from |
| * the empty iodev and connects back to the real hotword iodev. */ |
| EXPECT_EQ(0, cras_iodev_list_resume_hotword_stream()); |
| EXPECT_EQ(2, audio_thread_disconnect_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream); |
| EXPECT_EQ(&mock_hotword_iodev, audio_thread_disconnect_stream_dev); |
| EXPECT_EQ(3, audio_thread_add_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| EXPECT_EQ(&d1_, audio_thread_add_stream_dev); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, HotwordStreamsAddedAfterSuspend) { |
| struct cras_rstream rstream; |
| struct cras_rstream* stream_list = NULL; |
| cras_iodev_list_init(); |
| |
| node1.type = CRAS_NODE_TYPE_HOTWORD; |
| d1_.direction = CRAS_STREAM_INPUT; |
| EXPECT_EQ(0, cras_iodev_list_add_input(&d1_)); |
| |
| d1_.format = &fmt_; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| rstream.is_pinned = 1; |
| rstream.pinned_dev_idx = d1_.info.idx; |
| rstream.flags = HOTWORD_STREAM; |
| |
| /* Suspends hotword streams before a stream connected. */ |
| EXPECT_EQ(0, cras_iodev_list_suspend_hotword_streams()); |
| EXPECT_EQ(0, audio_thread_disconnect_stream_called); |
| EXPECT_EQ(0, audio_thread_add_stream_called); |
| |
| DL_APPEND(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| |
| /* Hotword stream connected, verify it is added to the empty iodev. */ |
| EXPECT_EQ(0, stream_add_cb(&rstream)); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(&mock_hotword_iodev, audio_thread_add_stream_dev); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| |
| /* Resume hotword streams, now the existing hotword stream should disconnect |
| * from the empty iodev and connect to the real hotword iodev. */ |
| EXPECT_EQ(0, cras_iodev_list_resume_hotword_stream()); |
| EXPECT_EQ(1, audio_thread_disconnect_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream); |
| EXPECT_EQ(&mock_hotword_iodev, audio_thread_disconnect_stream_dev); |
| EXPECT_EQ(2, audio_thread_add_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| EXPECT_EQ(&d1_, audio_thread_add_stream_dev); |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, GetSCOPCMIodevs) { |
| cras_iodev_list_init(); |
| |
| fake_sco_in_dev.direction = CRAS_STREAM_INPUT; |
| fake_sco_in_node.is_sco_pcm = 1; |
| cras_iodev_list_add_input(&fake_sco_in_dev); |
| fake_sco_out_dev.direction = CRAS_STREAM_OUTPUT; |
| fake_sco_out_node.is_sco_pcm = 1; |
| cras_iodev_list_add_output(&fake_sco_out_dev); |
| |
| EXPECT_EQ(&fake_sco_in_dev, |
| cras_iodev_list_get_sco_pcm_iodev(CRAS_STREAM_INPUT)); |
| EXPECT_EQ(&fake_sco_out_dev, |
| cras_iodev_list_get_sco_pcm_iodev(CRAS_STREAM_OUTPUT)); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, HotwordStreamsPausedAtSystemSuspend) { |
| struct cras_rstream rstream; |
| struct cras_rstream* stream_list = NULL; |
| cras_iodev_list_init(); |
| |
| node1.type = CRAS_NODE_TYPE_HOTWORD; |
| d1_.direction = CRAS_STREAM_INPUT; |
| EXPECT_EQ(0, cras_iodev_list_add_input(&d1_)); |
| |
| d1_.format = &fmt_; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| rstream.is_pinned = 1; |
| rstream.pinned_dev_idx = d1_.info.idx; |
| rstream.flags = HOTWORD_STREAM; |
| |
| /* Add a hotword stream. */ |
| EXPECT_EQ(0, stream_add_cb(&rstream)); |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(&d1_, audio_thread_add_stream_dev); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| |
| DL_APPEND(stream_list, &rstream); |
| stream_list_get_ret = stream_list; |
| |
| server_state_hotword_pause_at_suspend = 1; |
| |
| /* Trigger system suspend. Verify hotword stream is moved to empty dev. */ |
| observer_ops->suspend_changed(NULL, 1); |
| EXPECT_EQ(1, audio_thread_disconnect_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream); |
| EXPECT_EQ(&d1_, audio_thread_disconnect_stream_dev); |
| EXPECT_EQ(2, audio_thread_add_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| EXPECT_EQ(&mock_hotword_iodev, audio_thread_add_stream_dev); |
| |
| /* Trigger system resume. Verify hotword stream is moved to real dev.*/ |
| observer_ops->suspend_changed(NULL, 0); |
| EXPECT_EQ(2, audio_thread_disconnect_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_disconnect_stream_stream); |
| EXPECT_EQ(&mock_hotword_iodev, audio_thread_disconnect_stream_dev); |
| EXPECT_EQ(3, audio_thread_add_stream_called); |
| EXPECT_EQ(&rstream, audio_thread_add_stream_stream); |
| EXPECT_EQ(&d1_, audio_thread_add_stream_dev); |
| |
| server_state_hotword_pause_at_suspend = 0; |
| audio_thread_disconnect_stream_called = 0; |
| audio_thread_add_stream_called = 0; |
| |
| /* Trigger system suspend. Verify hotword stream is not touched. */ |
| observer_ops->suspend_changed(NULL, 1); |
| EXPECT_EQ(0, audio_thread_disconnect_stream_called); |
| EXPECT_EQ(0, audio_thread_add_stream_called); |
| |
| /* Trigger system resume. Verify hotword stream is not touched.*/ |
| observer_ops->suspend_changed(NULL, 0); |
| EXPECT_EQ(0, audio_thread_disconnect_stream_called); |
| EXPECT_EQ(0, audio_thread_add_stream_called); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| TEST_F(IoDevTestSuite, SetNoiseCancellation) { |
| struct cras_rstream rstream; |
| struct cras_rstream* stream_list = NULL; |
| int rc; |
| |
| memset(&rstream, 0, sizeof(rstream)); |
| |
| cras_iodev_list_init(); |
| |
| d1_.direction = CRAS_STREAM_INPUT; |
| rc = cras_iodev_list_add_input(&d1_); |
| ASSERT_EQ(0, rc); |
| |
| d1_.format = &fmt_; |
| |
| rstream.direction = CRAS_STREAM_INPUT; |
| |
| audio_thread_add_open_dev_called = 0; |
| audio_thread_rm_open_dev_called = 0; |
| cras_iodev_list_add_active_node(CRAS_STREAM_INPUT, |
| cras_make_node_id(d1_.info.idx, 1)); |
| DL_APPEND(stream_list, &rstream); |
| stream_add_cb(&rstream); |
| stream_list_get_ret = stream_list; |
| EXPECT_EQ(1, audio_thread_add_stream_called); |
| EXPECT_EQ(1, audio_thread_add_open_dev_called); |
| |
| // reset_for_noise_cancellation causes device suspend & resume |
| // While suspending d1_: rm d1_, open fallback |
| // While resuming d1_: rm fallback, open d1_ |
| cras_iodev_list_reset_for_noise_cancellation(); |
| EXPECT_EQ(3, audio_thread_add_open_dev_called); |
| EXPECT_EQ(2, audio_thread_rm_open_dev_called); |
| |
| cras_iodev_list_deinit(); |
| } |
| |
| } // namespace |
| |
| int main(int argc, char** argv) { |
| ::testing::InitGoogleTest(&argc, argv); |
| return RUN_ALL_TESTS(); |
| } |
| |
| extern "C" { |
| |
| // Stubs |
| struct main_thread_event_log* main_log; |
| |
| struct cras_server_state* cras_system_state_update_begin() { |
| return server_state_update_begin_return; |
| } |
| |
| void cras_system_state_update_complete() {} |
| |
| int cras_system_get_mute() { |
| return system_get_mute_return; |
| } |
| |
| bool cras_system_get_noise_cancellation_enabled() { |
| return false; |
| } |
| |
| struct audio_thread* audio_thread_create() { |
| return &thread; |
| } |
| |
| int audio_thread_start(struct audio_thread* thread) { |
| return 0; |
| } |
| |
| void audio_thread_destroy(struct audio_thread* thread) {} |
| |
| int audio_thread_set_active_dev(struct audio_thread* thread, |
| struct cras_iodev* dev) { |
| audio_thread_set_active_dev_called++; |
| audio_thread_set_active_dev_val = dev; |
| return 0; |
| } |
| |
| void audio_thread_remove_streams(struct audio_thread* thread, |
| enum CRAS_STREAM_DIRECTION dir) { |
| audio_thread_remove_streams_active_dev = audio_thread_set_active_dev_val; |
| } |
| |
| int audio_thread_add_open_dev(struct audio_thread* thread, |
| struct cras_iodev* dev) { |
| audio_thread_add_open_dev_dev = dev; |
| audio_thread_add_open_dev_called++; |
| return 0; |
| } |
| |
| int audio_thread_rm_open_dev(struct audio_thread* thread, |
| enum CRAS_STREAM_DIRECTION dir, |
| unsigned int dev_idx) { |
| audio_thread_rm_open_dev_called++; |
| return 0; |
| } |
| |
| int audio_thread_is_dev_open(struct audio_thread* thread, |
| struct cras_iodev* dev) { |
| return audio_thread_is_dev_open_ret; |
| } |
| |
| int audio_thread_add_stream(struct audio_thread* thread, |
| struct cras_rstream* stream, |
| struct cras_iodev** devs, |
| unsigned int num_devs) { |
| audio_thread_add_stream_called++; |
| audio_thread_add_stream_stream = stream; |
| audio_thread_add_stream_dev = (num_devs ? devs[0] : NULL); |
| return 0; |
| } |
| |
| int audio_thread_disconnect_stream(struct audio_thread* thread, |
| struct cras_rstream* stream, |
| struct cras_iodev* iodev) { |
| audio_thread_disconnect_stream_called++; |
| audio_thread_disconnect_stream_stream = stream; |
| audio_thread_disconnect_stream_dev = iodev; |
| return 0; |
| } |
| |
| int audio_thread_drain_stream(struct audio_thread* thread, |
| struct cras_rstream* stream) { |
| audio_thread_drain_stream_called++; |
| return audio_thread_drain_stream_return; |
| } |
| |
| struct cras_iodev* empty_iodev_create(enum CRAS_STREAM_DIRECTION direction, |
| enum CRAS_NODE_TYPE node_type) { |
| struct cras_iodev* dev; |
| if (node_type == CRAS_NODE_TYPE_HOTWORD) { |
| dev = &mock_hotword_iodev; |
| } else { |
| dev = &mock_empty_iodev[direction]; |
| } |
| dev->direction = direction; |
| if (dev->active_node == NULL) { |
| struct cras_ionode* node = (struct cras_ionode*)calloc(1, sizeof(*node)); |
| node->type = node_type; |
| dev->active_node = node; |
| } |
| return dev; |
| } |
| |
| void empty_iodev_destroy(struct cras_iodev* iodev) { |
| if (iodev->active_node) { |
| free(iodev->active_node); |
| iodev->active_node = NULL; |
| } |
| } |
| |
| struct cras_iodev* test_iodev_create(enum CRAS_STREAM_DIRECTION direction, |
| enum TEST_IODEV_TYPE type) { |
| return NULL; |
| } |
| |
| void test_iodev_command(struct cras_iodev* iodev, |
| enum CRAS_TEST_IODEV_CMD command, |
| unsigned int data_len, |
| const uint8_t* data) {} |
| |
| struct cras_iodev* loopback_iodev_create(enum CRAS_LOOPBACK_TYPE type) { |
| return &loopback_input; |
| } |
| |
| void loopback_iodev_destroy(struct cras_iodev* iodev) {} |
| |
| int cras_iodev_open(struct cras_iodev* iodev, |
| unsigned int cb_level, |
| const struct cras_audio_format* fmt) { |
| if (cras_iodev_open_ret[cras_iodev_open_called] == 0) |
| iodev->state = CRAS_IODEV_STATE_OPEN; |
| cras_iodev_open_fmt = *fmt; |
| iodev->format = &cras_iodev_open_fmt; |
| return cras_iodev_open_ret[cras_iodev_open_called++]; |
| } |
| |
| int cras_iodev_close(struct cras_iodev* iodev) { |
| iodev->state = CRAS_IODEV_STATE_CLOSE; |
| cras_iodev_close_called++; |
| cras_iodev_close_dev = iodev; |
| iodev->format = NULL; |
| return 0; |
| } |
| |
| int cras_iodev_set_format(struct cras_iodev* iodev, |
| const struct cras_audio_format* fmt) { |
| return 0; |
| } |
| |
| int cras_iodev_set_mute(struct cras_iodev* iodev) { |
| set_mute_called++; |
| set_mute_dev_vector.push_back(iodev); |
| return 0; |
| } |
| |
| void cras_iodev_set_node_plugged(struct cras_ionode* node, int plugged) { |
| set_node_plugged_called++; |
| } |
| |
| bool cras_iodev_support_noise_cancellation(const struct cras_iodev* iodev) { |
| return true; |
| } |
| |
| int cras_iodev_start_volume_ramp(struct cras_iodev* odev, |
| unsigned int old_volume, |
| unsigned int new_volume) { |
| cras_iodev_start_volume_ramp_called++; |
| return 0; |
| } |
| bool cras_iodev_is_aec_use_case(const struct cras_ionode* node) { |
| return 1; |
| } |
| bool stream_list_has_pinned_stream(struct stream_list* list, |
| unsigned int dev_idx) { |
| return stream_list_has_pinned_stream_ret[dev_idx]; |
| } |
| |
| struct stream_list* stream_list_create(stream_callback* add_cb, |
| stream_callback* rm_cb, |
| stream_create_func* create_cb, |
| stream_destroy_func* destroy_cb, |
| struct cras_tm* timer_manager) { |
| stream_add_cb = add_cb; |
| stream_rm_cb = rm_cb; |
| return reinterpret_cast<stream_list*>(0xf00); |
| } |
| |
| void stream_list_destroy(struct stream_list* list) {} |
| |
| struct cras_rstream* stream_list_get(struct stream_list* list) { |
| return stream_list_get_ret; |
| } |
| void server_stream_create(struct stream_list* stream_list, |
| unsigned int dev_idx) { |
| server_stream_create_called++; |
| } |
| void server_stream_destroy(struct stream_list* stream_list, |
| unsigned int dev_idx) { |
| server_stream_destroy_called++; |
| } |
| |
| int cras_rstream_create(struct cras_rstream_config* config, |
| struct cras_rstream** stream_out) { |
| return 0; |
| } |
| |
| void cras_rstream_destroy(struct cras_rstream* rstream) {} |
| |
| struct cras_tm* cras_system_state_get_tm() { |
| return NULL; |
| } |
| |
| 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_timer_cb = cb; |
| cras_tm_timer_cb_data = cb_data; |
| cras_tm_create_timer_called++; |
| return reinterpret_cast<struct cras_timer*>(0x404); |
| } |
| |
| void cras_tm_cancel_timer(struct cras_tm* tm, struct cras_timer* t) { |
| cras_tm_cancel_timer_called++; |
| } |
| |
| void cras_fmt_conv_destroy(struct cras_fmt_conv* conv) {} |
| |
| struct cras_fmt_conv* cras_channel_remix_conv_create(unsigned int num_channels, |
| const float* coefficient) { |
| return NULL; |
| } |
| |
| void cras_channel_remix_convert(struct cras_fmt_conv* conv, |
| uint8_t* in_buf, |
| size_t frames) {} |
| |
| struct cras_observer_client* cras_observer_add( |
| const struct cras_observer_ops* ops, |
| void* context) { |
| observer_ops = (struct cras_observer_ops*)calloc(1, sizeof(*ops)); |
| memcpy(observer_ops, ops, sizeof(*ops)); |
| cras_observer_add_called++; |
| return reinterpret_cast<struct cras_observer_client*>(0x55); |
| } |
| |
| void cras_observer_remove(struct cras_observer_client* client) { |
| if (observer_ops) |
| free(observer_ops); |
| cras_observer_remove_called++; |
| } |
| |
| void cras_observer_notify_nodes(void) { |
| cras_observer_notify_nodes_called++; |
| } |
| |
| void cras_observer_notify_active_node(enum CRAS_STREAM_DIRECTION direction, |
| cras_node_id_t node_id) { |
| cras_observer_notify_active_node_called++; |
| } |
| |
| void cras_observer_notify_output_node_volume(cras_node_id_t node_id, |
| int32_t volume) { |
| cras_observer_notify_output_node_volume_called++; |
| } |
| |
| void cras_observer_notify_node_left_right_swapped(cras_node_id_t node_id, |
| int swapped) { |
| cras_observer_notify_node_left_right_swapped_called++; |
| } |
| |
| void cras_observer_notify_input_node_gain(cras_node_id_t node_id, |
| int32_t gain) { |
| cras_observer_notify_input_node_gain_called++; |
| } |
| |
| int audio_thread_dev_start_ramp(struct audio_thread* thread, |
| unsigned int dev_idx, |
| enum CRAS_IODEV_RAMP_REQUEST request) { |
| audio_thread_dev_start_ramp_called++; |
| audio_thread_dev_start_ramp_dev_vector.push_back(dev_idx); |
| audio_thread_dev_start_ramp_req = request; |
| return 0; |
| } |
| |
| #ifdef HAVE_WEBRTC_APM |
| struct cras_apm* cras_apm_list_add_apm(struct cras_apm_list* list, |
| void* dev_ptr, |
| const struct cras_audio_format* fmt, |
| bool is_internal_dev) { |
| return NULL; |
| } |
| void cras_apm_list_remove_apm(struct cras_apm_list* list, void* dev_ptr) {} |
| int cras_apm_list_init(const char* device_config_dir) { |
| return 0; |
| } |
| #endif |
| |
| // From librt. |
| int clock_gettime(clockid_t clk_id, struct timespec* tp) { |
| tp->tv_sec = clock_gettime_retspec.tv_sec; |
| tp->tv_nsec = clock_gettime_retspec.tv_nsec; |
| return 0; |
| } |
| |
| bool cras_system_get_hotword_pause_at_suspend() { |
| return !!server_state_hotword_pause_at_suspend; |
| } |
| |
| } // extern "C" |