/* * test-alsa-pcm.c * SPDX-FileCopyrightText: 2016-2025 BlueALSA developers * SPDX-License-Identifier: MIT */ #if HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "shared/defs.h" #include "shared/log.h" #include "shared/rt.h" #include "inc/check.inc" #include "inc/mock.inc" #include "inc/preload.inc" #include "inc/sine.inc" #include "inc/spawn.inc" #define dumprv(fn) fprintf(stderr, #fn " = %d\n", (int)fn) static const char *pcm_device = NULL; static const char *pcm_hwcompat = NULL; static unsigned int pcm_channels = 2; static unsigned int pcm_rate = 44100; static snd_pcm_format_t pcm_format = SND_PCM_FORMAT_S16_LE; /* big enough buffer to keep one period of data */ static int16_t pcm_buffer[1024 * 8]; static int set_hw_params(snd_pcm_t *pcm, snd_pcm_format_t format, int channels, int rate, unsigned int *buffer_time, unsigned int *period_time) { snd_pcm_hw_params_t *params; int dir; int err; snd_pcm_hw_params_alloca(¶ms); snd_pcm_hw_params_any(pcm, params); if ((err = snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED)) != 0) { error("snd_pcm_hw_params_set_access: %s", snd_strerror(err)); goto fail; } if ((err = snd_pcm_hw_params_set_format(pcm, params, format)) != 0) { error("snd_pcm_hw_params_set_format: %s", snd_strerror(err)); goto fail; } if ((err = snd_pcm_hw_params_set_channels(pcm, params, channels)) != 0) { error("snd_pcm_hw_params_set_channels: %s", snd_strerror(err)); goto fail; } if ((err = snd_pcm_hw_params_set_rate(pcm, params, rate, 0)) != 0) { error("snd_pcm_hw_params_set_rate: %s", snd_strerror(err)); goto fail; } dir = 0; if ((err = snd_pcm_hw_params_set_buffer_time_near(pcm, params, buffer_time, &dir)) != 0) { error("snd_pcm_hw_params_set_buffer_time_near: %s", snd_strerror(err)); goto fail; } dir = 0; if ((err = snd_pcm_hw_params_set_period_time_near(pcm, params, period_time, &dir)) != 0) { error("snd_pcm_hw_params_set_period_time_near: %s", snd_strerror(err)); goto fail; } if ((err = snd_pcm_hw_params(pcm, params)) != 0) { error("snd_pcm_hw_params: %s", snd_strerror(err)); goto fail; } debug("Selected PCM parameters: buffer time: %u us, period time: %u us", *buffer_time, *period_time); return 0; fail: if (pcm_device != NULL) error("List available HW parameters with: aplay -D %s --dump-hw-params /dev/zero", pcm_device); return err; } static int set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t buffer_size, snd_pcm_uframes_t period_size) { snd_pcm_sw_params_t *params; int err; snd_pcm_sw_params_alloca(¶ms); if ((err = snd_pcm_sw_params_current(pcm, params)) != 0) return err; /* start the transfer when the buffer is full (or almost full) */ snd_pcm_uframes_t threshold = (buffer_size / period_size) * period_size; if ((err = snd_pcm_sw_params_set_start_threshold(pcm, params, threshold)) != 0) return err; /* allow the transfer when at least period_size samples can be processed */ if ((err = snd_pcm_sw_params_set_avail_min(pcm, params, period_size)) != 0) return err; if ((err = snd_pcm_sw_params(pcm, params)) != 0) return err; return 0; } static int test_pcm_open(struct spawn_process *sp_ba_mock, snd_pcm_t **pcm, snd_pcm_stream_t stream) { if (pcm_device != NULL) return snd_pcm_open(pcm, pcm_device, stream, 0); const char *profile = NULL; if (stream == SND_PCM_STREAM_PLAYBACK) profile = "--profile=a2dp-source"; if (stream == SND_PCM_STREAM_CAPTURE) profile = "--profile=a2dp-sink"; if (spawn_bluealsa_mock(sp_ba_mock, NULL, true, "--timeout=1000", profile, NULL) == -1) return -1; char name[64]; const char *dev = "bluealsa:DEV=12:34:56:78:9A:BC"; snprintf(name, sizeof(name), "%s", dev); if (pcm_hwcompat != NULL) { snprintf(name, sizeof(name), "%s,HWCOMPAT=%s", dev, pcm_hwcompat); /* Allow time for capture stream to reach "Running" state. */ if (stream == SND_PCM_STREAM_CAPTURE && strcmp(pcm_hwcompat, "busy") == 0) usleep(100000); } return snd_pcm_open(pcm, name, stream, 0); } static int test_pcm_close(struct spawn_process *sp_ba_mock, snd_pcm_t *pcm) { int rv = 0; if (pcm != NULL) rv = snd_pcm_close(pcm); if (pcm_device != NULL) return rv; if (sp_ba_mock != NULL) { spawn_terminate(sp_ba_mock, 0); spawn_close(sp_ba_mock, NULL); } return rv; } static int16_t *test_sine_s16le(snd_pcm_uframes_t size) { static size_t x = 0; assert(ARRAYSIZE(pcm_buffer) >= size * pcm_channels); x = snd_pcm_sine_s16_2le(pcm_buffer, pcm_channels, size, 441.0 / pcm_rate, x); return pcm_buffer; } static snd_pcm_state_t snd_pcm_state_runtime(snd_pcm_t *pcm) { snd_pcm_status_t *status; snd_pcm_status_alloca(&status); int rv; if ((rv = snd_pcm_status(pcm, status)) != 0) return rv; return snd_pcm_status_get_state(status); } CK_START_TEST(dump_capture) { snd_output_t *output; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(snd_output_stdio_attach(&output, stdout, 0), 0); ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_CAPTURE), 0); ck_assert_int_eq(snd_pcm_dump(pcm, output), 0); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); ck_assert_int_eq(snd_output_close(output), 0); } CK_END_TEST CK_START_TEST(test_capture_start) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; snd_pcm_sframes_t delay; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_CAPTURE), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_get_params(pcm, &buffer_size, &period_size), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); /* Sleep a little time so that we can check that the PCM has not delivered * any samples before the start request. */ usleep(period_time); /* check capture PCM initial state - not running */ ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_PREPARED); ck_assert_int_eq(snd_pcm_avail(pcm), 0); /* if PCM is not started there should be no delay */ ck_assert_int_eq(snd_pcm_delay(pcm, &delay), 0); ck_assert_int_eq(delay, 0); /* start capture PCM without reading data */ ck_assert_int_eq(snd_pcm_start(pcm), 0); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_RUNNING); /* wait two and a half period time just to make sure that * at least two periods of frames should be available */ usleep(5 * period_time / 2); /* verify the amount of accumulated data */ ck_assert_int_ge(snd_pcm_avail(pcm), 2 * period_size); ck_assert_int_eq(snd_pcm_delay(pcm, &delay), 0); ck_assert_int_ge(delay, 2 * period_size); /* read few periods from capture PCM */ for (size_t i = 0; i < buffer_size / period_size; i++) ck_assert_int_eq(snd_pcm_readi(pcm, pcm_buffer, period_size), period_size); /* after reading there should be no more than one period of data in buffer */ snd_pcm_sframes_t avail; ck_assert_int_le((avail = snd_pcm_avail(pcm)), period_size); /* but there may be more data in the FIFO */ ck_assert_int_eq(snd_pcm_delay(pcm, &delay), 0); ck_assert_int_ge(delay, avail); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_capture_drain) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_CAPTURE), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); ck_assert_int_eq(snd_pcm_start(pcm), 0); /* drain PCM buffer and stop capture */ ck_assert_int_eq(snd_pcm_drain(pcm), 0); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_SETUP); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_capture_pause) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_CAPTURE), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_get_params(pcm, &buffer_size, &period_size), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); ck_assert_int_eq(snd_pcm_start(pcm), 0); /* wait one and a half period time just to make sure that * at least one period of frames should be available */ usleep(3 * period_time / 2); snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca(¶ms); ck_assert_int_eq(snd_pcm_hw_params_current(pcm, params), 0); if (!snd_pcm_hw_params_can_pause(params)) warn("PCM capture pause test: %s", "PCM can not pause"); else { /* pause capture */ ck_assert_int_eq(snd_pcm_pause(pcm, 1), 0); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_PAUSED); /* record PCM parameters just after pausing */ snd_pcm_sframes_t delay0, delay; snd_pcm_uframes_t frames0 = snd_pcm_avail(pcm); ck_assert_int_eq(snd_pcm_delay(pcm, &delay0), 0); /* wait some time */ usleep(buffer_time); /* during pause PCM parameters shall not be modified */ ck_assert_int_eq(snd_pcm_avail(pcm), frames0); ck_assert_int_eq(snd_pcm_delay(pcm, &delay), 0); ck_assert_int_eq(delay, delay0); /* resume capture */ ck_assert_int_eq(snd_pcm_pause(pcm, 0), 0); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_RUNNING); /* wait a little bit */ usleep(period_time + 5000); /* check resume: more available frames, bigger delay */ ck_assert_int_gt(snd_pcm_avail(pcm), frames0); ck_assert_int_eq(snd_pcm_delay(pcm, &delay), 0); ck_assert_int_gt(delay, delay0); } ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_capture_overrun) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_CAPTURE), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_get_params(pcm, &buffer_size, &period_size), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); ck_assert_int_eq(snd_pcm_start(pcm), 0); /* check that PCM is running and we can read from it */ ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_RUNNING); ck_assert_int_eq(snd_pcm_readi(pcm, pcm_buffer, period_size), period_size); /* allow overrun to occur */ usleep(buffer_time + period_time); /* check whether ALSA reports overrun */ ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_XRUN); ck_assert_int_eq(snd_pcm_avail(pcm), -EPIPE); /* recover from overrun */ ck_assert_int_eq(snd_pcm_prepare(pcm), 0); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_PREPARED); /* start capture PCM */ ck_assert_int_eq(snd_pcm_start(pcm), 0); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_RUNNING); /* make sure that PCM is indeed readable */ for (size_t i = 0; i < buffer_size / period_size; i++) ck_assert_int_eq(snd_pcm_readi(pcm, pcm_buffer, period_size), period_size); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_capture_poll) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_CAPTURE), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); struct pollfd pfds[8]; unsigned short revents; int count = snd_pcm_poll_descriptors_count(pcm); ck_assert_int_eq(snd_pcm_poll_descriptors(pcm, pfds, ARRAYSIZE(pfds)), count); int rv; ck_assert_int_eq(snd_pcm_prepare(pcm), 0); /* For a capture PCM just after prepare, the poll() call shall block * forever or at least the dispatched event shall be set to 0. */ for (;;) { ck_assert_int_ne(rv = poll(pfds, count, 750), -1); /* make sure that at some point poll() will actually block */ if (rv == 0) break; snd_pcm_poll_descriptors_revents(pcm, pfds, count, &revents); ck_assert_int_eq(revents, 0); } ck_assert_int_eq(snd_pcm_start(pcm), 0); do { /* started capture PCM shall not block forever */ ck_assert_int_gt(poll(pfds, count, -1), 0); snd_pcm_poll_descriptors_revents(pcm, pfds, count, &revents); } while (revents == 0); /* we should get read event flag set */ ck_assert_int_eq(revents & POLLIN, POLLIN); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(dump_playback) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_output_t *output; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(snd_output_stdio_attach(&output, stdout, 0), 0); ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(snd_pcm_dump(pcm, output), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca(¶ms); ck_assert_int_eq(snd_pcm_hw_params_current(pcm, params), 0); dumprv(snd_pcm_hw_params_can_disable_period_wakeup(params)); dumprv(snd_pcm_hw_params_can_mmap_sample_resolution(params)); dumprv(snd_pcm_hw_params_can_overrange(params)); dumprv(snd_pcm_hw_params_can_pause(params)); dumprv(snd_pcm_hw_params_can_resume(params)); dumprv(snd_pcm_hw_params_can_sync_start(params)); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); ck_assert_int_eq(snd_output_close(output), 0); } CK_END_TEST CK_START_TEST(ba_test_playback_hw_constraints) { if (pcm_device != NULL) return; /* hard-coded values used in the bluealsad-mock */ const unsigned int server_channels_min = 1; const unsigned int server_channels_max = 2; const unsigned int server_rate_min = 16000; const unsigned int server_rate_max = 48000; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_ne(spawn_bluealsa_mock(&sp_ba_mock, NULL, true, "--timeout=1000", "--profile=a2dp-source", NULL), -1); snd_config_t *top; ck_assert_int_ge(snd_config_top(&top), 0); const char *config = "pcm.ba-direct {\n" " type bluealsa\n" " device \"12:34:56:78:9A:BC\"\n" " profile \"a2dp\"\n" "}\n"; snd_input_t *input; ck_assert_int_eq(snd_input_buffer_open(&input, config, strlen(config)), 0); ck_assert_int_eq(snd_config_load(top, input), 0); ck_assert_int_eq(snd_pcm_open_lconf(&pcm, "ba-direct", SND_PCM_STREAM_PLAYBACK, 0, top), 0); snd_config_delete(top); snd_input_close(input); snd_pcm_hw_params_t *params; int d; snd_pcm_hw_params_alloca(¶ms); snd_pcm_hw_params_any(pcm, params); ck_assert_int_eq(snd_pcm_hw_params_test_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED), 0); ck_assert_int_eq(snd_pcm_hw_params_test_access(pcm, params, SND_PCM_ACCESS_MMAP_INTERLEAVED), 0); ck_assert_int_eq(snd_pcm_hw_params_set_access(pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED), 0); snd_pcm_format_t format; snd_pcm_hw_params_any(pcm, params); ck_assert_int_eq(snd_pcm_hw_params_set_format_first(pcm, params, &format), 0); ck_assert_int_eq(format, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_any(pcm, params); ck_assert_int_eq(snd_pcm_hw_params_set_format_last(pcm, params, &format), 0); ck_assert_int_eq(format, SND_PCM_FORMAT_S16_LE); unsigned int channels; snd_pcm_hw_params_any(pcm, params); ck_assert_int_eq(snd_pcm_hw_params_set_channels_first(pcm, params, &channels), 0); ck_assert_int_eq(channels, server_channels_min); snd_pcm_hw_params_any(pcm, params); ck_assert_int_eq(snd_pcm_hw_params_set_channels_last(pcm, params, &channels), 0); ck_assert_int_eq(channels, server_channels_max); unsigned int rate; snd_pcm_hw_params_any(pcm, params); ck_assert_int_eq(snd_pcm_hw_params_set_rate_first(pcm, params, &rate, &d), 0); ck_assert_int_eq(rate, server_rate_min); ck_assert_int_eq(d, 0); snd_pcm_hw_params_any(pcm, params); ck_assert_int_eq(snd_pcm_hw_params_set_rate_last(pcm, params, &rate, &d), 0); ck_assert_int_eq(rate, server_rate_max); ck_assert_int_eq(d, 0); unsigned int periods; snd_pcm_hw_params_any(pcm, params); ck_assert_int_eq(snd_pcm_hw_params_set_periods_first(pcm, params, &periods, &d), 0); ck_assert_int_eq(periods, 2); ck_assert_int_eq(d, 0); snd_pcm_hw_params_any(pcm, params); ck_assert_int_eq(snd_pcm_hw_params_set_periods_last(pcm, params, &periods, &d), 0); ck_assert_int_eq(periods, 1024); ck_assert_int_eq(d, 0); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(ba_test_playback_channel_maps) { if (pcm_device != NULL) return; unsigned int buffer_time = 200000; unsigned int period_time = 25000; struct spawn_process sp_ba_mock; snd_pcm_chmap_query_t **ch_maps; snd_pcm_chmap_t *ch_map; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); /* get all supported channel maps */ ck_assert_ptr_nonnull(ch_maps = snd_pcm_query_chmaps(pcm)); size_t ch_maps_count = 0; for (size_t i = 0; ch_maps[i] != NULL; i++) ch_maps_count++; /* SBC codec supports only mono and stereo */ ck_assert_uint_eq(ch_maps_count, 2); ck_assert_uint_eq(ch_maps[0]->type, SND_CHMAP_TYPE_FIXED); ck_assert_uint_eq(ch_maps[0]->map.channels, 1); ck_assert_uint_eq(ch_maps[0]->map.pos[0], SND_CHMAP_MONO); ck_assert_uint_eq(ch_maps[1]->type, SND_CHMAP_TYPE_FIXED); ck_assert_uint_eq(ch_maps[1]->map.channels, 2); ck_assert_uint_eq(ch_maps[1]->map.pos[0], SND_CHMAP_FL); ck_assert_uint_eq(ch_maps[1]->map.pos[1], SND_CHMAP_FR); /* get currently selected channel map */ ck_assert_ptr_nonnull(ch_map = snd_pcm_get_chmap(pcm)); /* stereo channel mode shall be selected by default */ ck_assert_uint_eq(ch_map->channels, 2); ck_assert_uint_eq(ch_map->pos[0], SND_CHMAP_FL); ck_assert_uint_eq(ch_map->pos[1], SND_CHMAP_FR); free(ch_map); snd_pcm_free_chmaps(ch_maps); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(ba_test_playback_no_codec_selected) { if (pcm_device != NULL) return; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_ne(spawn_bluealsa_mock(&sp_ba_mock, NULL, true, "--timeout=1000", "--profile=hfp-ag", NULL), -1); int rv = 0; #if ENABLE_HFP_CODEC_SELECTION rv = -EAGAIN; #endif ck_assert_int_eq(snd_pcm_open(&pcm, "bluealsa:DEV=12:34:56:78:9A:BC,PROFILE=sco", SND_PCM_STREAM_PLAYBACK, 0), rv); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(ba_test_playback_no_such_device) { if (pcm_device != NULL) return; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_ne(spawn_bluealsa_mock(&sp_ba_mock, "test", true, "--timeout=1000", NULL), -1); ck_assert_int_eq(snd_pcm_open(&pcm, "bluealsa:DEV=DE:AD:DE:AD:DE:AD,SRV=org.bluealsa.test", SND_PCM_STREAM_PLAYBACK, 0), -ENODEV); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(ba_test_playback_extra_setup) { if (pcm_device != NULL) return; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_ne(spawn_bluealsa_mock(&sp_ba_mock, NULL, true, "--timeout=1000", "--profile=a2dp-source", "--profile=hfp-ag", NULL), -1); ck_assert_int_eq(snd_pcm_open(&pcm, "bluealsa:DEV=12:34:56:78:9A:BC,CODEC=SBC", SND_PCM_STREAM_PLAYBACK, 0), 0); ck_assert_int_eq(test_pcm_close(NULL, pcm), 0); ck_assert_int_eq(snd_pcm_open(&pcm, "bluealsa:DEV=12:34:56:78:9A:BC,CODEC=SBC:ffff0822", SND_PCM_STREAM_PLAYBACK, 0), 0); ck_assert_int_eq(test_pcm_close(NULL, pcm), 0); ck_assert_int_eq(snd_pcm_open(&pcm, "bluealsa:DEV=12:34:56:78:9A:BC,PROFILE=sco,CODEC=CVSD", SND_PCM_STREAM_PLAYBACK, 0), 0); ck_assert_int_eq(test_pcm_close(NULL, pcm), 0); ck_assert_int_eq(snd_pcm_open(&pcm, "bluealsa:DEV=12:34:56:78:9A:BC,DELAY=10", SND_PCM_STREAM_PLAYBACK, 0), 0); ck_assert_int_eq(test_pcm_close(NULL, pcm), 0); ck_assert_int_eq(snd_pcm_open(&pcm, "bluealsa:DEV=12:34:56:78:9A:BC,VOL=50+", SND_PCM_STREAM_PLAYBACK, 0), 0); ck_assert_int_eq(test_pcm_close(NULL, pcm), 0); ck_assert_int_eq(snd_pcm_open(&pcm, "bluealsa:DEV=12:34:56:78:9A:BC,SOFTVOL=true", SND_PCM_STREAM_PLAYBACK, 0), 0); ck_assert_int_eq(test_pcm_close(NULL, pcm), 0); spawn_terminate(&sp_ba_mock, 0); spawn_close(&sp_ba_mock, NULL); } CK_END_TEST CK_START_TEST(test_playback_hw_set_free) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); for (size_t i = 0; i < 5; i++) { int set_hw_param_ret; /* acquire Bluetooth transport */ if ((set_hw_param_ret = set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time)) == -EBUSY) { debug("Retrying snd_pcm_hw_params_set..."); /* do not treat busy as an error */ i--; continue; } ck_assert_int_eq(set_hw_param_ret, 0); /* release Bluetooth transport */ ck_assert_int_eq(snd_pcm_hw_free(pcm), 0); } ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_playback_start) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; snd_pcm_sframes_t delay; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; size_t i; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_get_params(pcm, &buffer_size, &period_size), 0); /* setup PCM to be started by writing the last period of data */ ck_assert_int_eq(set_sw_params(pcm, buffer_size, period_size), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); /* fill-in buffer without starting playback */ for (i = 0; i < (buffer_size - 10) / period_size; i++) ck_assert_int_eq(snd_pcm_writei(pcm, test_sine_s16le(period_size), period_size), period_size); /* wait some time to make sure playback was not started */ usleep(period_time); /* check if playback was not started */ ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_PREPARED); /* check if buffer fullness is at the next-to-last period */ ck_assert_int_le(snd_pcm_avail(pcm), buffer_size - (i - 1) * period_size); ck_assert_int_eq(snd_pcm_delay(pcm, &delay), 0); ck_assert_int_ge(delay, (i - 1) * period_size); /* start playback - start threshold will be exceeded */ ck_assert_int_eq(snd_pcm_writei(pcm, test_sine_s16le(period_size), period_size), period_size); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_RUNNING); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_playback_drain) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; struct timespec t0, t, diff; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_get_params(pcm, &buffer_size, &period_size), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); /* get current timestamp */ gettimestamp(&t0); /* fill-in entire PCM buffer */ for (size_t i = 0; i <= buffer_size / period_size; i++) ck_assert_int_eq(snd_pcm_writei(pcm, test_sine_s16le(period_size), period_size), period_size); /* drain PCM buffer and stop playback */ ck_assert_int_eq(snd_pcm_drain(pcm), 0); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_SETUP); gettimestamp(&t); difftimespec(&t0, &t, &diff); /* verify whether elapsed time is at least PCM buffer time length */ ck_assert_uint_gt(diff.tv_sec * 1000000 + diff.tv_nsec / 1000, buffer_time); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_playback_drain_not_started) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_get_params(pcm, &buffer_size, &period_size), 0); /* setup PCM to be started by writing the last period of data */ ck_assert_int_eq(set_sw_params(pcm, buffer_size, period_size), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); /* fill-in buffer without starting playback */ for (size_t i = 0; i < (buffer_size - 10) / period_size; i++) ck_assert_int_eq(snd_pcm_writei(pcm, test_sine_s16le(period_size), period_size), period_size); /* drain PCM buffer and stop playback */ ck_assert_int_eq(snd_pcm_drain(pcm), 0); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_SETUP); /* verify whether PCM buffer is empty */ ck_assert_int_le(snd_pcm_avail(pcm), buffer_size); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_playback_drain_nonblock) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; struct timespec t0, t, diff; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_get_params(pcm, &buffer_size, &period_size), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); struct pollfd pfds[8]; unsigned short revents; int count = snd_pcm_poll_descriptors_count(pcm); ck_assert_int_eq(snd_pcm_poll_descriptors(pcm, pfds, ARRAYSIZE(pfds)), count); /* get current timestamp */ gettimestamp(&t0); /* fill-in entire PCM buffer */ for (size_t i = 0; i <= buffer_size / period_size; i++) ck_assert_int_eq(snd_pcm_writei(pcm, test_sine_s16le(period_size), period_size), period_size); /* set the PCM nonblock flag */ ck_assert_int_eq(snd_pcm_nonblock(pcm, 1), 0); /* initiate drain of PCM buffer */ ck_assert_int_eq(snd_pcm_drain(pcm), -EAGAIN); do { /* draining PCM shall emit POLLOUT event when drained */ ck_assert_int_gt(poll(pfds, count, -1), 0); snd_pcm_poll_descriptors_revents(pcm, pfds, count, &revents); } while (revents == 0); /* we should get write event flag set */ ck_assert_int_eq(revents & POLLOUT, POLLOUT); /* verify that the drain did complete */ ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_SETUP); gettimestamp(&t); difftimespec(&t0, &t, &diff); /* verify whether elapsed time is at least PCM buffer time length */ ck_assert_uint_gt(diff.tv_sec * 1000000 + diff.tv_nsec / 1000, buffer_time); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_playback_pause) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_get_params(pcm, &buffer_size, &period_size), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); snd_pcm_hw_params_t *params; snd_pcm_hw_params_alloca(¶ms); ck_assert_int_eq(snd_pcm_hw_params_current(pcm, params), 0); if (!snd_pcm_hw_params_can_pause(params)) warn("PCM playback pause test: %s", "PCM can not pause"); else { /* fill-in buffer and start playback */ for (size_t i = 0; i <= buffer_size / period_size; i++) ck_assert_int_eq(snd_pcm_writei(pcm, test_sine_s16le(period_size), period_size), period_size); /* pause playback */ ck_assert_int_eq(snd_pcm_pause(pcm, 1), 0); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_PAUSED); /* record PCM parameters just after pausing */ snd_pcm_sframes_t delay0, delay; snd_pcm_uframes_t frames = snd_pcm_avail(pcm); ck_assert_int_eq(snd_pcm_delay(pcm, &delay0), 0); ck_assert_int_gt(delay0, 0); /* wait some time */ usleep(buffer_time); /* during pause PCM parameters shall not be modified */ ck_assert_int_eq(snd_pcm_avail(pcm), frames); ck_assert_int_eq(snd_pcm_delay(pcm, &delay), 0); ck_assert_int_eq(delay, delay0); /* resume playback */ ck_assert_int_eq(snd_pcm_pause(pcm, 0), 0); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_RUNNING); /* wait a little bit */ usleep(3 * period_time / 2); /* check resume: more available frames, lower delay */ ck_assert_int_gt(snd_pcm_avail(pcm), frames); ck_assert_int_eq(snd_pcm_delay(pcm, &delay), 0); ck_assert_int_lt(delay, delay0); } ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_playback_reset) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; snd_pcm_sframes_t frames; snd_pcm_sframes_t delay; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_get_params(pcm, &buffer_size, &period_size), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); retry: /* fill-in buffer and start playback */ for (size_t i = 0; i <= buffer_size / period_size; i++) ck_assert_int_eq(snd_pcm_writei(pcm, test_sine_s16le(period_size), period_size), period_size); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_RUNNING); /* there should be less than one period of free space */ ck_assert_int_lt(snd_pcm_avail(pcm), period_size); /* delay should be no less then buffer_size - period_size */ ck_assert_int_eq(snd_pcm_delay(pcm, &delay), 0); ck_assert_int_gt(delay, buffer_size - period_size); /* remove queued data from PCM buffer - reset delay to 0 */ ck_assert_int_eq(snd_pcm_reset(pcm), 0); /* immediately try to write one period of data, however, at this point * we might face PCM in the under-run state; do not treat it as an error */ if ((frames = snd_pcm_writei(pcm, test_sine_s16le(period_size), period_size)) == -EPIPE) { debug("Retrying playback reset test: Device in the under-run state"); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); goto retry; } ck_assert_int_eq(frames, period_size); /* verify that only one frame is in the PCM buffer */ ck_assert_int_ge(snd_pcm_avail(pcm), buffer_size - period_size); ck_assert_int_eq(snd_pcm_delay(pcm, &delay), 0); /* from the logical point of view there should be no more than one period * of delay, however, hardware PCM device reports a little bit more than * a period of delay, so this test is not as strict as it should be :) */ ck_assert_int_le(delay, 3 * period_size / 2); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST CK_START_TEST(test_playback_underrun) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_uframes_t buffer_size; snd_pcm_uframes_t period_size; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_get_params(pcm, &buffer_size, &period_size), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); /* fill-in buffer and start playback */ for (size_t i = 0; i <= buffer_size / period_size; i++) ck_assert_int_eq(snd_pcm_writei(pcm, test_sine_s16le(period_size), period_size), period_size); /* after one and a half period time we shall be * able to write at least one period frames */ usleep(3 * period_time / 2); ck_assert_int_ge(snd_pcm_avail(pcm), period_size); /* allow under-run to occur */ usleep(buffer_time); /* check whether ALSA reports under-run */ ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_XRUN); ck_assert_int_eq(snd_pcm_avail(pcm), -EPIPE); /* recover from under-run */ ck_assert_int_eq(snd_pcm_prepare(pcm), 0); /* check successful recovery */ for (size_t i = 0; i <= buffer_size / period_size; i++) ck_assert_int_eq(snd_pcm_writei(pcm, test_sine_s16le(period_size), period_size), period_size); ck_assert_int_eq(snd_pcm_state_runtime(pcm), SND_PCM_STATE_RUNNING); ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST /** * Make reference test for device unplug. * * Values obtained with an external USB sound card: * - frames = -19 * - snd_pcm_poll_descriptors_count(pcm) = 1 * - snd_pcm_poll_descriptors(pcm, pfds, 4) = 1 * - snd_pcm_poll_descriptors_revents(pcm, pfds, 4, &revents) = 0 * - snd_pcm_prepare(pcm) = -19 * - snd_pcm_reset(pcm) = 0 * - snd_pcm_start(pcm) = -19 * - snd_pcm_drop(pcm) = -19 * - snd_pcm_drain(pcm) = -19 * - snd_pcm_pause(pcm, 0) = -19 * - snd_pcm_delay(pcm, &frames) = -19 * - snd_pcm_resume(pcm) = -38 * - snd_pcm_avail(pcm) = -19 * - snd_pcm_avail_update(pcm) = 15081 * - snd_pcm_writei(pcm, pcm_buffer, frames) = -19 * - snd_pcm_wait(pcm, 10) = -19 * - snd_pcm_close(pcm) = 0 */ CK_START_TEST(reference_playback_device_unplug) { unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_sframes_t frames = 0; snd_pcm_t *pcm = NULL; struct pollfd pfds[4]; unsigned short revents; /* this test needs user-defined PCM device */ ck_assert_ptr_ne(pcm_device, NULL); ck_assert_int_eq(test_pcm_open(NULL, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); fprintf(stderr, "Unplug PCM device..."); while (frames >= 0) frames = snd_pcm_writei(pcm, test_sine_s16le(512), 512); fprintf(stderr, "\n"); dumprv(frames); dumprv(snd_pcm_poll_descriptors_count(pcm)); dumprv(snd_pcm_poll_descriptors(pcm, pfds, ARRAYSIZE(pfds))); dumprv(snd_pcm_poll_descriptors_revents(pcm, pfds, ARRAYSIZE(pfds), &revents)); dumprv(snd_pcm_prepare(pcm)); dumprv(snd_pcm_reset(pcm)); dumprv(snd_pcm_start(pcm)); dumprv(snd_pcm_drop(pcm)); dumprv(snd_pcm_drain(pcm)); dumprv(snd_pcm_pause(pcm, 0)); dumprv(snd_pcm_delay(pcm, &frames)); dumprv(snd_pcm_resume(pcm)); dumprv(snd_pcm_avail(pcm)); dumprv(snd_pcm_avail_update(pcm)); dumprv(snd_pcm_writei(pcm, test_sine_s16le(128), 128)); dumprv(snd_pcm_wait(pcm, 10)); dumprv(snd_pcm_close(pcm)); } CK_END_TEST CK_START_TEST(ba_test_playback_device_unplug) { if (pcm_device != NULL) return; unsigned int buffer_time = 200000; unsigned int period_time = 25000; snd_pcm_sframes_t frames = 0; struct spawn_process sp_ba_mock; snd_pcm_t *pcm = NULL; ck_assert_ptr_eq(pcm_device, NULL); ck_assert_int_eq(test_pcm_open(&sp_ba_mock, &pcm, SND_PCM_STREAM_PLAYBACK), 0); ck_assert_int_eq(set_hw_params(pcm, pcm_format, pcm_channels, pcm_rate, &buffer_time, &period_time), 0); ck_assert_int_eq(snd_pcm_prepare(pcm), 0); /* write samples until server disconnects */ while (frames >= 0) frames = snd_pcm_writei(pcm, test_sine_s16le(128), 128); #if 0 /* check if most commonly used calls will report missing device */ struct pollfd pfds[4]; unsigned short revents; ck_assert_int_eq(frames, -ENODEV); ck_assert_int_eq(snd_pcm_poll_descriptors_count(pcm), 2); ck_assert_int_eq(snd_pcm_poll_descriptors(pcm, pfds, ARRAYSIZE(pfds)), 2); ck_assert_int_eq(snd_pcm_poll_descriptors_revents(pcm, pfds, ARRAYSIZE(pfds), &revents), -ENODEV); ck_assert_int_eq(snd_pcm_prepare(pcm), -ENODEV); ck_assert_int_eq(snd_pcm_reset(pcm), 0); ck_assert_int_eq(snd_pcm_start(pcm), -EBADFD); ck_assert_int_eq(snd_pcm_drop(pcm), 0); ck_assert_int_eq(snd_pcm_drain(pcm), -EBADFD); ck_assert_int_eq(snd_pcm_pause(pcm, 0), -EBADFD); ck_assert_int_eq(snd_pcm_delay(pcm, &frames), -ENODEV); ck_assert_int_eq(snd_pcm_resume(pcm), 0); ck_assert_int_eq(snd_pcm_avail(pcm), -EPIPE); ck_assert_int_eq(snd_pcm_avail_update(pcm), -EPIPE); ck_assert_int_eq(snd_pcm_writei(pcm, test_sine_s16le(128), 128), -EPIPE); ck_assert_int_eq(snd_pcm_wait(pcm, 10), -EPIPE); ck_assert_int_eq(snd_pcm_close(pcm), 0); #endif ck_assert_int_eq(test_pcm_close(&sp_ba_mock, pcm), 0); } CK_END_TEST int main(int argc, char *argv[]) { preload(argc, argv, ".libs/libaloader.so"); int opt; const char *opts = "hD:H:c:f:r:"; struct option longopts[] = { { "help", no_argument, NULL, 'h' }, { "pcm", required_argument, NULL, 'D' }, { "hwcompat", required_argument, NULL, 'H' }, { "channels", required_argument, NULL, 'c' }, { "format", required_argument, NULL, 'f' }, { "rate", required_argument, NULL, 'r' }, { 0, 0, 0, 0 }, }; bool run_capture = false; bool run_playback = false; bool run_unplug = false; while ((opt = getopt_long(argc, argv, opts, longopts, NULL)) != -1) switch (opt) { case 'h' /* --help */ : printf("usage: %s [--pcm=NAME] [playback|capture|unplug]\n", argv[0]); return 0; case 'D' /* --pcm=NAME */ : pcm_device = optarg; break; case 'H' /* --hwcompat=MODE */ : { const char * values[] = { "none", "busy", "silence" }; for (size_t i = 0; i < ARRAYSIZE(values); i++) if (strcmp(optarg, values[i]) == 0) pcm_hwcompat = optarg; } break; case 'c' /* --channels=NUM */ : pcm_channels = atoi(optarg); break; case 'f' /* --format=FMT */ : if ((pcm_format = snd_pcm_format_value(optarg)) == -1) pcm_format = SND_PCM_FORMAT_U8; break; case 'r' /* --rate=NUM */ : pcm_rate = atoi(optarg); break; default: fprintf(stderr, "Try '%s --help' for more information.\n", argv[0]); return 1; } char *argv_0 = strdup(argv[0]); snprintf(bluealsad_mock_path, sizeof(bluealsad_mock_path), "%s/mock/bluealsad-mock", dirname(argv_0)); if (argc == optind) { run_capture = true; run_playback = true; } else { for (; optind < argc; optind++) { if (strcmp(argv[optind], "capture") == 0) run_capture = true; else if (strcmp(argv[optind], "playback") == 0) run_playback = true; else if (strcmp(argv[optind], "unplug") == 0) run_unplug = true; } } Suite *s = suite_create(__FILE__); SRunner *sr = srunner_create(s); if (run_capture) { TCase *tc = tcase_create("capture"); tcase_add_test(tc, dump_capture); tcase_add_test(tc, test_capture_start); tcase_add_test(tc, test_capture_drain); tcase_add_test(tc, test_capture_pause); tcase_add_test(tc, test_capture_overrun); tcase_add_test(tc, test_capture_poll); suite_add_tcase(s, tc); } if (run_playback) { TCase *tc = tcase_create("playback"); tcase_add_test(tc, dump_playback); tcase_add_test(tc, ba_test_playback_hw_constraints); tcase_add_test(tc, ba_test_playback_channel_maps); tcase_add_test(tc, ba_test_playback_no_codec_selected); tcase_add_test(tc, ba_test_playback_no_such_device); tcase_add_test(tc, ba_test_playback_extra_setup); tcase_add_test(tc, test_playback_hw_set_free); tcase_add_test(tc, test_playback_start); tcase_add_test(tc, test_playback_drain); tcase_add_test(tc, test_playback_drain_not_started); tcase_add_test(tc, test_playback_drain_nonblock); tcase_add_test(tc, test_playback_pause); tcase_add_test(tc, test_playback_reset); tcase_add_test(tc, test_playback_underrun); tcase_add_test(tc, ba_test_playback_device_unplug); suite_add_tcase(s, tc); } if (run_unplug) { TCase *tc = tcase_create("unplug"); tcase_add_test(tc, reference_playback_device_unplug); suite_add_tcase(s, tc); } srunner_run_all(sr, CK_ENV); int nf = srunner_ntests_failed(sr); srunner_free(sr); free(argv_0); return nf == 0 ? 0 : 1; }