tests: add procqueue test

This test is intended to validate the processing queue
management API. Moreover, the talloc debugging API is
used to ensure that there are no memory leaks.

First, four processing queues are being allocated. One
of them is empty, while others have different count of
items. Then the human-readable description is being
generated for all of them. And finally, the processing
and exit cllback are being tested.

During the test execution, the talloc NULL-context
tracking feature is enabled, allowing to observe every
memory allocation within the libosmogapk, and to detect
memory leaks.
This commit is contained in:
Vadim Yanitskiy 2017-09-12 15:32:52 +03:00
parent f069eb37fe
commit 1fe6a9b9ed
4 changed files with 450 additions and 0 deletions

View File

@ -11,6 +11,15 @@ AM_CFLAGS = \
$(NULL)
check_PROGRAMS = \
procqueue/pq_test \
$(NULL)
procqueue_pq_test_SOURCES = procqueue/pq_test.c
procqueue_pq_test_LDADD = \
$(top_builddir)/src/libosmogapk.la \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODEC_LIBS) \
$(TALLOC_LIBS) \
$(NULL)
# The `:;' works around a Bash 3.2 bug when the output is not writeable.
@ -37,6 +46,10 @@ EXTRA_DIST = \
$(srcdir)/package.m4 \
$(NULL)
EXTRA_DIST += \
procqueue/pq_test.ok \
$(NULL)
DISTCLEANFILES = atconfig
TESTSUITE = $(srcdir)/testsuite

371
tests/procqueue/pq_test.c Normal file
View File

@ -0,0 +1,371 @@
/*
* This file is part of GAPK (GSM Audio Pocket Knife).
*
* (C) 2017 by Vadim Yanitskiy <axilirator@gmail.com>
*
* GAPK is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GAPK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GAPK. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <talloc.h>
#include <assert.h>
#include <osmocom/gapk/procqueue.h>
#include <osmocom/gapk/common.h>
/**
* This test is intended to validate the processing queue
* management API. Moreover, the talloc debugging API is
* used to ensure that there are no memory leaks.
*
* First, four processing queues are being allocated. One
* of them is empty, while others have different count of
* items. Then the human-readable description is being
* generated for all of them. And finally, the processing
* and exit callback are being tested.
*
* During the test execution, the talloc NULL-context
* tracking feature is enabled, allowing to observe every
* memory allocation within the libosmogapk, and to detect
* memory leaks.
*/
static void talloc_ctx_walk_cb(const void *chunk, int depth,
int max_depth, int is_ref, void *data)
{
const char *chunk_name = talloc_get_name(chunk);
int spaces_cnt;
/* Hierarchical spacing */
for (spaces_cnt = 0; spaces_cnt < depth; spaces_cnt++)
printf(" ");
/* Chunk info */
printf("chunk %s: depth=%d\n", chunk_name, depth);
}
static int q3_i0_proc(void *state, uint8_t *out, const uint8_t *in,
unsigned int in_len)
{
int i;
printf("Incoming data: ");
for (i = 0; i < 10; i++) {
printf("%d ", i);
out[i] = i;
}
printf("\n");
return 0;
}
static int q3_i1_proc(void *state, uint8_t *out, const uint8_t *in,
unsigned int in_len)
{
int i;
for (i = 0; i < 10; i++)
out[i] = in[i] % 2;
return 0;
}
static int q3_i2_proc(void *state, uint8_t *out, const uint8_t *in,
unsigned int in_len)
{
int i;
printf("Outgoing data: ");
for (i = 0; i < 10; i++)
printf("%d ", in[i]);
printf("\n");
return 0;
}
static void q3_exit(void *state)
{
struct osmo_gapk_pq_item *item;
/* Make sure the item's state is passed correctly */
assert(state != NULL);
item = (struct osmo_gapk_pq_item *) state;
printf("Calling exit callback for '%s/%s'\n",
item->cat_name, item->sub_name);
}
int main(int argc, char **argv)
{
/* Enable tracking the use of NULL memory contexts */
talloc_enable_null_tracking();
/**
* 1. Processing queue allocation test
*/
struct osmo_gapk_pq *q0, *q1, *q2, *q3;
printf("Processing queue allocation test:\n");
/* Allocate four queues */
q0 = osmo_gapk_pq_create(NULL);
q1 = osmo_gapk_pq_create("q1");
q2 = osmo_gapk_pq_create("q2");
q3 = osmo_gapk_pq_create("q3");
/* Make sure all queues are allocated */
assert(q0 != NULL);
assert(q1 != NULL);
assert(q2 != NULL);
assert(q3 != NULL);
/* Print talloc memory hierarchy */
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
printf("\n");
/**
* 2. Item allocation test
*/
struct osmo_gapk_pq_item *q3_i0, *q3_i1, *q3_i2;
struct osmo_gapk_pq_item *q2_i0, *q2_i1;
struct osmo_gapk_pq_item *q1_i0;
printf("Item allocation test:\n");
/* Make sure there are no items */
assert(q0->n_items == 0);
assert(q1->n_items == 0);
assert(q2->n_items == 0);
assert(q3->n_items == 0);
/* Allocate items */
q3_i0 = osmo_gapk_pq_add_item(q3);
q3_i1 = osmo_gapk_pq_add_item(q3);
q3_i2 = osmo_gapk_pq_add_item(q3);
q2_i0 = osmo_gapk_pq_add_item(q2);
q2_i1 = osmo_gapk_pq_add_item(q2);
q1_i0 = osmo_gapk_pq_add_item(q1);
/* Make sure all items are allocated */
assert(q3_i0 != NULL);
assert(q3_i1 != NULL);
assert(q3_i2 != NULL);
assert(q2_i0 != NULL);
assert(q2_i1 != NULL);
assert(q1_i0 != NULL);
/* Check item count */
assert(q0->n_items == 0);
assert(q1->n_items == 1);
assert(q2->n_items == 2);
assert(q3->n_items == 3);
/* Print talloc memory hierarchy */
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
printf("\n");
/**
* 3. Items persistence test
*/
struct osmo_gapk_pq_item *item;
/* Make sure that q0 is empty, others are not */
assert(llist_empty(&q0->items) == 1);
assert(llist_empty(&q1->items) == 0);
assert(llist_empty(&q2->items) == 0);
assert(llist_empty(&q3->items) == 0);
/* A single item must be the first and the last in a queue */
item = llist_first_entry(&q1->items, struct osmo_gapk_pq_item, list);
assert(item == q1_i0);
item = llist_last_entry(&q1->items, struct osmo_gapk_pq_item, list);
assert(item == q1_i0);
/* Two items: one is the first, second is the last */
item = llist_first_entry(&q2->items, struct osmo_gapk_pq_item, list);
assert(item == q2_i0);
item = llist_last_entry(&q2->items, struct osmo_gapk_pq_item, list);
assert(item == q2_i1);
/* Three items: one is the first, third is the last */
item = llist_first_entry(&q3->items, struct osmo_gapk_pq_item, list);
assert(item == q3_i0);
assert(item != q3_i1);
item = llist_last_entry(&q3->items, struct osmo_gapk_pq_item, list);
assert(item == q3_i2);
assert(item != q3_i1);
/**
* 4. Queue I/O data lengths check
*/
q3_i0->len_in = 10;
q3_i0->len_out = 20;
q3_i1->len_in = 20;
q3_i1->len_out = 30;
q3_i2->len_in = 30;
q3_i2->len_out = 10;
/* Normal case */
assert(osmo_gapk_pq_check(q3, 0) == 0);
/* Abnormal case (I/O length mismatch) */
q3_i0->len_out = 10;
assert(osmo_gapk_pq_check(q3, 0) != 0);
q3_i0->len_out = 20;
/* Check queue in strict mode (requires src -> ... -> sink) */
q3_i0->type = OSMO_GAPK_ITEM_TYPE_SOURCE;
q3_i1->type = OSMO_GAPK_ITEM_TYPE_PROC;
q3_i2->type = OSMO_GAPK_ITEM_TYPE_SINK;
/* Normal case (src -> proc -> sink) */
assert(osmo_gapk_pq_check(q3, 1) == 0);
/* Abnormal case (proc -> proc -> sink) */
q3_i0->type = OSMO_GAPK_ITEM_TYPE_PROC;
assert(osmo_gapk_pq_check(q3, 1) != 0);
q3_i0->type = OSMO_GAPK_ITEM_TYPE_SOURCE;
/**
* 5. Buffer allocation test
*/
printf("Queue preparation test:\n");
/* Prepare the queue */
assert(osmo_gapk_pq_prepare(q3) == 0);
/* Make sure buffers were allocated */
assert(q3_i0->buf != NULL);
assert(q3_i1->buf != NULL);
/* Currently, we don't allocate buffers for sinks */
assert(q3_i2->buf == NULL);
/* Compare required vs allocated buffer sizes */
assert(talloc_total_size(q3_i0->buf) == q3_i0->len_out);
assert(talloc_total_size(q3_i1->buf) == q3_i1->len_out);
/* Print talloc memory hierarchy */
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
printf("\n");
/**
* 6. Queue description test
*/
char *queue_desc;
printf("Queue description test:\n");
/* An empty queue doesn't have description */
queue_desc = osmo_gapk_pq_describe(q0);
assert(queue_desc == NULL);
/* Fill in some data */
q3_i0->cat_name = "source";
q3_i0->sub_name = "file";
q3_i1->cat_name = "proc";
q3_i1->sub_name = "dummy";
q3_i2->cat_name = "sink";
q3_i2->sub_name = "alsa";
q2_i0->cat_name = "source";
q2_i0->sub_name = "dummy";
q2_i1->cat_name = "sink";
q2_i1->sub_name = "dummy";
q1_i0->cat_name = "dummy";
q1_i0->sub_name = "dummy";
queue_desc = osmo_gapk_pq_describe(q3);
assert(queue_desc != NULL);
printf("Queue q3 description: %s\n", queue_desc);
talloc_free(queue_desc);
queue_desc = osmo_gapk_pq_describe(q2);
assert(queue_desc != NULL);
printf("Queue q2 description: %s\n", queue_desc);
talloc_free(queue_desc);
queue_desc = osmo_gapk_pq_describe(q1);
assert(queue_desc != NULL);
printf("Queue q1 description: %s\n\n", queue_desc);
talloc_free(queue_desc);
/**
* 7. Queue execution test
*/
printf("Processing queue execution test:\n");
/* Make sure there are no callbacks by default */
assert(q3_i0->proc == NULL);
assert(q3_i0->wait == NULL);
assert(q3_i0->exit == NULL);
assert(q3_i1->proc == NULL);
assert(q3_i1->wait == NULL);
assert(q3_i1->exit == NULL);
assert(q3_i2->proc == NULL);
assert(q3_i2->wait == NULL);
assert(q3_i2->exit == NULL);
q3_i0->proc = &q3_i0_proc;
q3_i1->proc = &q3_i1_proc;
q3_i2->proc = &q3_i2_proc;
assert(osmo_gapk_pq_execute(q3) == 0);
printf("\n");
/**
* 8. Exit callback & deallocation tests
*/
printf("Processing queue exit callback test:\n");
q3_i0->state = q3_i0;
q3_i1->state = q3_i1;
q3_i2->state = q3_i2;
q3_i0->exit = &q3_exit;
q3_i1->exit = &q3_exit;
q3_i2->exit = &q3_exit;
osmo_gapk_pq_destroy(q0);
osmo_gapk_pq_destroy(q1);
osmo_gapk_pq_destroy(q2);
osmo_gapk_pq_destroy(q3);
printf("\n");
/**
* 9. Memory leak detection test
*/
printf("Processing queue deallocation test:\n");
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
return 0;
}

View File

@ -0,0 +1,60 @@
Processing queue allocation test:
chunk null_context: depth=0
chunk struct osmo_gapk_pq 'q3': depth=1
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q2': depth=1
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q1': depth=1
chunk .name: depth=2
chunk struct osmo_gapk_pq: depth=1
Item allocation test:
chunk null_context: depth=0
chunk struct osmo_gapk_pq 'q3': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk struct osmo_gapk_pq_item: depth=2
chunk struct osmo_gapk_pq_item: depth=2
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q2': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk struct osmo_gapk_pq_item: depth=2
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q1': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk .name: depth=2
chunk struct osmo_gapk_pq: depth=1
Queue preparation test:
chunk null_context: depth=0
chunk struct osmo_gapk_pq 'q3': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk struct osmo_gapk_pq_item: depth=2
chunk .buffer: depth=3
chunk struct osmo_gapk_pq_item: depth=2
chunk .buffer: depth=3
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q2': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk struct osmo_gapk_pq_item: depth=2
chunk .name: depth=2
chunk struct osmo_gapk_pq 'q1': depth=1
chunk struct osmo_gapk_pq_item: depth=2
chunk .name: depth=2
chunk struct osmo_gapk_pq: depth=1
Queue description test:
Queue q3 description: source/file -> proc/dummy -> sink/alsa
Queue q2 description: source/dummy -> sink/dummy
Queue q1 description: dummy/dummy
Processing queue execution test:
Incoming data: 0 1 2 3 4 5 6 7 8 9
Outgoing data: 0 1 0 1 0 1 0 1 0 1
Processing queue exit callback test:
Calling exit callback for 'source/file'
Calling exit callback for 'proc/dummy'
Calling exit callback for 'sink/alsa'
Processing queue deallocation test:
chunk null_context: depth=0

View File

@ -1,2 +1,8 @@
AT_INIT
AT_BANNER([Regression tests.])
AT_SETUP([procqueue])
AT_KEYWORDS([procqueue])
cat $abs_srcdir/procqueue/pq_test.ok > expout
AT_CHECK([$abs_top_builddir/tests/procqueue/pq_test], [0], [expout])
AT_CLEANUP