Files
2026-08-24 10:03:24 +08:00

1263 lines
40 KiB
C

/*
* test-alsa-pcm.c
* SPDX-FileCopyrightText: 2016-2025 BlueALSA developers
* SPDX-License-Identifier: MIT
*/
#if HAVE_CONFIG_H
# include <config.h>
#endif
#include <assert.h>
#include <errno.h>
#include <getopt.h>
#include <libgen.h>
#include <poll.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <check.h>
#include <alsa/asoundlib.h>
#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(&params);
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(&params);
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(&params);
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(&params);
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(&params);
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(&params);
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;
}