// 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 #include #include #include 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 set_mute_dev_vector; static std::vector 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 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; int dev_idx_in_vector(std::vector v, unsigned int idx) { return std::find(v.begin(), v.end(), idx) != v.end(); } int device_in_vector(std::vector 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; } 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(1) << idx))); found_mask |= (static_cast(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(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(); } } // 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; } 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++; } 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(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(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(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; } } // extern "C"