axfer: add a unit test for mapper interface

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@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Sakamoto 2018-11-13 15:41:24 +09:00 committed by Takashi Iwai
parent 25c8e3bebb
commit 39d1ab8a0c
2 changed files with 509 additions and 2 deletions

View file

@ -1,8 +1,10 @@
TESTS = \ TESTS = \
container-test container-test \
mapper-test
check_PROGRAMS = \ check_PROGRAMS = \
container-test container-test \
mapper-test
container_test_SOURCES = \ container_test_SOURCES = \
../container.h \ ../container.h \
@ -13,3 +15,17 @@ container_test_SOURCES = \
../container-raw.c \ ../container-raw.c \
generator.c \ generator.c \
container-test.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

491
axfer/test/mapper-test.c Normal file
View file

@ -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@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;
}