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 0000000..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 +// +// Licensed under the terms of the GNU General Public License, version 2. + +#include "../mapper.h" +#include "../misc.h" + +#include "generator.h" + +#include +#include +#include + +#include + +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; +}