mirror of https://gerrit.osmocom.org/gapk
tests: add pq_rtp test
This test is intended to check the RTP source / sink operability. To do this, two processing queues are being allocated: "generator": source/random -> sink/rtp "checker": source/rtp -> sink/checker The first one generates some amount of random bytes (payload), and stores them inside a buffer that is shared between both queues. After generation, a payload is being sent from the first queue via an RTP sink, and then being received by the second via an RTP source. As both queues do use a shared buffer, the last item of the second queue (named 'sink/checker') is able to compare a received payload with expected.
This commit is contained in:
parent
3e9e57fb40
commit
9d2b15dc8a
|
@ -13,6 +13,7 @@ AM_CFLAGS = \
|
|||
check_PROGRAMS = \
|
||||
procqueue/pq_test \
|
||||
io/pq_file_test \
|
||||
io/pq_rtp_test \
|
||||
$(NULL)
|
||||
|
||||
procqueue_pq_test_SOURCES = procqueue/pq_test.c
|
||||
|
@ -31,6 +32,14 @@ io_pq_file_test_LDADD = \
|
|||
$(TALLOC_LIBS) \
|
||||
$(NULL)
|
||||
|
||||
io_pq_rtp_test_SOURCES = io/pq_rtp_test.c
|
||||
io_pq_rtp_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.
|
||||
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
|
||||
:;{ \
|
||||
|
@ -58,6 +67,7 @@ EXTRA_DIST = \
|
|||
EXTRA_DIST += \
|
||||
procqueue/pq_test.ok \
|
||||
io/pq_file_test.ok \
|
||||
io/pq_rtp_test.ok \
|
||||
io/io_sample.txt \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* 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 <time.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <talloc.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include <osmocom/core/socket.h>
|
||||
|
||||
#include <osmocom/gapk/procqueue.h>
|
||||
#include <osmocom/gapk/common.h>
|
||||
|
||||
/**
|
||||
* This test is intended to check the RTP source / sink operability.
|
||||
* To do this, two processing queues are being allocated:
|
||||
*
|
||||
* "generator": source/random -> sink/rtp
|
||||
* "checker": source/rtp -> sink/checker
|
||||
*
|
||||
* The first one generates some amount of random bytes (payload),
|
||||
* and stores them inside a buffer that is shared between both
|
||||
* queues.
|
||||
*
|
||||
* After generation, a payload is being sent from the first
|
||||
* queue via an RTP sink, and then being received by the second
|
||||
* via an RTP source.
|
||||
*
|
||||
* As both queues do use a shared buffer, the last item of the
|
||||
* second queue (named 'sink/checker') is able to compare a
|
||||
* received payload with expected.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
#define RTP_TEST_BUF_LEN 128
|
||||
|
||||
struct rtp_test_state {
|
||||
unsigned int payload_len;
|
||||
unsigned int rtp_port;
|
||||
int rtp_src_fd;
|
||||
int rtp_dst_fd;
|
||||
|
||||
uint8_t data[RTP_TEST_BUF_LEN];
|
||||
uint8_t *ptr;
|
||||
};
|
||||
|
||||
static int src_rand_proc(void *data, uint8_t *out, const uint8_t *in,
|
||||
unsigned int in_len)
|
||||
{
|
||||
struct rtp_test_state *state = (struct rtp_test_state *) data;
|
||||
unsigned int i;
|
||||
|
||||
/* Generate a random payload */
|
||||
for (i = 0; i < state->payload_len; i++) {
|
||||
uint8_t byte = rand() % 0xff;
|
||||
*(state->ptr + i) = byte;
|
||||
out[i] = byte;
|
||||
}
|
||||
|
||||
return state->payload_len;
|
||||
}
|
||||
|
||||
static void src_rand_exit(void *data)
|
||||
{
|
||||
struct rtp_test_state *state = (struct rtp_test_state *) data;
|
||||
|
||||
if (state->rtp_src_fd >= 0) {
|
||||
close(state->rtp_src_fd);
|
||||
state->rtp_src_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int sink_chk_proc(void *data, uint8_t *out, const uint8_t *in,
|
||||
unsigned int in_len)
|
||||
{
|
||||
struct rtp_test_state *state = (struct rtp_test_state *) data;
|
||||
unsigned int i;
|
||||
|
||||
/* Make sure we have all bytes transferred */
|
||||
if (in_len != state->payload_len) {
|
||||
printf("Data length mismatch!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < in_len; i++) {
|
||||
if (in[i] != *(state->ptr + i)) {
|
||||
printf("Data mismatch!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return in_len;
|
||||
}
|
||||
|
||||
static void sink_chk_exit(void *data)
|
||||
{
|
||||
struct rtp_test_state *state = (struct rtp_test_state *) data;
|
||||
|
||||
if (state->rtp_dst_fd >= 0) {
|
||||
close(state->rtp_dst_fd);
|
||||
state->rtp_dst_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocates: source/random -> sink/rtp */
|
||||
static int init_gen_queue(struct osmo_gapk_pq *pq,
|
||||
struct rtp_test_state *state, unsigned int payload_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Allocate memory for the 'source/random' */
|
||||
struct osmo_gapk_pq_item *src_rand = osmo_gapk_pq_add_item(pq);
|
||||
if (!src_rand)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Fill in meta information */
|
||||
src_rand->type = OSMO_GAPK_ITEM_TYPE_SOURCE;
|
||||
src_rand->cat_name = "source";
|
||||
src_rand->sub_name = "random";
|
||||
|
||||
/* Set I/O buffer lengths */
|
||||
state->payload_len = payload_len;
|
||||
src_rand->len_out = payload_len;
|
||||
|
||||
/* Set proc / exit callbacks and state */
|
||||
src_rand->proc = &src_rand_proc;
|
||||
src_rand->exit = &src_rand_exit;
|
||||
src_rand->state = state;
|
||||
|
||||
|
||||
/* Init connection socket */
|
||||
state->rtp_dst_fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM,
|
||||
IPPROTO_UDP, "127.0.0.1", state->rtp_port, OSMO_SOCK_F_CONNECT);
|
||||
if (state->rtp_dst_fd < 0) {
|
||||
printf("Could not init connection socket\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Init an RTP sink */
|
||||
rc = osmo_gapk_pq_queue_rtp_output(pq, state->rtp_dst_fd, payload_len);
|
||||
if (rc) {
|
||||
printf("Could not init an RTP sink\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check and prepare */
|
||||
rc = osmo_gapk_pq_check(pq, 1);
|
||||
if (rc) {
|
||||
printf("Queue check failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = osmo_gapk_pq_prepare(pq);
|
||||
if (rc) {
|
||||
printf("Queue preparation failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Allocates: source/rtp -> sink/checker */
|
||||
static int init_chk_queue(struct osmo_gapk_pq *pq,
|
||||
struct rtp_test_state *state, unsigned int payload_len)
|
||||
{
|
||||
int rc;
|
||||
|
||||
/* Init listening socket */
|
||||
state->rtp_src_fd = osmo_sock_init(AF_UNSPEC, SOCK_DGRAM,
|
||||
IPPROTO_UDP, "127.0.0.1", 0, OSMO_SOCK_F_BIND);
|
||||
if (state->rtp_src_fd < 0) {
|
||||
printf("Could not init listening socket\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Init an RTP source on any available port */
|
||||
rc = osmo_gapk_pq_queue_rtp_input(pq, state->rtp_src_fd, payload_len);
|
||||
if (rc) {
|
||||
printf("Could not init an RTP sink\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Determine on which port are we listening */
|
||||
struct sockaddr_in adr_inet;
|
||||
socklen_t len_inet;
|
||||
|
||||
len_inet = sizeof(adr_inet);
|
||||
rc = getsockname(state->rtp_src_fd,
|
||||
(struct sockaddr *) &adr_inet, &len_inet);
|
||||
if (rc)
|
||||
return -EINVAL;
|
||||
|
||||
/* Save assigned port to shared state */
|
||||
state->rtp_port = (unsigned int) ntohs(adr_inet.sin_port);
|
||||
|
||||
|
||||
/* Allocate memory for the 'sink/checker' */
|
||||
struct osmo_gapk_pq_item *sink_chk = osmo_gapk_pq_add_item(pq);
|
||||
if (!sink_chk)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Fill in meta information */
|
||||
sink_chk->type = OSMO_GAPK_ITEM_TYPE_SINK;
|
||||
sink_chk->cat_name = "sink";
|
||||
sink_chk->sub_name = "checker";
|
||||
|
||||
/* Set I/O buffer lengths */
|
||||
sink_chk->len_in = payload_len;
|
||||
|
||||
/* Set proc / exit callbacks and state */
|
||||
sink_chk->proc = &sink_chk_proc;
|
||||
sink_chk->exit = &sink_chk_exit;
|
||||
sink_chk->state = state;
|
||||
|
||||
/* Check and prepare */
|
||||
rc = osmo_gapk_pq_check(pq, 1);
|
||||
if (rc) {
|
||||
printf("Queue check failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = osmo_gapk_pq_prepare(pq);
|
||||
if (rc) {
|
||||
printf("Queue preparation failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtp_test(struct rtp_test_state *state, unsigned int payload_len)
|
||||
{
|
||||
struct osmo_gapk_pq *q_gen, *q_chk;
|
||||
unsigned int i, chunks;
|
||||
int rc;
|
||||
|
||||
/* Allocate two queues */
|
||||
q_gen = osmo_gapk_pq_create("generator");
|
||||
q_chk = osmo_gapk_pq_create("checker");
|
||||
|
||||
/* Make sure both queues are allocated */
|
||||
if (!q_gen || !q_chk) {
|
||||
rc = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Init both queues: generator and checker */
|
||||
rc = init_chk_queue(q_chk, state, payload_len);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
rc = init_gen_queue(q_gen, state, payload_len);
|
||||
if (rc)
|
||||
goto exit;
|
||||
|
||||
/* Calculate how much chunks do we have */
|
||||
chunks = RTP_TEST_BUF_LEN / payload_len;
|
||||
|
||||
/* Execute both queues */
|
||||
for (i = 0; i < chunks; i++) {
|
||||
/* Move data pointer */
|
||||
state->ptr = state->data + i * payload_len;
|
||||
|
||||
/* Generate and send a payload */
|
||||
rc = osmo_gapk_pq_execute(q_gen);
|
||||
if (rc) {
|
||||
printf("Queue '%s' execution aborted on chunk %u/%u\n",
|
||||
q_gen->name, i + 1, chunks);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* TODO: prevent test hang if nothing was being sent */
|
||||
|
||||
/* Receive and check a payload */
|
||||
rc = osmo_gapk_pq_execute(q_chk);
|
||||
if (rc) {
|
||||
printf("Queue '%s' execution aborted on chunk %u/%u\n",
|
||||
q_gen->name, i + 1, chunks);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Payload len=%u check ok\n", payload_len);
|
||||
|
||||
exit:
|
||||
/* Deallocate both queues and data */
|
||||
osmo_gapk_pq_destroy(q_gen);
|
||||
osmo_gapk_pq_destroy(q_chk);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct rtp_test_state state;
|
||||
unsigned int len;
|
||||
|
||||
/* Enable tracking the use of NULL memory contexts */
|
||||
talloc_enable_null_tracking();
|
||||
|
||||
/* Init pseudo-random generator */
|
||||
srand(time(NULL));
|
||||
|
||||
/* Perform testing with different payload size values */
|
||||
for (len = 1; len <= RTP_TEST_BUF_LEN; len *= 2)
|
||||
assert(rtp_test(&state, len) == 0);
|
||||
printf("\n");
|
||||
|
||||
/* Memory leak detection test */
|
||||
talloc_report_depth_cb(NULL, 0, 10, &talloc_ctx_walk_cb, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
Payload len=1 check ok
|
||||
Payload len=2 check ok
|
||||
Payload len=4 check ok
|
||||
Payload len=8 check ok
|
||||
Payload len=16 check ok
|
||||
Payload len=32 check ok
|
||||
Payload len=64 check ok
|
||||
Payload len=128 check ok
|
||||
|
||||
chunk null_context: depth=0
|
|
@ -7,7 +7,7 @@ cat $abs_srcdir/procqueue/pq_test.ok > expout
|
|||
AT_CHECK([$abs_top_builddir/tests/procqueue/pq_test], [0], [expout])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([pq_file])
|
||||
AT_SETUP([io/pq_file])
|
||||
AT_KEYWORDS([pq_file])
|
||||
cat $abs_srcdir/io/pq_file_test.ok > expout
|
||||
AT_CHECK([
|
||||
|
@ -15,3 +15,10 @@ AT_CHECK([
|
|||
$abs_top_builddir/tests/io/io_sample.txt],
|
||||
[0], [expout])
|
||||
AT_CLEANUP
|
||||
|
||||
AT_SETUP([io/pq_rtp])
|
||||
AT_KEYWORDS([pq_rtp])
|
||||
cat $abs_srcdir/io/pq_rtp_test.ok > expout
|
||||
AT_CHECK([
|
||||
$abs_top_builddir/tests/io/pq_rtp_test], [0], [expout])
|
||||
AT_CLEANUP
|
||||
|
|
Loading…
Reference in New Issue