blob: d5ece9adc4591f26ca0d99e67b91ccb2a1c95d45 [file] [log] [blame]
/*
* Copyright (c) 2018, Intel Corporation
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Intel Corporation nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* Author: Slawomir Blauciak <slawomir.blauciak@linux.intel.com>
*/
#include <stdarg.h>
#include <stddef.h>
#include <math.h>
#include <setjmp.h>
#include <cmocka.h>
#include <sof/list.h>
#include <sof/ipc.h>
#include <sof/audio/buffer.h>
#include <sof/audio/component.h>
#include <sof/audio/format.h>
#include "comp_mock.h"
#define MIX_TEST_SAMPLES 32
struct comp_driver drv_mock;
struct comp_driver mixer_drv_mock;
struct comp_dev *mixer_dev_mock;
struct comp_dev *post_mixer_comp;
struct comp_buffer *post_mixer_buf;
/* Mocking comp_register here so we can register our components properly */
int comp_register(struct comp_driver *drv)
{
void *dst;
switch (drv->type) {
case SOF_COMP_MIXER:
dst = &mixer_drv_mock;
break;
case SOF_COMP_MOCK:
dst = &drv_mock;
break;
}
memcpy(dst, drv, sizeof(struct comp_driver));
return 0;
}
struct source {
struct comp_dev *comp;
struct comp_buffer *buf;
};
struct mix_test_case {
int num_sources;
int num_chans;
const char *name;
struct source *sources;
};
#define TEST_CASE(_num_sources, _num_chans) \
{ \
.num_sources = (_num_sources), \
.num_chans = (_num_chans), \
.name = ("test_audio_mixer_copy_" \
#_num_sources "_srcs_" \
#_num_chans "ch"), \
.sources = NULL \
}
static struct mix_test_case mix_test_cases[] = {
TEST_CASE(1, 2),
TEST_CASE(1, 4),
TEST_CASE(1, 8),
TEST_CASE(2, 2),
TEST_CASE(2, 4),
TEST_CASE(2, 8),
TEST_CASE(3, 2),
TEST_CASE(4, 2),
TEST_CASE(6, 2),
TEST_CASE(8, 2)
};
static struct sof_ipc_comp mixer = {
.type = SOF_COMP_MIXER
};
static struct sof_ipc_comp mock_comp = {
.type = SOF_COMP_MOCK
};
static struct comp_dev *create_comp(struct sof_ipc_comp *comp,
struct comp_driver *drv)
{
struct comp_dev *cd = drv->ops.new(comp);
assert_non_null(cd);
memcpy(&cd->comp, comp, sizeof(*comp));
cd->drv = drv;
spinlock_init(&cd->lock);
list_init(&cd->bsource_list);
list_init(&cd->bsink_list);
return cd;
}
static void destroy_comp(struct comp_driver *drv, struct comp_dev *dev)
{
drv->ops.free(dev);
}
static void create_sources(struct mix_test_case *tc)
{
int src_idx;
tc->sources = malloc(tc->num_sources * sizeof(struct source));
for (src_idx = 0; src_idx < tc->num_sources; ++src_idx) {
struct source *src = &tc->sources[src_idx];
struct sof_ipc_buffer buf = {
.size = (MIX_TEST_SAMPLES * sizeof(uint32_t)) *
tc->num_chans
};
src->comp = create_comp(&mock_comp, &drv_mock);
src->buf = buffer_new(&buf);
src->buf->source = src->comp;
src->buf->sink = mixer_dev_mock;
list_item_prepend(&src->buf->source_list,
&src->comp->bsink_list);
list_item_prepend(&src->buf->sink_list,
&mixer_dev_mock->bsource_list);
}
}
static void destroy_sources(struct mix_test_case *tc)
{
int src_idx;
for (src_idx = 0; src_idx < tc->num_sources; ++src_idx)
destroy_comp(&drv_mock, tc->sources[src_idx].comp);
free(tc->sources);
}
static void activate_periph_comps(struct mix_test_case *tc)
{
int src_idx;
for (src_idx = 0; src_idx < tc->num_sources; ++src_idx)
tc->sources[src_idx].comp->state = COMP_STATE_ACTIVE;
post_mixer_comp->state = COMP_STATE_ACTIVE;
}
static int test_group_setup(void **state)
{
sys_comp_mixer_init();
sys_comp_mock_init();
return 0;
}
static int test_setup(void **state)
{
mixer_dev_mock = create_comp(&mixer, &mixer_drv_mock);
struct mix_test_case *tc = *((struct mix_test_case **)state);
if (tc) {
struct sof_ipc_buffer buf = {
.size = (MIX_TEST_SAMPLES * sizeof(uint32_t)) *
tc->num_chans
};
post_mixer_buf = buffer_new(&buf);
create_sources(tc);
post_mixer_comp = create_comp(&mock_comp, &drv_mock);
activate_periph_comps(tc);
mixer_drv_mock.ops.prepare(mixer_dev_mock);
mixer_dev_mock->state = COMP_STATE_ACTIVE;
post_mixer_buf->source = mixer_dev_mock;
post_mixer_buf->sink = post_mixer_comp;
list_item_prepend(&post_mixer_buf->source_list,
&mixer_dev_mock->bsink_list);
list_item_prepend(&post_mixer_buf->sink_list,
&post_mixer_comp->bsource_list);
mixer_dev_mock->frames = MIX_TEST_SAMPLES;
}
return 0;
}
static int test_teardown(void **state)
{
destroy_comp(&mixer_drv_mock, mixer_dev_mock);
struct mix_test_case *tc = *((struct mix_test_case **)state);
if (tc) {
buffer_free(post_mixer_buf);
destroy_sources(tc);
}
return 0;
}
/*
* Tests
*/
static void test_audio_mixer_new(void **state)
{
assert_non_null(mixer_dev_mock->private);
}
static void test_audio_mixer_prepare_no_sources(void **state)
{
int downstream = mixer_drv_mock.ops.prepare(mixer_dev_mock);
assert_int_equal(downstream, 0);
}
static void test_audio_mixer_copy(void **state)
{
int src_idx;
int smp;
struct mix_test_case *tc = *((struct mix_test_case **)state);
mixer_dev_mock->params.channels = tc->num_chans;
for (src_idx = 0; src_idx < tc->num_sources; ++src_idx) {
uint32_t *samples = tc->sources[src_idx].buf->addr;
for (smp = 0; smp < MIX_TEST_SAMPLES; ++smp) {
double rad = M_PI / (180.0 / (smp * (src_idx + 1)));
samples[smp] = ((sin(rad) + 1) / 2) * (0xFFFFFFFF / 2);
}
}
mixer_drv_mock.ops.copy(mixer_dev_mock);
for (smp = 0; smp < MIX_TEST_SAMPLES; ++smp) {
uint64_t sum = 0;
for (src_idx = 0; src_idx < tc->num_sources; ++src_idx) {
assert_non_null(tc->sources[src_idx].buf);
uint32_t *samples = tc->sources[src_idx].buf->addr;
sum += samples[smp];
}
sum = sat_int32(sum);
uint32_t *out_samples = post_mixer_buf->addr;
assert_int_equal(out_samples[smp], sum);
}
}
int main(void)
{
struct CMUnitTest tests[ARRAY_SIZE(mix_test_cases) + 2];
int i;
int cur_test_case = 0;
tests[0].test_func = test_audio_mixer_new;
tests[0].initial_state = NULL;
tests[0].setup_func = test_setup;
tests[0].teardown_func = test_teardown;
tests[0].name = "test_audio_mixer_new";
tests[1].test_func = test_audio_mixer_prepare_no_sources;
tests[1].initial_state = NULL;
tests[1].setup_func = test_setup;
tests[1].teardown_func = test_teardown;
tests[1].name = "test_audio_mixer_prepare_no_sources";
for (i = 2; i < ARRAY_SIZE(tests); (++i, ++cur_test_case)) {
tests[i].test_func = test_audio_mixer_copy;
tests[i].initial_state = &mix_test_cases[cur_test_case];
tests[i].setup_func = test_setup;
tests[i].teardown_func = test_teardown;
tests[i].name = mix_test_cases[cur_test_case].name;
}
cmocka_set_message_output(CM_OUTPUT_TAP);
return cmocka_run_group_tests(tests, test_group_setup, NULL);
}