[alsa-devel] [PATCH 08/35] axfer: add unit test for container interface

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


In former commits, container module gets supports of parser/builder for
several types of file format. 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 long time to finish.

Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
 axfer/Makefile.am           |   3 +-
 axfer/test/Makefile.am      |  15 ++
 axfer/test/container-test.c | 299 ++++++++++++++++++++++++++++++++++++
 axfer/test/generator.c      | 260 +++++++++++++++++++++++++++++++
 axfer/test/generator.h      |  47 ++++++
 configure.ac                |   3 +-
 6 files changed, 625 insertions(+), 2 deletions(-)
 create mode 100644 axfer/test/Makefile.am
 create mode 100644 axfer/test/container-test.c
 create mode 100644 axfer/test/generator.c
 create mode 100644 axfer/test/generator.h

diff --git a/axfer/Makefile.am b/axfer/Makefile.am
index f1ec1d1..55fcf71 100644
--- a/axfer/Makefile.am
+++ b/axfer/Makefile.am
@@ -6,7 +6,8 @@ AM_CPPFLAGS = \
 	-I$(top_srcdir)/include
 
 # Unit tests.
-SUBDIRS =
+SUBDIRS = \
+	test
 
 LIBRT = @LIBRT@
 LDADD = \
diff --git a/axfer/test/Makefile.am b/axfer/test/Makefile.am
new file mode 100644
index 00000000..66b30ef
--- /dev/null
+++ b/axfer/test/Makefile.am
@@ -0,0 +1,15 @@
+TESTS = \
+	container-test
+
+check_PROGRAMS = \
+	container-test
+
+container_test_SOURCES = \
+	../container.h \
+	../container.c \
+	../container-riff-wave.c \
+	../container-au.c \
+	../container-voc.c \
+	../container-raw.c \
+	generator.c \
+	container-test.c
diff --git a/axfer/test/container-test.c b/axfer/test/container-test.c
new file mode 100644
index 00000000..0e2e6e9
--- /dev/null
+++ b/axfer/test/container-test.c
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// container-io.c - a unit test for parser/builder of supported containers.
+//
+// Copyright (c) 2018 Takashi Sakamoto <o-takashi at sakamocchi.jp>
+//
+// Licensed under the terms of the GNU General Public License, version 2.
+
+#include "../container.h"
+#include "../misc.h"
+
+#include "generator.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdbool.h>
+
+#include <assert.h>
+
+struct container_trial {
+	enum container_format format;
+
+	struct container_context cntr;
+	bool verbose;
+};
+
+static void test_builder(struct container_context *cntr,
+			 enum container_format format, const char *const name,
+			 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,
+			 bool verbose)
+{
+	snd_pcm_format_t sample;
+	unsigned int channels;
+	unsigned int rate;
+	uint64_t max_frame_count;
+	unsigned int handled_frame_count;
+	uint64_t total_frame_count;
+	int err;
+
+	err = container_builder_init(cntr, name, format, verbose);
+	assert(err == 0);
+
+	sample = sample_format;
+	channels = samples_per_frame;
+	rate = frames_per_second;
+	max_frame_count = 0;
+	err = container_context_pre_process(cntr, &sample, &channels, &rate,
+					    &max_frame_count);
+	assert(err == 0);
+	assert(sample == sample_format);
+	assert(channels == samples_per_frame);
+	assert(rate == frames_per_second);
+	assert(max_frame_count > 0);
+
+	handled_frame_count = frame_count;
+	err = container_context_process_frames(cntr, frame_buffer,
+					       &handled_frame_count);
+	assert(err == 0);
+	assert(handled_frame_count > 0);
+	assert(handled_frame_count <= frame_count);
+
+	total_frame_count = 0;
+	err = container_context_post_process(cntr, &total_frame_count);
+	assert(err == 0);
+	assert(total_frame_count == frame_count);
+
+	container_context_destroy(cntr);
+}
+
+static void test_parser(struct container_context *cntr,
+		        enum container_format format, const char *const name,
+		        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,
+			bool verbose)
+{
+	snd_pcm_format_t sample;
+	unsigned int channels;
+	unsigned int rate;
+	uint64_t total_frame_count;
+	unsigned int handled_frame_count;
+	int err;
+
+	err = container_parser_init(cntr, name, verbose);
+	assert(err == 0);
+
+	sample = sample_format;
+	channels = samples_per_frame;
+	rate = frames_per_second;
+	total_frame_count = 0;
+	err = container_context_pre_process(cntr, &sample, &channels, &rate,
+					    &total_frame_count);
+	assert(err == 0);
+	assert(sample == sample_format);
+	assert(channels == samples_per_frame);
+	assert(rate == frames_per_second);
+	assert(total_frame_count == frame_count);
+
+	handled_frame_count = total_frame_count;
+	err = container_context_process_frames(cntr, frame_buffer,
+					       &handled_frame_count);
+	assert(err == 0);
+	assert(handled_frame_count == frame_count);
+
+	total_frame_count = 0;
+	err = container_context_post_process(cntr, &total_frame_count);
+	assert(err == 0);
+	assert(total_frame_count == handled_frame_count);
+
+	container_context_destroy(cntr);
+}
+
+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)
+{
+	static const unsigned int entries[] = {
+		[0] = 44100,
+		[1] = 48000,
+		[2] = 88200,
+		[3] = 96000,
+		[4] = 176400,
+		[5] = 192000,
+	};
+	struct container_trial *trial = gen->private_data;
+	unsigned int frames_per_second;
+	const char *const name = "hoge";
+	unsigned int size;
+	void *buf;
+	int i;
+	int err = 0;
+
+	size = frame_count * samples_per_frame *
+			snd_pcm_format_physical_width(sample_format) / 8;
+	buf = malloc(size);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	// Remove a result of a previous trial.
+	unlink(name);
+
+	for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+		frames_per_second = entries[i];
+
+		test_builder(&trial->cntr, trial->format, name, access,
+			     sample_format, samples_per_frame,
+			     frames_per_second, frame_buffer, frame_count,
+			     trial->verbose);
+
+		test_parser(&trial->cntr, trial->format, name, access,
+			    sample_format, samples_per_frame, frames_per_second,
+			    buf, frame_count, trial->verbose);
+
+		err = memcmp(buf, frame_buffer, size);
+		assert(err == 0);
+
+		unlink(name);
+	}
+
+	free(buf);
+
+	return 0;
+}
+
+int main(int argc, const char *argv[])
+{
+	static const uint64_t sample_format_masks[] = {
+		[CONTAINER_FORMAT_RIFF_WAVE] =
+			(1ul << SND_PCM_FORMAT_U8) |
+			(1ul << SND_PCM_FORMAT_S16_LE) |
+			(1ul << SND_PCM_FORMAT_S16_BE) |
+			(1ul << SND_PCM_FORMAT_S24_LE) |
+			(1ul << SND_PCM_FORMAT_S24_BE) |
+			(1ul << SND_PCM_FORMAT_S32_LE) |
+			(1ul << SND_PCM_FORMAT_S32_BE) |
+			(1ul << SND_PCM_FORMAT_FLOAT_LE) |
+			(1ul << SND_PCM_FORMAT_FLOAT_BE) |
+			(1ul << SND_PCM_FORMAT_FLOAT64_LE) |
+			(1ul << SND_PCM_FORMAT_FLOAT64_BE) |
+			(1ul << SND_PCM_FORMAT_MU_LAW) |
+			(1ul << SND_PCM_FORMAT_A_LAW) |
+			(1ul << SND_PCM_FORMAT_S24_3LE) |
+			(1ul << SND_PCM_FORMAT_S24_3BE) |
+			(1ul << SND_PCM_FORMAT_S20_3LE) |
+			(1ul << SND_PCM_FORMAT_S20_3BE) |
+			(1ul << SND_PCM_FORMAT_S18_3LE) |
+			(1ul << SND_PCM_FORMAT_S18_3BE),
+		[CONTAINER_FORMAT_AU] =
+			(1ul << SND_PCM_FORMAT_S8) |
+			(1ul << SND_PCM_FORMAT_S16_BE) |
+			(1ul << SND_PCM_FORMAT_S32_BE) |
+			(1ul << SND_PCM_FORMAT_FLOAT_BE) |
+			(1ul << SND_PCM_FORMAT_FLOAT64_BE) |
+			(1ul << SND_PCM_FORMAT_MU_LAW) |
+			(1ul << SND_PCM_FORMAT_A_LAW),
+		[CONTAINER_FORMAT_VOC] =
+			(1ul << SND_PCM_FORMAT_U8) |
+			(1ul << SND_PCM_FORMAT_S16_LE) |
+			(1ul << SND_PCM_FORMAT_MU_LAW) |
+			(1ul << SND_PCM_FORMAT_A_LAW),
+		[CONTAINER_FORMAT_RAW] =
+			(1ul << SND_PCM_FORMAT_S8) |
+			(1ul << SND_PCM_FORMAT_U8) |
+			(1ul << SND_PCM_FORMAT_S16_LE) |
+			(1ul << SND_PCM_FORMAT_S16_BE) |
+			(1ul << SND_PCM_FORMAT_U16_LE) |
+			(1ul << SND_PCM_FORMAT_U16_BE) |
+			(1ul << SND_PCM_FORMAT_S24_LE) |
+			(1ul << SND_PCM_FORMAT_S24_BE) |
+			(1ul << SND_PCM_FORMAT_U24_LE) |
+			(1ul << SND_PCM_FORMAT_U24_BE) |
+			(1ul << SND_PCM_FORMAT_S32_LE) |
+			(1ul << SND_PCM_FORMAT_S32_BE) |
+			(1ul << SND_PCM_FORMAT_U32_LE) |
+			(1ul << SND_PCM_FORMAT_U32_BE) |
+			(1ul << SND_PCM_FORMAT_FLOAT_LE) |
+			(1ul << SND_PCM_FORMAT_FLOAT_BE) |
+			(1ul << SND_PCM_FORMAT_FLOAT64_LE) |
+			(1ul << SND_PCM_FORMAT_FLOAT64_BE) |
+			(1ul << SND_PCM_FORMAT_IEC958_SUBFRAME_LE) |
+			(1ul << SND_PCM_FORMAT_IEC958_SUBFRAME_BE) |
+			(1ul << SND_PCM_FORMAT_MU_LAW) |
+			(1ul << SND_PCM_FORMAT_A_LAW) |
+			(1ul << SND_PCM_FORMAT_S24_3LE) |
+			(1ul << SND_PCM_FORMAT_S24_3BE) |
+			(1ul << SND_PCM_FORMAT_U24_3LE) |
+			(1ul << SND_PCM_FORMAT_U24_3BE) |
+			(1ul << SND_PCM_FORMAT_S20_3LE) |
+			(1ul << SND_PCM_FORMAT_S20_3BE) |
+			(1ul << SND_PCM_FORMAT_U20_3LE) |
+			(1ul << SND_PCM_FORMAT_U20_3BE) |
+			(1ul << SND_PCM_FORMAT_S18_3LE) |
+			(1ul << SND_PCM_FORMAT_S18_3BE) |
+			(1ul << SND_PCM_FORMAT_U18_3LE) |
+			(1ul << SND_PCM_FORMAT_U18_3BE) |
+			(1ul << SND_PCM_FORMAT_DSD_U8) |
+			(1ul << SND_PCM_FORMAT_DSD_U16_LE) |
+			(1ul << SND_PCM_FORMAT_DSD_U32_LE) |
+			(1ul << SND_PCM_FORMAT_DSD_U16_BE) |
+			(1ul << SND_PCM_FORMAT_DSD_U32_BE),
+	};
+	static const uint64_t access_mask =
+		(1ul << SND_PCM_ACCESS_MMAP_INTERLEAVED) |
+		(1ul << SND_PCM_ACCESS_RW_INTERLEAVED);
+	struct test_generator gen = {0};
+	struct container_trial *trial;
+	int i;
+	int begin;
+	int end;
+	bool verbose;
+	int err;
+
+	if (argc > 1) {
+		char *term;
+		begin = strtol(argv[1], &term, 10);
+		if (errno || *term != '\0')
+			return EXIT_FAILURE;
+		if (begin < CONTAINER_FORMAT_RIFF_WAVE &&
+		    begin > CONTAINER_FORMAT_RAW)
+			return -EXIT_FAILURE;
+		end = begin + 1;
+		verbose = true;
+	} else {
+		begin = CONTAINER_FORMAT_RIFF_WAVE;
+		end = CONTAINER_FORMAT_RAW + 1;
+		verbose = false;
+	}
+
+	for (i = begin; i < end; ++i) {
+		err = generator_context_init(&gen, access_mask,
+					     sample_format_masks[i],
+					     1, 128, 23, 4500, 1024,
+					     sizeof(struct container_trial));
+		if (err >= 0) {
+			trial = gen.private_data;
+			trial->format = i;
+			trial->verbose = verbose;
+			err = generator_context_run(&gen, callback);
+		}
+
+		generator_context_destroy(&gen);
+
+		if (err < 0)
+			break;
+	}
+
+	if (err < 0) {
+		printf("%s\n", strerror(-err));
+		return EXIT_FAILURE;
+	}
+
+	return EXIT_SUCCESS;
+}
diff --git a/axfer/test/generator.c b/axfer/test/generator.c
new file mode 100644
index 00000000..cdea2c9
--- /dev/null
+++ b/axfer/test/generator.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// allocator.h - a header of a generator for test with buffers of PCM frames.
+//
+// Copyright (c) 2018 Takashi Sakamoto <o-takashi at sakamocchi.jp>
+//
+// Licensed under the terms of the GNU General Public License, version 2.
+
+#include "generator.h"
+
+#include <stdlib.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <unistd.h>
+
+int generator_context_init(struct test_generator *gen,
+			   uint64_t access_mask, uint64_t sample_format_mask,
+			   unsigned int min_samples_per_frame,
+			   unsigned int max_samples_per_frame,
+			   unsigned int min_frame_count,
+			   unsigned int max_frame_count,
+			   unsigned int step_frame_count,
+			   unsigned int private_size)
+{
+	gen->fd = open("/dev/urandom", O_RDONLY);
+	if (gen->fd < 0)
+		return -errno;
+
+	gen->private_data = malloc(private_size);
+	if (gen->private_data == NULL)
+		return -ENOMEM;
+	memset(gen->private_data, 0, private_size);
+
+	gen->access_mask = access_mask;
+	gen->sample_format_mask = sample_format_mask;
+	gen->min_samples_per_frame = min_samples_per_frame;
+	gen->max_samples_per_frame = max_samples_per_frame;
+	gen->min_frame_count = min_frame_count;
+	gen->max_frame_count = max_frame_count;
+	gen->step_frame_count = step_frame_count;
+
+	return 0;
+}
+
+static void *allocate_buf(snd_pcm_access_t access,
+			  snd_pcm_format_t sample_format,
+			  unsigned int samples_per_frame,
+			  unsigned int frame_count)
+{
+	unsigned int bytes_per_sample;
+
+	bytes_per_sample = snd_pcm_format_physical_width(sample_format) / 8;
+
+	return calloc(samples_per_frame * frame_count, bytes_per_sample);
+}
+
+static void *allocate_vector(snd_pcm_access_t access,
+			     snd_pcm_format_t sample_format,
+			     unsigned int samples_per_frame,
+			     unsigned int frame_count)
+{
+	unsigned int bytes_per_sample;
+	char **bufs;
+	int i;
+
+	bytes_per_sample = snd_pcm_format_physical_width(sample_format) / 8;
+
+	bufs = calloc(samples_per_frame, sizeof(char *));
+	if (bufs == NULL)
+		return NULL;
+
+	for (i = 0; i < samples_per_frame; ++i) {
+		bufs[i] = calloc(frame_count, bytes_per_sample);
+		if (bufs[i] == NULL) {
+			for (; i >= 0; --i)
+				free(bufs[i]);
+			free(bufs);
+			return NULL;
+		}
+	}
+
+	return bufs;
+}
+
+static int fill_buf(int fd, void *frame_buffer, snd_pcm_access_t access,
+		    snd_pcm_format_t sample_format,
+		    unsigned int samples_per_frame, unsigned int frame_count)
+{
+	unsigned int size;
+	int len;
+
+	size = snd_pcm_format_physical_width(sample_format) / 8 *
+						samples_per_frame * frame_count;
+	while (size > 0) {
+		len = read(fd, frame_buffer, size);
+		if (len < 0)
+			return len;
+		size -= len;
+	}
+
+	return 0;
+}
+
+static int fill_vector(int fd, void *frame_buffer, snd_pcm_access_t access,
+		       snd_pcm_format_t sample_format,
+		       unsigned int samples_per_frame, unsigned int frame_count)
+{
+	char **bufs = frame_buffer;
+	unsigned int size;
+	int len;
+	int i;
+
+	for (i = 0; i < samples_per_frame; ++i) {
+		size = frame_count *
+			snd_pcm_format_physical_width(sample_format) / 8;
+
+		while (size > 0) {
+			len = read(fd, bufs[i], size);
+			if (len < 0)
+				return len;
+			size -= len;
+		}
+	}
+
+	return 0;
+}
+
+static void deallocate_buf(void *frame_buffer, unsigned int samples_per_frame)
+{
+	free(frame_buffer);
+}
+
+static void deallocate_vector(void *frame_buffer,
+			      unsigned int samples_per_frame)
+{
+	char **bufs = frame_buffer;
+	int i;
+
+	for (i = 0; i < samples_per_frame; ++i)
+		free(bufs[i]);
+
+	free(bufs);
+}
+
+static int test_frame_count(struct test_generator *gen,
+			    snd_pcm_access_t access,
+			    snd_pcm_format_t sample_format,
+			    unsigned int samples_per_frame)
+{
+	void *(*allocator)(snd_pcm_access_t access,
+			   snd_pcm_format_t sample_format,
+			   unsigned int samples_per_frame,
+			   unsigned int frame_count);
+	int (*fill)(int fd, void *frame_buffer, snd_pcm_access_t access,
+		    snd_pcm_format_t sample_format,
+		    unsigned int samples_per_frame, unsigned int frame_count);
+	void (*deallocator)(void *frame_buffer, unsigned int samples_per_frame);
+	void *frame_buffer;
+	int i;
+	int err = 0;
+
+	if (access != SND_PCM_ACCESS_RW_NONINTERLEAVED) {
+		allocator = allocate_buf;
+		fill = fill_buf;
+		deallocator = deallocate_buf;
+	} else {
+		allocator = allocate_vector;
+		fill = fill_vector;
+		deallocator = deallocate_vector;
+	}
+
+	frame_buffer = allocator(access, sample_format, samples_per_frame,
+				 gen->max_frame_count);
+	if (frame_buffer == NULL)
+		return -ENOMEM;
+
+	err = fill(gen->fd, frame_buffer, access, sample_format,
+		   samples_per_frame, gen->max_frame_count);
+	if (err < 0)
+		goto end;
+
+
+	for (i = gen->min_frame_count;
+	     i <= gen->max_frame_count; i += gen->step_frame_count) {
+		err = gen->cb(gen, access ,sample_format, samples_per_frame,
+			      frame_buffer, i);
+		if (err < 0)
+			break;
+	}
+end:
+	deallocator(frame_buffer, samples_per_frame);
+
+	return err;
+}
+
+static int test_samples_per_frame(struct test_generator *gen,
+				  snd_pcm_access_t access,
+				  snd_pcm_format_t sample_format)
+{
+	int i;
+	int err = 0;
+
+	for (i = gen->min_samples_per_frame;
+	     i <= gen->max_samples_per_frame; ++i) {
+		err = test_frame_count(gen, access, sample_format, i);
+		if (err < 0)
+			break;
+	}
+
+	return err;
+}
+
+static int test_sample_format(struct test_generator *gen,
+			      snd_pcm_access_t access)
+{
+	int i;
+	int err = 0;
+
+	for (i = 0; i <= SND_PCM_FORMAT_LAST; ++i) {
+		if (!((1ul << i) & gen->sample_format_mask))
+			continue;
+
+		err = test_samples_per_frame(gen, access, i);
+		if (err < 0)
+			break;
+	}
+
+	return err;
+}
+
+static int test_access(struct test_generator *gen)
+{
+	int i;
+	int err = 0;
+
+	for (i = 0; i <= SND_PCM_ACCESS_LAST; ++i) {
+		if (!((1ul << i) & gen->access_mask))
+			continue;
+
+		err = test_sample_format(gen, i);
+		if (err < 0)
+			break;
+	}
+	return err;
+}
+
+int generator_context_run(struct test_generator *gen, generator_cb_t cb)
+{
+	gen->cb = cb;
+	return test_access(gen);
+}
+
+void generator_context_destroy(struct test_generator *gen)
+{
+	free(gen->private_data);
+	close(gen->fd);
+}
diff --git a/axfer/test/generator.h b/axfer/test/generator.h
new file mode 100644
index 00000000..c35ed98
--- /dev/null
+++ b/axfer/test/generator.h
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// generator.c - a generator for test with buffers of PCM frames.
+//
+// Copyright (c) 2018 Takashi Sakamoto <o-takashi at sakamocchi.jp>
+//
+// Licensed under the terms of the GNU General Public License, version 2.
+
+#ifndef __ALSA_UTILS_AXFER_TEST_GENERATOR__H_
+#define __ALSA_UTILS_AXFER_TEST_GENERATOR__H_
+
+#include <stdint.h>
+#include <alsa/asoundlib.h>
+
+struct test_generator;
+typedef int (*generator_cb_t)(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);
+
+struct test_generator {
+	int fd;
+	uint64_t access_mask;
+	uint64_t sample_format_mask;
+	unsigned int min_samples_per_frame;
+	unsigned int max_samples_per_frame;
+	unsigned int min_frame_count;
+	unsigned int max_frame_count;
+	unsigned int step_frame_count;
+
+	generator_cb_t cb;
+	void *private_data;
+};
+
+int generator_context_init(struct test_generator *gen,
+			   uint64_t access_mask, uint64_t sample_format_mask,
+			   unsigned int min_samples_per_frame,
+			   unsigned int max_samples_per_frame,
+			   unsigned int min_frame_count,
+			   unsigned int max_frame_count,
+			   unsigned int step_frame_count,
+			   unsigned int private_size);
+int generator_context_run(struct test_generator *gen, generator_cb_t cb);
+void generator_context_destroy(struct test_generator *gen);
+
+#endif
diff --git a/configure.ac b/configure.ac
index 404fa16..1c64617 100644
--- a/configure.ac
+++ b/configure.ac
@@ -430,4 +430,5 @@ AC_OUTPUT(Makefile alsactl/Makefile alsactl/init/Makefile \
 	  utils/alsa-utils.spec seq/Makefile seq/aconnect/Makefile \
 	  seq/aplaymidi/Makefile seq/aseqdump/Makefile seq/aseqnet/Makefile \
 	  speaker-test/Makefile speaker-test/samples/Makefile \
-	  alsaloop/Makefile alsa-info/Makefile axfer/Makefile)
+	  alsaloop/Makefile alsa-info/Makefile \
+	  axfer/Makefile axfer/test/Makefile)
-- 
2.19.1



More information about the Alsa-devel mailing list