mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH 0/3] selftests: ALSA: pcmtest: Test error injection and pause
@ 2026-10-22 11:12 HyeongJun An
  2026-10-22 11:12 ` [PATCH 1/3] selftests: ALSA: pcmtest: Move the capture data check into a helper HyeongJun An
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: HyeongJun An @ 2026-10-22 11:12 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Ivan Orlov, Mark Brown, Jaroslav Kysela, Shuah Khan, linux-sound,
	linux-kselftest, linux-kernel, HyeongJun An

The pcmtest driver can return errors from its PCM callbacks and
supports pause, but the selftest covers neither. This series adds tests
for both.

Patch 1 moves the capture data check into a helper, so patch 3 can
reuse it. Patch 2 tests each inject_*_err parameter. Patch 3 tests
pausing a capture stream.

Tested on x86_64 with the sound/for-next driver, built for a 6.17 host.
All 10 tests pass in three runs. To check that the pause test catches
real bugs, I loaded two broken drivers. One keeps running on PAUSE_PUSH.
The other restarts the data on PAUSE_RELEASE. Only capture_pause fails
on each.

HyeongJun An (3):
  selftests: ALSA: pcmtest: Move the capture data check into a helper
  selftests: ALSA: pcmtest: Test errors from the PCM callbacks
  selftests: ALSA: pcmtest: Test pausing a capture stream

 .../selftests/alsa/test-pcmtest-driver.c      | 167 +++++++++++++++++-
 1 file changed, 158 insertions(+), 9 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 1/3] selftests: ALSA: pcmtest: Move the capture data check into a helper
  2026-10-22 11:12 [PATCH 0/3] selftests: ALSA: pcmtest: Test error injection and pause HyeongJun An
@ 2026-10-22 11:12 ` HyeongJun An
  2026-10-22 11:12 ` [PATCH 2/3] selftests: ALSA: pcmtest: Test errors from the PCM callbacks HyeongJun An
  2026-10-22 11:12 ` [PATCH 3/3] selftests: ALSA: pcmtest: Test pausing a capture stream HyeongJun An
  2 siblings, 0 replies; 4+ messages in thread
From: HyeongJun An @ 2026-10-22 11:12 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Ivan Orlov, Mark Brown, Jaroslav Kysela, Shuah Khan, linux-sound,
	linux-kselftest, linux-kernel, HyeongJun An

The capture test checks the captured bytes against the patterns in an
inline loop. A pause test added later needs the same check, so move the
loop into find_capture_mismatch(). It returns the offset of the first
wrong byte, or -1 when all bytes match.

No functional change.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
---
 .../selftests/alsa/test-pcmtest-driver.c      | 29 +++++++++++++------
 1 file changed, 20 insertions(+), 9 deletions(-)

diff --git a/tools/testing/selftests/alsa/test-pcmtest-driver.c b/tools/testing/selftests/alsa/test-pcmtest-driver.c
index 18ee72d5d4c9..fc4d12542808 100644
--- a/tools/testing/selftests/alsa/test-pcmtest-driver.c
+++ b/tools/testing/selftests/alsa/test-pcmtest-driver.c
@@ -82,6 +82,25 @@ static size_t get_sec_buf_len(unsigned int rate, unsigned long channels, snd_pcm
 	return rate * channels * snd_pcm_format_physical_width(format) / 8;
 }
 
+/*
+ * Check interleaved capture data against the patterns. Return the offset of the first wrong
+ * byte, or -1 if all bytes match.
+ */
+static long find_capture_mismatch(const unsigned char *it, struct pcmtest_test_params *params)
+{
+	size_t i, cur_ch, pos_in_ch;
+
+	for (i = 0; i < params->sec_buf_len * params->time; i++) {
+		cur_ch = (i / params->sample_size) % CH_NUM;
+		pos_in_ch = i / params->sample_size / CH_NUM * params->sample_size
+			    + (i % params->sample_size);
+		if (it[i] != patterns[cur_ch].buf[pos_in_ch % patterns[cur_ch].len])
+			return i;
+	}
+
+	return -1;
+}
+
 static int setup_handle(snd_pcm_t **handle, snd_pcm_sw_params_t *swparams,
 			snd_pcm_hw_params_t *hwparams, struct pcmtest_test_params *params,
 			int card, snd_pcm_stream_t stream)
@@ -207,9 +226,7 @@ TEST_F(pcmtest, playback) {
  */
 TEST_F(pcmtest, capture) {
 	snd_pcm_t *handle;
-	unsigned char *it;
 	snd_pcm_sframes_t read_res;
-	int i, cur_ch, pos_in_ch;
 	void *samples;
 	struct pcmtest_test_params *params = &self->params;
 
@@ -226,13 +243,7 @@ TEST_F(pcmtest, capture) {
 	read_res = snd_pcm_readi(handle, samples, params->rate * params->time);
 	ASSERT_EQ(read_res, params->rate * params->time);
 	snd_pcm_close(handle);
-	it = (unsigned char *)samples;
-	for (i = 0; i < self->params.sec_buf_len * self->params.time; i++) {
-		cur_ch = (i / params->sample_size) % CH_NUM;
-		pos_in_ch = i / params->sample_size / CH_NUM * params->sample_size
-			    + (i % params->sample_size);
-		ASSERT_EQ(it[i], patterns[cur_ch].buf[pos_in_ch % patterns[cur_ch].len]);
-	}
+	ASSERT_EQ(find_capture_mismatch(samples, params), -1);
 	free(samples);
 }
 
-- 
2.43.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 2/3] selftests: ALSA: pcmtest: Test errors from the PCM callbacks
  2026-10-22 11:12 [PATCH 0/3] selftests: ALSA: pcmtest: Test error injection and pause HyeongJun An
  2026-10-22 11:12 ` [PATCH 1/3] selftests: ALSA: pcmtest: Move the capture data check into a helper HyeongJun An
@ 2026-10-22 11:12 ` HyeongJun An
  2026-10-22 11:12 ` [PATCH 3/3] selftests: ALSA: pcmtest: Test pausing a capture stream HyeongJun An
  2 siblings, 0 replies; 4+ messages in thread
From: HyeongJun An @ 2026-10-22 11:12 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Ivan Orlov, Mark Brown, Jaroslav Kysela, Shuah Khan, linux-sound,
	linux-kselftest, linux-kernel, HyeongJun An

The driver can return an error from its open, hw_params, prepare and
trigger callbacks, controlled by the inject_*_err module parameters.
No test uses them, so nothing checks that these errors reach user space.

Add a test for each callback. It sets the parameter, checks that the
matching call fails with the driver's error, then clears the parameter
and checks that the call succeeds. The trigger test also checks that a
failed start leaves the stream in the PREPARED state. The fixture
teardown clears all parameters, so a failed test does not affect the
next one.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
---
 .../selftests/alsa/test-pcmtest-driver.c      | 89 +++++++++++++++++++
 1 file changed, 89 insertions(+)

diff --git a/tools/testing/selftests/alsa/test-pcmtest-driver.c b/tools/testing/selftests/alsa/test-pcmtest-driver.c
index fc4d12542808..380364ee7924 100644
--- a/tools/testing/selftests/alsa/test-pcmtest-driver.c
+++ b/tools/testing/selftests/alsa/test-pcmtest-driver.c
@@ -5,6 +5,7 @@
  *
  * Copyright 2023 Ivan Orlov <ivan.orlov0322@gmail.com>
  */
+#include <errno.h>
 #include <string.h>
 #include <alsa/asoundlib.h>
 #include "kselftest_harness.h"
@@ -58,6 +59,20 @@ static int read_patterns(void)
 	return 0;
 }
 
+static int set_param(const char *name, int value)
+{
+	char path[128];
+	FILE *f;
+
+	sprintf(path, "/sys/module/snd_pcmtest/parameters/%s", name);
+	f = fopen(path, "w");
+	if (!f)
+		return -1;
+	fprintf(f, "%d", value);
+
+	return fclose(f) ? -1 : 0;
+}
+
 static int get_test_results(char *debug_name)
 {
 	int result;
@@ -141,6 +156,10 @@ FIXTURE(pcmtest) {
 };
 
 FIXTURE_TEARDOWN(pcmtest) {
+	set_param("inject_open_err", 0);
+	set_param("inject_hwpars_err", 0);
+	set_param("inject_prepare_err", 0);
+	set_param("inject_trigger_err", 0);
 }
 
 FIXTURE_SETUP(pcmtest) {
@@ -343,4 +362,74 @@ TEST_F(pcmtest, reset_ioctl) {
 	snd_pcm_close(handle);
 }
 
+/*
+ * Here we check that an error from each PCM callback reaches user space. The driver returns
+ * the error while the matching 'inject_*_err' parameter is set, and works again once it is
+ * cleared.
+ */
+TEST_F(pcmtest, open_err) {
+	snd_pcm_t *handle;
+	char pcm_name[32];
+
+	sprintf(pcm_name, "hw:%d,0,0", self->card);
+
+	ASSERT_EQ(set_param("inject_open_err", 1), 0);
+	ASSERT_EQ(snd_pcm_open(&handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0), -EBUSY);
+
+	ASSERT_EQ(set_param("inject_open_err", 0), 0);
+	ASSERT_EQ(snd_pcm_open(&handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0), 0);
+	snd_pcm_close(handle);
+}
+
+TEST_F(pcmtest, hw_params_err) {
+	snd_pcm_t *handle;
+	char pcm_name[32];
+
+	snd_pcm_hw_params_alloca(&self->hwparams);
+	sprintf(pcm_name, "hw:%d,0,0", self->card);
+	ASSERT_EQ(snd_pcm_open(&handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0), 0);
+	ASSERT_EQ(snd_pcm_hw_params_any(handle, self->hwparams), 0);
+
+	ASSERT_EQ(set_param("inject_hwpars_err", 1), 0);
+	ASSERT_EQ(snd_pcm_hw_params(handle, self->hwparams), -EBUSY);
+
+	ASSERT_EQ(set_param("inject_hwpars_err", 0), 0);
+	ASSERT_EQ(snd_pcm_hw_params(handle, self->hwparams), 0);
+	snd_pcm_close(handle);
+}
+
+TEST_F(pcmtest, prepare_err) {
+	snd_pcm_t *handle;
+
+	snd_pcm_sw_params_alloca(&self->swparams);
+	snd_pcm_hw_params_alloca(&self->hwparams);
+	ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams, &self->params,
+			       self->card, SND_PCM_STREAM_PLAYBACK), 0);
+
+	ASSERT_EQ(set_param("inject_prepare_err", 1), 0);
+	ASSERT_EQ(snd_pcm_prepare(handle), -EINVAL);
+
+	ASSERT_EQ(set_param("inject_prepare_err", 0), 0);
+	ASSERT_EQ(snd_pcm_prepare(handle), 0);
+	snd_pcm_close(handle);
+}
+
+TEST_F(pcmtest, trigger_err) {
+	snd_pcm_t *handle;
+
+	snd_pcm_sw_params_alloca(&self->swparams);
+	snd_pcm_hw_params_alloca(&self->hwparams);
+	ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams, &self->params,
+			       self->card, SND_PCM_STREAM_CAPTURE), 0);
+
+	ASSERT_EQ(set_param("inject_trigger_err", 1), 0);
+	ASSERT_EQ(snd_pcm_start(handle), -EINVAL);
+	ASSERT_EQ(snd_pcm_state(handle), SND_PCM_STATE_PREPARED);
+
+	ASSERT_EQ(set_param("inject_trigger_err", 0), 0);
+	ASSERT_EQ(snd_pcm_start(handle), 0);
+	ASSERT_EQ(snd_pcm_state(handle), SND_PCM_STATE_RUNNING);
+	snd_pcm_close(handle);
+}
+
 TEST_HARNESS_MAIN
-- 
2.43.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH 3/3] selftests: ALSA: pcmtest: Test pausing a capture stream
  2026-10-22 11:12 [PATCH 0/3] selftests: ALSA: pcmtest: Test error injection and pause HyeongJun An
  2026-10-22 11:12 ` [PATCH 1/3] selftests: ALSA: pcmtest: Move the capture data check into a helper HyeongJun An
  2026-10-22 11:12 ` [PATCH 2/3] selftests: ALSA: pcmtest: Test errors from the PCM callbacks HyeongJun An
@ 2026-10-22 11:12 ` HyeongJun An
  2 siblings, 0 replies; 4+ messages in thread
From: HyeongJun An @ 2026-10-22 11:12 UTC (permalink / raw)
  To: Takashi Iwai
  Cc: Ivan Orlov, Mark Brown, Jaroslav Kysela, Shuah Khan, linux-sound,
	linux-kselftest, linux-kernel, HyeongJun An

The driver supports pause, but no test uses it.

Add a capture test that pauses the stream halfway for half a second.
The core does not read the hardware pointer while the stream is
paused, so a driver that keeps running shows up only at the release,
as a jump of the pointer. Check that the jump is under a tenth of a
second, and that the data carries on from where it stopped.

Assisted-by: Claude:claude-opus-5-5
Signed-off-by: HyeongJun An <sammiee5311@gmail.com>
---
 .../selftests/alsa/test-pcmtest-driver.c      | 49 +++++++++++++++++++
 1 file changed, 49 insertions(+)

diff --git a/tools/testing/selftests/alsa/test-pcmtest-driver.c b/tools/testing/selftests/alsa/test-pcmtest-driver.c
index 380364ee7924..b796a5613c6f 100644
--- a/tools/testing/selftests/alsa/test-pcmtest-driver.c
+++ b/tools/testing/selftests/alsa/test-pcmtest-driver.c
@@ -7,6 +7,7 @@
  */
 #include <errno.h>
 #include <string.h>
+#include <unistd.h>
 #include <alsa/asoundlib.h>
 #include "kselftest_harness.h"
 
@@ -362,6 +363,54 @@ TEST_F(pcmtest, reset_ioctl) {
 	snd_pcm_close(handle);
 }
 
+/*
+ * Here we pause a capture stream halfway. The core does not read the hardware pointer while
+ * the stream is paused, so a driver that keeps running only shows up after the release, as a
+ * jump of the pointer. The data must also continue where it stopped, so the whole buffer still
+ * matches the patterns.
+ */
+TEST_F(pcmtest, capture_pause) {
+	snd_pcm_t *handle;
+	snd_pcm_sframes_t read_res, paused_avail, avail, frames, half;
+	unsigned char *samples;
+	struct pcmtest_test_params *params = &self->params;
+
+	samples = calloc(params->sec_buf_len * params->time, 1);
+	ASSERT_NE(samples, NULL);
+
+	snd_pcm_sw_params_alloca(&self->swparams);
+	snd_pcm_hw_params_alloca(&self->hwparams);
+	ASSERT_EQ(setup_handle(&handle, self->swparams, self->hwparams, params,
+			       self->card, SND_PCM_STREAM_CAPTURE), 0);
+
+	frames = params->rate * params->time;
+	half = frames / 2;
+	read_res = snd_pcm_readi(handle, samples, half);
+	ASSERT_EQ(read_res, half);
+
+	ASSERT_EQ(snd_pcm_pause(handle, 1), 0);
+	ASSERT_EQ(snd_pcm_state(handle), SND_PCM_STATE_PAUSED);
+	paused_avail = snd_pcm_avail(handle);
+	ASSERT_GE(paused_avail, 0);
+	usleep(500000);
+
+	/*
+	 * At 8000 Hz the pause is 4000 frames long. Allow a tenth of a second (800 frames) for
+	 * the release.
+	 */
+	ASSERT_EQ(snd_pcm_pause(handle, 0), 0);
+	avail = snd_pcm_avail(handle);
+	ASSERT_GE(avail, 0);
+	ASSERT_LT(avail - paused_avail, (snd_pcm_sframes_t)params->rate / 10);
+	read_res = snd_pcm_readi(handle, samples + snd_pcm_frames_to_bytes(handle, half),
+				 frames - half);
+	ASSERT_EQ(read_res, frames - half);
+	snd_pcm_close(handle);
+
+	ASSERT_EQ(find_capture_mismatch(samples, params), -1);
+	free(samples);
+}
+
 /*
  * Here we check that an error from each PCM callback reaches user space. The driver returns
  * the error while the matching 'inject_*_err' parameter is set, and works again once it is
-- 
2.43.0


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-10-22 11:13 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-22 11:12 [PATCH 0/3] selftests: ALSA: pcmtest: Test error injection and pause HyeongJun An
2026-10-22 11:12 ` [PATCH 1/3] selftests: ALSA: pcmtest: Move the capture data check into a helper HyeongJun An
2026-10-22 11:12 ` [PATCH 2/3] selftests: ALSA: pcmtest: Test errors from the PCM callbacks HyeongJun An
2026-10-22 11:12 ` [PATCH 3/3] selftests: ALSA: pcmtest: Test pausing a capture stream HyeongJun An

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®