summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVadim Yanitskiy <axilirator@gmail.com>2017-09-14 15:20:50 +0430
committerVadim Yanitskiy <axilirator@gmail.com>2017-12-31 12:21:00 +0100
commit9d2b15dc8a97ceefc5cd2e95a885bd306ec992be (patch)
tree5f336a7faec969526c6582a955e1a28694eb5a8b
parent3e9e57fb400218a74c44f9e3ffb70cf5291410a4 (diff)
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.
-rw-r--r--tests/Makefile.am10
-rw-r--r--tests/io/pq_rtp_test.c349
-rw-r--r--tests/io/pq_rtp_test.ok10
-rw-r--r--tests/testsuite.at9
4 files changed, 377 insertions, 1 deletions
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 555899c..8f53787 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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)
diff --git a/tests/io/pq_rtp_test.c b/tests/io/pq_rtp_test.c
new file mode 100644
index 0000000..cf6e2a6
--- /dev/null
+++ b/tests/io/pq_rtp_test.c
@@ -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;
+}
diff --git a/tests/io/pq_rtp_test.ok b/tests/io/pq_rtp_test.ok
new file mode 100644
index 0000000..bae485f
--- /dev/null
+++ b/tests/io/pq_rtp_test.ok
@@ -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
diff --git a/tests/testsuite.at b/tests/testsuite.at
index bffbc52..1127f2b 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -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