[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