From 24cb2dada8639d05560024c610c991ee93791ffa Mon Sep 17 00:00:00 2001 From: Benjamin Valentin Date: Thu, 21 Oct 2021 13:32:51 +0200 Subject: [PATCH] tests/coding: add tests for XOR codding --- tests/unittests/tests-coding/Makefile | 1 + tests/unittests/tests-coding/Makefile.include | 1 + .../unittests/tests-coding/tests-coding-xor.c | 190 ++++++++++++++++++ tests/unittests/tests-coding/tests-coding.c | 14 ++ tests/unittests/tests-coding/tests-coding.h | 44 ++++ 5 files changed, 250 insertions(+) create mode 100644 tests/unittests/tests-coding/Makefile create mode 100644 tests/unittests/tests-coding/Makefile.include create mode 100644 tests/unittests/tests-coding/tests-coding-xor.c create mode 100644 tests/unittests/tests-coding/tests-coding.c create mode 100644 tests/unittests/tests-coding/tests-coding.h diff --git a/tests/unittests/tests-coding/Makefile b/tests/unittests/tests-coding/Makefile new file mode 100644 index 0000000000..48422e909a --- /dev/null +++ b/tests/unittests/tests-coding/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-coding/Makefile.include b/tests/unittests/tests-coding/Makefile.include new file mode 100644 index 0000000000..aabc0c646a --- /dev/null +++ b/tests/unittests/tests-coding/Makefile.include @@ -0,0 +1 @@ +USEMODULE += coding diff --git a/tests/unittests/tests-coding/tests-coding-xor.c b/tests/unittests/tests-coding/tests-coding-xor.c new file mode 100644 index 0000000000..c56ec80add --- /dev/null +++ b/tests/unittests/tests-coding/tests-coding-xor.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2021 Benjamin Valentin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include +#include +#include +#include "embUnit/embUnit.h" + +#include "bitfield.h" +#include "coding/xor.h" +#include "tests-coding.h" + +static void test_coding_xor_building_blocks(void) +{ + char string[] = "0123456789AB"; + uint8_t parity[CODING_XOR_PARITY_LEN(sizeof(string) - 1)]; + + coding_xor_generate(string, sizeof(string) - 1, parity); + + TEST_ASSERT_EQUAL_INT(0x33, parity[0]); + TEST_ASSERT_EQUAL_INT(0x32, parity[1]); + TEST_ASSERT_EQUAL_INT(0x39, parity[2]); + TEST_ASSERT_EQUAL_INT(0x3A, parity[3]); + TEST_ASSERT_EQUAL_STRING("0369147A258B", string); + + uint8_t blocks = 0x80; + bool success = coding_xor_recover(string, sizeof(string) - 1, parity, + &blocks, sizeof(string) - 1, false); + TEST_ASSERT(success); + TEST_ASSERT_EQUAL_STRING("0123456789AB", string); +} + +static void test_coding_xor_recovery(void) +{ + char string[] = "0123456789AB"; + char string_rx[sizeof(string)]; + uint8_t parity[CODING_XOR_PARITY_LEN(sizeof(string) - 1)]; + const uint8_t chunk_size = 4; + const uint8_t num_chunks = sizeof(string) / chunk_size; + const uint8_t num_parity_chunks = sizeof(parity) / chunk_size; + + BITFIELD(chunks, num_chunks + num_parity_chunks); + memset(chunks, 0, sizeof(chunks)); + memset(string_rx, 0, sizeof(string_rx)); + + coding_xor_generate(string, sizeof(string) - 1, parity); + + for (unsigned i = 0; i < num_chunks; ++i) { + /* lose a single chunk / packet */ + if (i == 1) { + continue; + } + + memcpy(string_rx + i * chunk_size, + string + i * chunk_size, + chunk_size); + bf_set(chunks, i); + } + /* we have all parity chunks */ + for (unsigned i = 0; i < num_parity_chunks; ++i) { + bf_set(chunks, num_chunks + i); + } + + bool success = coding_xor_recover(string_rx, sizeof(string_rx) - 1, + parity, chunks, chunk_size, false); + TEST_ASSERT(success); + TEST_ASSERT_EQUAL_STRING("0123456789AB", string_rx); +} + +static void test_coding_xor_recovery_failed(void) +{ + char string[] = "0123456789AB"; + char string_rx[sizeof(string)]; + uint8_t parity[CODING_XOR_PARITY_LEN(sizeof(string) - 1)]; + const uint8_t chunk_size = 4; + const uint8_t num_chunks = sizeof(string) / chunk_size; + const uint8_t num_parity_chunks = sizeof(parity) / chunk_size; + + BITFIELD(chunks, num_chunks + num_parity_chunks); + memset(chunks, 0, sizeof(chunks)); + memset(string_rx, 0, sizeof(string_rx)); + + coding_xor_generate(string, sizeof(string) - 1, parity); + + for (unsigned i = 0; i < num_chunks; ++i) { + /* lose two chunks / packets */ + if (i == 1 || i == 2) { + continue; + } + + memcpy(string_rx + i * chunk_size, + string + i * chunk_size, + chunk_size); + bf_set(chunks, i); + } + /* we have all parity chunks */ + for (unsigned i = 0; i < num_parity_chunks; ++i) { + bf_set(chunks, num_chunks + i); + } + + bool success = coding_xor_recover(string_rx, sizeof(string_rx) - 1, + parity, chunks, chunk_size, false); + TEST_ASSERT(!success); +} + +static void test_coding_xor_recovery_parity(void) +{ + char string[] = "0123456789AB"; + uint8_t parity[CODING_XOR_PARITY_LEN(sizeof(string) - 1)]; + uint8_t parity_rx[CODING_XOR_PARITY_LEN(sizeof(string) - 1)]; + const uint8_t chunk_size = 4; + const uint8_t num_chunks = sizeof(string) / chunk_size; + const uint8_t num_parity_chunks = sizeof(parity) / chunk_size; + + BITFIELD(chunks, num_chunks + num_parity_chunks); + memset(chunks, 0, sizeof(chunks)); + + coding_xor_generate(string, sizeof(string) - 1, parity); + + /* we have all data chunks */ + for (unsigned i = 0; i < num_chunks; ++i) { + bf_set(chunks, i); + } + + bool success = coding_xor_recover(string, sizeof(string) - 1, parity_rx, + chunks, chunk_size, true); + TEST_ASSERT(success); + TEST_ASSERT_EQUAL_STRING("0123456789AB", string); + TEST_ASSERT(memcmp(parity, parity_rx, sizeof(parity)) == 0); +} + +static void test_coding_xor_recovery_large(void) +{ + uint8_t txbuf[1024]; + const size_t data_len = 768; + + const size_t parity_len = CODING_XOR_PARITY_LEN(data_len); + uint8_t *parity = &txbuf[data_len]; + + const uint8_t chunk_size = 64; + const size_t num_chunks = (data_len + parity_len) / chunk_size; + BITFIELD(chunks, num_chunks); + memset(chunks, 0, sizeof(chunks)); + + TEST_ASSERT_EQUAL_INT(sizeof(txbuf), data_len + parity_len); + + /* fill TX buffer with known pattern */ + for (unsigned i = 0; i < data_len; ++i) { + txbuf[i] = i & 0xFF; + } + coding_xor_generate(txbuf, data_len, parity); + + for (unsigned i = 0; i < num_chunks; ++i) { + uint8_t *data = &txbuf[i * chunk_size]; + + /* lose some chunks */ + if (i == 3 || i == 10 || i == 13) { + memset(data, 0, chunk_size); + } else { + bf_set(chunks, i); + } + } + + bool success = coding_xor_recover(txbuf, data_len, parity, chunks, + chunk_size, true); + TEST_ASSERT(success); + for (unsigned i = 0; i < data_len; ++i) { + TEST_ASSERT_EQUAL_INT(i & 0xFF, txbuf[i]); + } +} + +Test *tests_coding_xor_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_coding_xor_building_blocks), + new_TestFixture(test_coding_xor_recovery), + new_TestFixture(test_coding_xor_recovery_failed), + new_TestFixture(test_coding_xor_recovery_parity), + new_TestFixture(test_coding_xor_recovery_large), + }; + + EMB_UNIT_TESTCALLER(coding_xor_tests, NULL, NULL, fixtures); + + return (Test *)&coding_xor_tests; +} diff --git a/tests/unittests/tests-coding/tests-coding.c b/tests/unittests/tests-coding/tests-coding.c new file mode 100644 index 0000000000..228887f4d0 --- /dev/null +++ b/tests/unittests/tests-coding/tests-coding.c @@ -0,0 +1,14 @@ +/* + * Copyright 2021 Benjamin Valentin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +#include "tests-coding.h" + +void tests_coding(void) +{ + TESTS_RUN(tests_coding_xor_tests()); +} diff --git a/tests/unittests/tests-coding/tests-coding.h b/tests/unittests/tests-coding/tests-coding.h new file mode 100644 index 0000000000..6ac8ffd4bb --- /dev/null +++ b/tests/unittests/tests-coding/tests-coding.h @@ -0,0 +1,44 @@ +/* + * Copyright 2021 Benjamin Valentin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Unittests for the ``coding`` module + * + * @author Benjamin Valentin + */ +#ifndef TESTS_CODING_H +#define TESTS_CODING_H + +#include "embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The entry point of this test suite. + */ +void tests_coding(void); + +/** + * @brief Generates tests for coding/xor.h + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_coding_xor_tests(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_CODING_H */ +/** @} */