[alsa-devel] [PATCH 12/35] axfer: add a unit test for mapper interface

Takashi Sakamoto o-takashi at sakamocchi.jp
Tue Nov 13 07:41:24 CET 2018


In former commits, mapper module gets supports of muxer/demuxer for
single/multiple targets for playback source or capture destination. This
commit adds a unit test for them. This includes positive test cases only.
The test cases actually generate I/O to file systems for many test cases.
It takes a bit long time to finish.

Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
 axfer/test/Makefile.am   |  20 +-
 axfer/test/mapper-test.c | 491 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 509 insertions(+), 2 deletions(-)
 create mode 100644 axfer/test/mapper-test.c

diff --git a/axfer/test/Makefile.am b/axfer/test/Makefile.am
index 66b30ef..76a93bf 100644
--- a/axfer/test/Makefile.am
+++ b/axfer/test/Makefile.am
@@ -1,8 +1,10 @@
 TESTS = \
-	container-test
+	container-test  \
+	mapper-test
 
 check_PROGRAMS = \
-	container-test
+	container-test \
+	mapper-test
 
 container_test_SOURCES = \
 	../container.h \
@@ -13,3 +15,17 @@ container_test_SOURCES = \
 	../container-raw.c \
 	generator.c \
 	container-test.c
+
+mapper_test_SOURCES = \
+	../container.h \
+	../container.c \
+	../container-riff-wave.c \
+	../container-au.c \
+	../container-voc.c \
+	../container-raw.c \
+	../mapper.h \
+	../mapper.c \
+	../mapper-single.c \
+	../mapper-multiple.c \
+	generator.c \
+	mapper-test.c
diff --git a/axfer/test/mapper-test.c b/axfer/test/mapper-test.c
new file mode 100644
index 00000000..9f005aa
--- /dev/null
+++ b/axfer/test/mapper-test.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// mapper-io.c - a unit test for muxer/demuxer for PCM frames on buffer.
+//
+// Copyright (c) 2018 Takashi Sakamoto <o-takashi at sakamocchi.jp>
+//
+// Licensed under the terms of the GNU General Public License, version 2.
+
+#include "../mapper.h"
+#include "../misc.h"
+
+#include "generator.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include <assert.h>
+
+struct mapper_trial {
+	enum container_format cntr_format;
+	struct container_context *cntrs;
+
+	char **paths;
+
+	struct mapper_context mapper;
+	bool verbose;
+};
+
+static void test_demuxer(struct mapper_context *mapper, snd_pcm_access_t access,
+			 unsigned int bytes_per_sample,
+			 unsigned int samples_per_frame,
+			 unsigned int frames_per_buffer,
+			 void *frame_buffer, unsigned int frame_count,
+			 struct container_context *cntrs,
+			 unsigned int cntr_count, bool verbose)
+{
+	unsigned int total_frame_count;
+	int err;
+
+	err = mapper_context_init(mapper, MAPPER_TYPE_DEMUXER, cntr_count,
+				  verbose);
+	assert(err == 0);
+
+	err = mapper_context_pre_process(mapper, access, bytes_per_sample,
+					 samples_per_frame, frames_per_buffer,
+					 cntrs);
+	assert(err == 0);
+
+	total_frame_count = frame_count;
+	err = mapper_context_process_frames(mapper, frame_buffer,
+					    &total_frame_count, cntrs);
+	assert(err == 0);
+	assert(total_frame_count == frame_count);
+
+	mapper_context_post_process(mapper);
+	mapper_context_destroy(mapper);
+}
+
+static int test_demux(struct mapper_trial *trial, snd_pcm_access_t access,
+		      snd_pcm_format_t sample_format,
+		      unsigned int samples_per_frame,
+		      unsigned int frames_per_second,
+		      unsigned int frames_per_buffer,
+		      void *frame_buffer, unsigned int frame_count,
+		      unsigned int cntr_count)
+{
+	struct container_context *cntrs = trial->cntrs;
+	char **paths = trial->paths;
+	enum container_format cntr_format = trial->cntr_format;
+	unsigned int bytes_per_sample;
+	uint64_t total_frame_count;
+	int i;
+	int err;
+
+	for (i = 0; i < cntr_count; ++i) {
+		snd_pcm_format_t format;
+		unsigned int channels;
+		unsigned int rate;
+
+		err = container_builder_init(cntrs + i, paths[i], cntr_format,
+					     0);
+		if (err < 0)
+			goto end;
+
+		format = sample_format;
+		rate = frames_per_second;
+		total_frame_count = frame_count;
+		if (cntr_count > 1)
+			channels = 1;
+		else
+			channels = samples_per_frame;
+		err = container_context_pre_process(cntrs + i, &format,
+						    &channels, &rate,
+						    &total_frame_count);
+		if (err < 0)
+			goto end;
+		assert(format == sample_format);
+		assert(rate == frames_per_second);
+		assert(total_frame_count >= 0);
+		if (cntr_count > 1)
+			assert(channels == 1);
+		else
+			assert(channels == samples_per_frame);
+	}
+
+	bytes_per_sample = snd_pcm_format_physical_width(sample_format) / 8;
+	test_demuxer(&trial->mapper, access, bytes_per_sample,
+		     samples_per_frame, frames_per_buffer, frame_buffer,
+		     frame_count, cntrs, cntr_count, trial->verbose);
+
+	for (i = 0; i < cntr_count; ++i) {
+		container_context_post_process(cntrs + i, &total_frame_count);
+		assert(total_frame_count == frame_count);
+	}
+end:
+	for (i = 0; i < cntr_count; ++i)
+		container_context_destroy(cntrs + i);
+
+	return err;
+}
+
+static void test_muxer(struct mapper_context *mapper, snd_pcm_access_t access,
+		       unsigned int bytes_per_sample,
+		       unsigned int samples_per_frame,
+		       unsigned int frames_per_buffer,
+		       void *frame_buffer, unsigned int frame_count,
+		       struct container_context *cntrs,
+		       unsigned int cntr_count, bool verbose)
+{
+	unsigned int total_frame_count;
+	int err;
+
+	err = mapper_context_init(mapper, MAPPER_TYPE_MUXER, cntr_count,
+				  verbose);
+	assert(err == 0);
+
+	err = mapper_context_pre_process(mapper, access, bytes_per_sample,
+					 samples_per_frame, frames_per_buffer,
+					 cntrs);
+	assert(err == 0);
+
+	total_frame_count = frame_count;
+	err = mapper_context_process_frames(mapper, frame_buffer,
+					    &total_frame_count, cntrs);
+	assert(err == 0);
+	assert(total_frame_count == frame_count);
+
+	mapper_context_post_process(mapper);
+	mapper_context_destroy(mapper);
+}
+
+static int test_mux(struct mapper_trial *trial, snd_pcm_access_t access,
+		    snd_pcm_format_t sample_format,
+		    unsigned int samples_per_frame,
+		    unsigned int frames_per_second,
+		    unsigned int frames_per_buffer,
+		    void *frame_buffer, unsigned int frame_count,
+		    unsigned int cntr_count)
+{
+	struct container_context *cntrs = trial->cntrs;
+	char **paths = trial->paths;
+	unsigned int bytes_per_sample;
+	uint64_t total_frame_count;
+	int i;
+	int err;
+
+	for (i = 0; i < cntr_count; ++i) {
+		snd_pcm_format_t format;
+		unsigned int channels;
+		unsigned int rate;
+
+		err = container_parser_init(cntrs + i, paths[i], 0);
+		if (err < 0)
+			goto end;
+
+		format = sample_format;
+		rate = frames_per_second;
+		if (cntr_count > 1)
+			channels = 1;
+		else
+			channels = samples_per_frame;
+		err = container_context_pre_process(cntrs + i, &format,
+						    &channels, &rate,
+						    &total_frame_count);
+		if (err < 0)
+			goto end;
+
+		assert(format == sample_format);
+		assert(rate == frames_per_second);
+		assert(total_frame_count == frame_count);
+		if (cntr_count > 1)
+			assert(channels == 1);
+		else
+			assert(channels == samples_per_frame);
+	}
+
+	bytes_per_sample = snd_pcm_format_physical_width(sample_format) / 8;
+	test_muxer(&trial->mapper, access, bytes_per_sample, samples_per_frame,
+		   frames_per_buffer, frame_buffer, frame_count, cntrs,
+		   cntr_count, trial->verbose);
+
+	for (i = 0; i < cntr_count; ++i) {
+		container_context_post_process(cntrs + i, &total_frame_count);
+		assert(total_frame_count == frame_count);
+	}
+end:
+	for (i = 0; i < cntr_count; ++i)
+		container_context_destroy(cntrs + i);
+
+	return err;
+}
+
+static int test_mapper(struct mapper_trial *trial, snd_pcm_access_t access,
+		    snd_pcm_format_t sample_format,
+		    unsigned int samples_per_frame,
+		    unsigned int frames_per_second, void *frame_buffer,
+		    void *check_buffer, unsigned int frame_count,
+		    unsigned int cntr_count)
+{
+	unsigned int frames_per_buffer;
+	int i;
+	int err;
+
+	// Use a buffer aligned by typical size of page frame.
+	frames_per_buffer = ((frame_count + 4096) / 4096) * 4096;
+
+	err = test_demux(trial, access, sample_format, samples_per_frame,
+			 frames_per_second, frames_per_buffer, frame_buffer,
+			 frame_count, cntr_count);
+	if (err < 0)
+		goto end;
+
+	err = test_mux(trial, access, sample_format, samples_per_frame,
+		       frames_per_second, frames_per_buffer, check_buffer,
+		       frame_count, cntr_count);
+end:
+	for (i = 0; i < cntr_count; ++i)
+		unlink(trial->paths[i]);
+
+	return err;
+}
+
+static int test_i_buf(struct mapper_trial *trial, snd_pcm_access_t access,
+		      snd_pcm_format_t sample_format,
+		      unsigned int samples_per_frame,
+		      unsigned int frames_per_second, void *frame_buffer,
+		      unsigned int frame_count, unsigned int cntr_count)
+{
+	unsigned int size;
+	char *buf;
+	int err;
+
+	size = frame_count * samples_per_frame *
+			snd_pcm_format_physical_width(sample_format) / 8;
+	buf = malloc(size);
+	if (buf == 0)
+		return -ENOMEM;
+	memset(buf, 0, size);
+
+	// Test multiple target.
+	err = test_mapper(trial, access, sample_format, samples_per_frame,
+			  frames_per_second, frame_buffer, buf,
+			  frame_count, cntr_count);
+	if (err < 0)
+		goto end;
+	err = memcmp(frame_buffer, buf, size);
+	assert(err == 0);
+
+	// Test single target.
+	err = test_mapper(trial, access, sample_format, samples_per_frame,
+			  frames_per_second, frame_buffer, buf,
+			  frame_count, 1);
+	if (err < 0)
+		goto end;
+	err = memcmp(frame_buffer, buf, size);
+	assert(err == 0);
+end:
+	free(buf);
+
+	return err;
+}
+
+static int test_vector(struct mapper_trial *trial, snd_pcm_access_t access,
+		       snd_pcm_format_t sample_format,
+		       unsigned int samples_per_frame,
+		       unsigned int frames_per_second, void *frame_buffer,
+		       unsigned int frame_count, unsigned int cntr_count)
+{
+	unsigned int size;
+	char **bufs;
+	int i;
+	int err;
+
+	bufs = calloc(cntr_count, sizeof(*bufs));
+	if (bufs == NULL)
+		return -ENOMEM;
+
+	size = frame_count * snd_pcm_format_physical_width(sample_format) / 8;
+
+	for (i = 0; i < cntr_count; ++i) {
+		bufs[i] = malloc(size);
+		if (bufs[i] == NULL)
+			goto end;
+		memset(bufs[i], 0, size);
+	}
+
+	// Test multiple target.
+	err = test_mapper(trial, access, sample_format, samples_per_frame,
+			  frames_per_second, frame_buffer, bufs,
+			  frame_count, cntr_count);
+	if (err < 0)
+		goto end;
+	for (i = 0; i < cntr_count; ++i) {
+		char **target = frame_buffer;
+		err = memcmp(target[i], bufs[i], size);
+		assert(err == 0);
+	}
+
+	// Test single target.
+	err = test_mapper(trial, access, sample_format, samples_per_frame,
+			  frames_per_second, frame_buffer, bufs,
+			  frame_count, 1);
+	if (err < 0)
+		goto end;
+	for (i = 0; i < cntr_count; ++i) {
+		char **target = frame_buffer;
+		err = memcmp(target[i], bufs[i], size);
+		assert(err == 0);
+	}
+end:
+	for (i = 0; i < cntr_count; ++i) {
+		if (bufs[i])
+			free(bufs[i]);
+	}
+	free(bufs);
+
+	return err;
+}
+
+static int test_n_buf(struct mapper_trial *trial, snd_pcm_access_t access,
+		      snd_pcm_format_t sample_format,
+		      unsigned int samples_per_frame,
+		      unsigned int frames_per_second, void *frame_buffer,
+		      unsigned int frame_count, unsigned int cntr_count)
+{
+	char *test_buf = frame_buffer;
+	unsigned int size;
+	char **test_vec;
+	int i;
+	int err;
+
+	size = frame_count * snd_pcm_format_physical_width(sample_format) / 8;
+
+	test_vec = calloc(cntr_count * 2, sizeof(*test_vec));
+	if (test_vec == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < cntr_count; ++i)
+		test_vec[i] = test_buf + size * i;
+
+	err = test_vector(trial, access, sample_format, samples_per_frame,
+			  frames_per_second, test_vec, frame_count, cntr_count);
+	free(test_vec);
+
+	return err;
+}
+
+static int callback(struct test_generator *gen, snd_pcm_access_t access,
+		    snd_pcm_format_t sample_format,
+		    unsigned int samples_per_frame, void *frame_buffer,
+		    unsigned int frame_count)
+{
+
+	int (*handler)(struct mapper_trial *trial, snd_pcm_access_t access,
+		       snd_pcm_format_t sample_format,
+		       unsigned int samples_per_frame,
+		       unsigned int frames_per_second, void *frame_buffer,
+		       unsigned int frame_count, unsigned int cntr_count);
+	struct mapper_trial *trial = gen->private_data;
+
+	if (access == SND_PCM_ACCESS_RW_NONINTERLEAVED)
+		handler = test_vector;
+	else if (access == SND_PCM_ACCESS_MMAP_NONINTERLEAVED)
+		handler = test_n_buf;
+	else
+		handler = test_i_buf;
+
+	return handler(trial, access, sample_format, samples_per_frame, 48000,
+		       frame_buffer, frame_count, samples_per_frame);
+};
+
+int main(int argc, const char *argv[])
+{
+	// Test 8/16/18/20/24/32/64 bytes per sample.
+	static const uint64_t sample_format_mask =
+			(1ul << SND_PCM_FORMAT_U8) |
+			(1ul << SND_PCM_FORMAT_S16_LE) |
+			(1ul << SND_PCM_FORMAT_S18_3LE) |
+			(1ul << SND_PCM_FORMAT_S20_3LE) |
+			(1ul << SND_PCM_FORMAT_S24_LE) |
+			(1ul << SND_PCM_FORMAT_S32_LE) |
+			(1ul << SND_PCM_FORMAT_FLOAT64_LE);
+	uint64_t access_mask;
+	struct test_generator gen = {0};
+	struct mapper_trial *trial;
+	struct container_context *cntrs;
+	unsigned int samples_per_frame;
+	char **paths = NULL;
+	snd_pcm_access_t access;
+	bool verbose;
+	int i;
+	int err;
+
+	// Test up to 32 channels.
+	samples_per_frame = 32;
+	cntrs = calloc(samples_per_frame, sizeof(*cntrs));
+	if (cntrs == NULL)
+		return -ENOMEM;
+
+	paths = calloc(samples_per_frame, sizeof(*paths));
+	if (paths == NULL) {
+		err = -ENOMEM;
+		goto end;
+	}
+	for (i = 0; i < samples_per_frame; ++i) {
+		paths[i] = malloc(8);
+		if (paths[i] == NULL) {
+			err = -ENOMEM;
+			goto end;
+		}
+		snprintf(paths[i], 8, "hoge%d", i);
+	}
+
+	if (argc > 1) {
+		char *term;
+		access = strtol(argv[1], &term, 10);
+		if (errno != 0 || *term != '\0') {
+			err = -EINVAL;;
+			goto end;
+		}
+		if (access < SND_PCM_ACCESS_MMAP_INTERLEAVED &&
+		    access > SND_PCM_ACCESS_RW_NONINTERLEAVED) {
+			err = -EINVAL;
+			goto end;
+		}
+		if (access == SND_PCM_ACCESS_MMAP_COMPLEX) {
+			err = -EINVAL;
+			goto end;
+		}
+
+		access_mask = 1ul << access;
+		verbose = true;
+	} else {
+		access_mask = (1ul << SND_PCM_ACCESS_MMAP_INTERLEAVED) |
+			      (1ul << SND_PCM_ACCESS_MMAP_NONINTERLEAVED) |
+			      (1ul << SND_PCM_ACCESS_RW_INTERLEAVED) |
+			      (1ul << SND_PCM_ACCESS_RW_NONINTERLEAVED);
+		verbose = false;
+	}
+
+	err = generator_context_init(&gen, access_mask, sample_format_mask,
+				     1, samples_per_frame,
+				     23, 4500, 1024,
+				     sizeof(struct mapper_trial));
+	if (err < 0)
+		goto end;
+
+	trial = gen.private_data;
+	trial->cntrs = cntrs;
+	trial->cntr_format = CONTAINER_FORMAT_RIFF_WAVE;
+	trial->paths = paths;
+	trial->verbose = verbose;
+	err = generator_context_run(&gen, callback);
+
+	generator_context_destroy(&gen);
+end:
+	if (paths) {
+		for (i = 0; i < samples_per_frame; ++i)
+			free(paths[i]);
+		free(paths);
+	}
+	free(cntrs);
+
+	if (err < 0) {
+		printf("%s\n", strerror(-err));
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
-- 
2.19.1



More information about the Alsa-devel mailing list