summaryrefslogtreecommitdiffstats
path: root/mqtt_websockets/c-rbuf/tests/ringbuffer_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'mqtt_websockets/c-rbuf/tests/ringbuffer_test.c')
-rw-r--r--mqtt_websockets/c-rbuf/tests/ringbuffer_test.c491
1 files changed, 491 insertions, 0 deletions
diff --git a/mqtt_websockets/c-rbuf/tests/ringbuffer_test.c b/mqtt_websockets/c-rbuf/tests/ringbuffer_test.c
new file mode 100644
index 00000000..d810ea5a
--- /dev/null
+++ b/mqtt_websockets/c-rbuf/tests/ringbuffer_test.c
@@ -0,0 +1,491 @@
+/*
+ *
+ * Copyright: SPDX-License-Identifier: LGPL-3.0-only
+ *
+ * Author: Timotej Šiškovič <timotejs@gmail.com>
+ *
+ */
+
+#include "ringbuffer.h"
+
+// to be able to access internals
+// never do this from app
+#include "../src/ringbuffer_internal.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#define KNRM "\x1B[0m"
+#define KRED "\x1B[31m"
+#define KGRN "\x1B[32m"
+#define KYEL "\x1B[33m"
+#define KBLU "\x1B[34m"
+#define KMAG "\x1B[35m"
+#define KCYN "\x1B[36m"
+#define KWHT "\x1B[37m"
+
+#define UNUSED(x) (void)(x)
+
+int total_fails = 0;
+int total_tests = 0;
+int total_checks = 0;
+
+#define CHECK_EQ_RESULT(x, y) \
+ while (s_len--) \
+ putchar('.'); \
+ printf("%s%s " KNRM "\n", (((x) == (y)) ? KGRN : KRED), (((x) == (y)) ? " PASS " : " FAIL ")); \
+ if ((x) != (y)) \
+ total_fails++; \
+ total_checks++;
+
+#define CHECK_EQ_PREFIX(x, y, prefix, subtest_name, ...) \
+ { \
+ int s_len = \
+ 100 - \
+ printf(("Checking: " KWHT "%s %s%2d " subtest_name " " KNRM), __func__, prefix, subtest_no, ##__VA_ARGS__); \
+ CHECK_EQ_RESULT(x, y) \
+ }
+
+#define CHECK_EQ(x, y, subtest_name, ...) \
+ { \
+ int s_len = \
+ 100 - printf(("Checking: " KWHT "%s %2d " subtest_name " " KNRM), __func__, subtest_no, ##__VA_ARGS__); \
+ CHECK_EQ_RESULT(x, y) \
+ }
+
+#define TEST_DECL() \
+ int subtest_no = 0; \
+ printf(KYEL "TEST SUITE: %s\n" KNRM, __func__); \
+ total_tests++;
+
+static void test_rbuf_get_linear_insert_range()
+{
+ TEST_DECL();
+
+ // check empty buffer behaviour
+ rbuf_t buff = rbuf_create(5);
+ char *to_write;
+ size_t ret;
+ to_write = rbuf_get_linear_insert_range(buff, &ret);
+ CHECK_EQ(ret, 5, "empty size");
+ CHECK_EQ(to_write, buff->head, "empty write ptr");
+ rbuf_free(buff);
+
+ // check full buffer behaviour
+ subtest_no++;
+ buff = rbuf_create(5);
+ ret = rbuf_bump_head(buff, 5);
+ CHECK_EQ(ret, 1, "ret");
+ to_write = rbuf_get_linear_insert_range(buff, &ret);
+ CHECK_EQ(to_write, NULL, "writable NULL");
+ CHECK_EQ(ret, 0, "writable count = 0");
+
+ // check buffer flush
+ subtest_no++;
+ rbuf_flush(buff);
+ CHECK_EQ(rbuf_bytes_free(buff), 5, "size_free");
+ CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail");
+ CHECK_EQ(buff->head, buff->data, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ // check behaviour head > tail
+ subtest_no++;
+ rbuf_flush(buff);
+ rbuf_bump_head(buff, 3);
+ to_write = rbuf_get_linear_insert_range(buff, &ret);
+ CHECK_EQ(to_write, buff->head, "write location");
+ CHECK_EQ(ret, 2, "availible to linear write");
+
+ // check behaviour tail > head
+ subtest_no++;
+ rbuf_flush(buff);
+ rbuf_bump_head(buff, 5);
+ rbuf_bump_tail(buff, 3);
+ CHECK_EQ(buff->head, buff->data, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data + 3, "tail_ptr");
+ to_write = rbuf_get_linear_insert_range(buff, &ret);
+ CHECK_EQ(to_write, buff->head, "write location");
+ CHECK_EQ(ret, 3, "availible to linear write");
+
+/* // check behaviour tail and head at last element
+ subtest_no++;
+ rbuf_flush(buff);
+ rbuf_bump_head(buff, 4);
+ rbuf_bump_tail(buff, 4);
+ CHECK_EQ(buff->head, buff->end - 1, "head_ptr");
+ CHECK_EQ(buff->tail, buff->end - 1, "tail_ptr");
+ to_write = rbuf_get_linear_insert_range(buff, &ret);
+ CHECK_EQ(to_write, buff->head, "write location");
+ CHECK_EQ(ret, 1, "availible to linear write");*/
+
+ // check behaviour tail and head at last element
+ // after rbuf_bump_tail optimisation that restarts buffer
+ // in case tail catches up with head
+ subtest_no++;
+ rbuf_flush(buff);
+ rbuf_bump_head(buff, 4);
+ rbuf_bump_tail(buff, 4);
+ CHECK_EQ(buff->head, buff->data, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+ to_write = rbuf_get_linear_insert_range(buff, &ret);
+ CHECK_EQ(to_write, buff->head, "write location");
+ CHECK_EQ(ret, 5, "availible to linear write");
+}
+
+#define _CHECK_EQ(x, y, subtest_name, ...) CHECK_EQ_PREFIX(x, y, prefix, subtest_name, ##__VA_ARGS__)
+#define _PREFX "(size = %5zu) "
+static void test_rbuf_bump_head_bsize(size_t size)
+{
+ char prefix[16];
+ snprintf(prefix, 16, _PREFX, size);
+ int subtest_no = 0;
+ rbuf_t buff = rbuf_create(size);
+ _CHECK_EQ(rbuf_bytes_free(buff), size, "size_free");
+
+ subtest_no++;
+ int ret = rbuf_bump_head(buff, size);
+ _CHECK_EQ(buff->data, buff->head, "loc");
+ _CHECK_EQ(ret, 1, "ret");
+ _CHECK_EQ(buff->size_data, buff->size, "size");
+ _CHECK_EQ(rbuf_bytes_free(buff), 0, "size_free");
+
+ subtest_no++;
+ ret = rbuf_bump_head(buff, 1);
+ _CHECK_EQ(buff->data, buff->head, "loc no move");
+ _CHECK_EQ(ret, 0, "ret error");
+ _CHECK_EQ(buff->size_data, buff->size, "size");
+ _CHECK_EQ(rbuf_bytes_free(buff), 0, "size_free");
+ rbuf_free(buff);
+
+ subtest_no++;
+ buff = rbuf_create(size);
+ ret = rbuf_bump_head(buff, size - 1);
+ _CHECK_EQ(buff->head, buff->end-1, "loc end");
+ rbuf_free(buff);
+}
+#undef _CHECK_EQ
+
+static void test_rbuf_bump_head()
+{
+ TEST_DECL();
+ UNUSED(subtest_no);
+
+ size_t test_sizes[] = { 1, 2, 3, 5, 6, 7, 8, 100, 99999, 0 };
+ for (int i = 0; test_sizes[i]; i++)
+ test_rbuf_bump_head_bsize(test_sizes[i]);
+}
+
+static void test_rbuf_bump_tail_noopt(int subtest_no)
+{
+ rbuf_t buff = rbuf_create(10);
+ CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free");
+ CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail");
+
+ subtest_no++;
+ int ret = rbuf_bump_head(buff, 5);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_free(buff), 5, "size_free");
+ CHECK_EQ(rbuf_bytes_available(buff), 5, "size_avail");
+ CHECK_EQ(buff->head, buff->data + 5, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ subtest_no++;
+ ret = rbuf_bump_tail_noopt(buff, 2);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 3, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 7, "size_free");
+ CHECK_EQ(buff->head, buff->data + 5, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data + 2, "tail_ptr");
+
+ subtest_no++;
+ ret = rbuf_bump_tail_noopt(buff, 3);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free");
+ CHECK_EQ(buff->head, buff->data + 5, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data + 5, "tail_ptr");
+
+ subtest_no++;
+ ret = rbuf_bump_tail_noopt(buff, 1);
+ CHECK_EQ(ret, 0, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free");
+ CHECK_EQ(buff->head, buff->data + 5, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data + 5, "tail_ptr");
+
+ subtest_no++;
+ ret = rbuf_bump_head(buff, 7);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 7, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 3, "size_free");
+ CHECK_EQ(buff->head, buff->data + 2, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data + 5, "tail_ptr");
+
+ subtest_no++;
+ ret = rbuf_bump_tail_noopt(buff, 5);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 2, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 8, "size_free");
+ CHECK_EQ(buff->head, buff->data + 2, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ // check tail can't overrun head
+ subtest_no++;
+ ret = rbuf_bump_tail_noopt(buff, 3);
+ CHECK_EQ(ret, 0, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 2, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 8, "size_free");
+ CHECK_EQ(buff->head, buff->data + 2, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ // check head can't overrun tail
+ subtest_no++;
+ ret = rbuf_bump_head(buff, 9);
+ CHECK_EQ(ret, 0, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 2, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 8, "size_free");
+ CHECK_EQ(buff->head, buff->data + 2, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ // check head can fill the buffer
+ subtest_no++;
+ ret = rbuf_bump_head(buff, 8);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 10, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 0, "size_free");
+ CHECK_EQ(buff->head, buff->data, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ // check can empty the buffer
+ subtest_no++;
+ ret = rbuf_bump_tail_noopt(buff, 10);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free");
+ CHECK_EQ(buff->head, buff->data, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+}
+
+static void test_rbuf_bump_tail_opt(int subtest_no)
+{
+ subtest_no++;
+ rbuf_t buff = rbuf_create(10);
+ CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free");
+ CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail");
+
+ subtest_no++;
+ int ret = rbuf_bump_head(buff, 5);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_free(buff), 5, "size_free");
+ CHECK_EQ(rbuf_bytes_available(buff), 5, "size_avail");
+ CHECK_EQ(buff->head, buff->data + 5, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ subtest_no++;
+ ret = rbuf_bump_tail(buff, 2);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 3, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 7, "size_free");
+ CHECK_EQ(buff->head, buff->data + 5, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data + 2, "tail_ptr");
+
+ subtest_no++;
+ ret = rbuf_bump_tail(buff, 3);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free");
+ CHECK_EQ(buff->head, buff->data, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ subtest_no++;
+ ret = rbuf_bump_tail_noopt(buff, 1);
+ CHECK_EQ(ret, 0, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free");
+ CHECK_EQ(buff->head, buff->data, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ subtest_no++;
+ ret = rbuf_bump_head(buff, 6);
+ ret = rbuf_bump_tail(buff, 5);
+ ret = rbuf_bump_head(buff, 6);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 7, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 3, "size_free");
+ CHECK_EQ(buff->head, buff->data + 2, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data + 5, "tail_ptr");
+
+ subtest_no++;
+ ret = rbuf_bump_tail(buff, 5);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 2, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 8, "size_free");
+ CHECK_EQ(buff->head, buff->data + 2, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ // check tail can't overrun head
+ subtest_no++;
+ ret = rbuf_bump_tail(buff, 3);
+ CHECK_EQ(ret, 0, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 2, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 8, "size_free");
+ CHECK_EQ(buff->head, buff->data + 2, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ // check head can't overrun tail
+ subtest_no++;
+ ret = rbuf_bump_head(buff, 9);
+ CHECK_EQ(ret, 0, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 2, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 8, "size_free");
+ CHECK_EQ(buff->head, buff->data + 2, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ // check head can fill the buffer
+ subtest_no++;
+ ret = rbuf_bump_head(buff, 8);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 10, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 0, "size_free");
+ CHECK_EQ(buff->head, buff->data, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+
+ // check can empty the buffer
+ subtest_no++;
+ ret = rbuf_bump_tail(buff, 10);
+ CHECK_EQ(ret, 1, "ret");
+ CHECK_EQ(rbuf_bytes_available(buff), 0, "size_avail");
+ CHECK_EQ(rbuf_bytes_free(buff), 10, "size_free");
+ CHECK_EQ(buff->head, buff->data, "head_ptr");
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+}
+
+static void test_rbuf_bump_tail()
+{
+ TEST_DECL();
+ test_rbuf_bump_tail_noopt(subtest_no);
+ test_rbuf_bump_tail_opt(subtest_no);
+}
+
+#define ASCII_A 0x61
+#define ASCII_Z 0x7A
+#define TEST_DATA_SIZE ASCII_Z-ASCII_A+1
+static void test_rbuf_push()
+{
+ TEST_DECL();
+ rbuf_t buff = rbuf_create(10);
+ int i;
+ char test_data[TEST_DATA_SIZE];
+
+ for (int i = 0; i <= TEST_DATA_SIZE; i++)
+ test_data[i] = i + ASCII_A;
+
+ int ret = rbuf_push(buff, test_data, 10);
+ CHECK_EQ(ret, 10, "written 10 bytes");
+ CHECK_EQ(rbuf_bytes_free(buff), 0, "empty size == 0");
+ for (i = 0; i < 10; i++)
+ CHECK_EQ(buff->data[i], i + ASCII_A, "Check data");
+
+ subtest_no++;
+ rbuf_flush(buff);
+ rbuf_bump_head(buff, 5);
+ rbuf_bump_tail_noopt(buff, 5); //to not reset both pointers to beginning
+ ret = rbuf_push(buff, test_data, 10);
+ CHECK_EQ(ret, 10, "written 10 bytes");
+ for (i = 0; i < 10; i++)
+ CHECK_EQ(buff->data[i], ((i+5)%10) + ASCII_A, "Check Data");
+
+ subtest_no++;
+ rbuf_flush(buff);
+ rbuf_bump_head(buff, 9);
+ rbuf_bump_tail_noopt(buff, 9);
+ ret = rbuf_push(buff, test_data, 10);
+ CHECK_EQ(ret, 10, "written 10 bytes");
+ for (i = 0; i < 10; i++)
+ CHECK_EQ(buff->data[i], ((i + 1) % 10) + ASCII_A, "Check data");
+
+ // let tail > head
+ subtest_no++;
+ rbuf_flush(buff);
+ rbuf_bump_head(buff, 9);
+ rbuf_bump_tail_noopt(buff, 9);
+ rbuf_bump_head(buff, 1);
+ ret = rbuf_push(buff, test_data, 9);
+ CHECK_EQ(ret, 9, "written 9 bytes");
+ CHECK_EQ(buff->head, buff->end - 1, "head_ptr");
+ CHECK_EQ(buff->tail, buff->head, "tail_ptr");
+ rbuf_bump_tail(buff, 1);
+ //TODO push byte can be usefull optimisation
+ ret = rbuf_push(buff, &test_data[9], 1);
+ CHECK_EQ(ret, 1, "written 1 byte");
+ CHECK_EQ(rbuf_bytes_free(buff), 0, "empty size == 0");
+ for (i = 0; i < 10; i++)
+ CHECK_EQ(buff->data[i], i + ASCII_A, "Check data");
+
+ subtest_no++;
+ rbuf_flush(buff);
+ rbuf_bump_head(buff, 9);
+ rbuf_bump_tail_noopt(buff, 7);
+ rbuf_bump_head(buff, 1);
+ ret = rbuf_push(buff, test_data, 7);
+ CHECK_EQ(ret, 7, "written 7 bytes");
+ CHECK_EQ(buff->head, buff->data + 7, "head_ptr");
+ CHECK_EQ(buff->tail, buff->head, "tail_ptr");
+ rbuf_bump_tail(buff, 3);
+ CHECK_EQ(buff->tail, buff->data, "tail_ptr");
+ //TODO push byte can be usefull optimisation
+ ret = rbuf_push(buff, &test_data[7], 3);
+ CHECK_EQ(ret, 3, "written 3 bytes");
+ CHECK_EQ(rbuf_bytes_free(buff), 0, "empty size == 0");
+ for (i = 0; i < 10; i++)
+ CHECK_EQ(buff->data[i], i + ASCII_A, "Check data");
+
+ // test can't overfill the buffer
+ subtest_no++;
+ rbuf_flush(buff);
+ rbuf_push(buff, test_data, TEST_DATA_SIZE);
+ CHECK_EQ(ret, 3, "written 10 bytes");
+ for (i = 0; i < 10; i++)
+ CHECK_EQ(buff->data[i], i + ASCII_A, "Check data");
+}
+
+#define TEST_RBUF_FIND_BYTES_SIZE 10
+void test_rbuf_find_bytes()
+{
+ TEST_DECL();
+ rbuf_t buff = rbuf_create(TEST_RBUF_FIND_BYTES_SIZE);
+ char *filler_3 = " ";
+ char *needle = "needle";
+ int idx;
+ char *ptr;
+
+ // make sure needle is wrapped aroung in the buffer
+ // to test we still can find it
+ // target "edle ne"
+ rbuf_bump_head(buff, TEST_RBUF_FIND_BYTES_SIZE / 2);
+ rbuf_push(buff, filler_3, strlen(filler_3));
+ rbuf_bump_tail(buff, TEST_RBUF_FIND_BYTES_SIZE / 2);
+ rbuf_push(buff, needle, strlen(needle));
+ ptr = rbuf_find_bytes(buff, needle, strlen(needle), &idx);
+ CHECK_EQ(ptr, buff->data + (TEST_RBUF_FIND_BYTES_SIZE / 2) + strlen(filler_3), "Pointer to needle correct");
+ CHECK_EQ(idx, ptr - buff->tail, "Check needle index");
+}
+
+int main()
+{
+ test_rbuf_bump_head();
+ test_rbuf_bump_tail();
+ test_rbuf_get_linear_insert_range();
+ test_rbuf_push();
+ test_rbuf_find_bytes();
+
+ printf(
+ KNRM "Total Tests %d, Total Checks %d, Successful Checks %d, Failed Checks %d\n",
+ total_tests, total_checks, total_checks - total_fails, total_fails);
+ if (total_fails)
+ printf(KRED "!!!Some test(s) Failed!!!\n");
+ else
+ printf(KGRN "ALL TESTS PASSED\n");
+
+ return total_fails;
+}