From f5f11f5d58c65eb9c977f0e8271c1b5c08726ff1 Mon Sep 17 00:00:00 2001 From: Dirk Engling Date: Sat, 24 Oct 2026 02:01:47 +0200 Subject: Add tests for the hex converter --- tests/runner.c | 1 + tests/t_hex.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/test.h | 1 + 3 files changed, 130 insertions(+) create mode 100644 tests/t_hex.c diff --git a/tests/runner.c b/tests/runner.c index 1b6deb1..c9385f0 100644 --- a/tests/runner.c +++ b/tests/runner.c @@ -18,6 +18,7 @@ static const struct module modules[] = {{"scan_urlencoded_query", test_scan_urle {"ot_iovec", test_iovec}, {"ot_vector", test_vector}, {"ot_random", test_random}, + {"ot_hex", test_hex}, {NULL, NULL}}; /* Run every module, or only those whose name contains argv[1] */ diff --git a/tests/t_hex.c b/tests/t_hex.c new file mode 100644 index 0000000..7cce8ac --- /dev/null +++ b/tests/t_hex.c @@ -0,0 +1,128 @@ +/* This software was written by Dirk Engling + It is considered beerware. Prost. Skol. Cheers or whatever. + + $id$ */ + +/* System */ +#include +#include + +/* Opentracker */ +#include "ot_hex.h" +#include "test.h" + +static const uint8_t sample[20] = {0x00, 0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xff, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xa0}; + +static void case_encode(void) { + char dest[64]; + + test_case("a hash encodes to forty upper case digits and a terminator"); + memset(dest, 0x7f, sizeof(dest)); + CHECK_PTR(hex_fromhash(dest, sample), dest); + CHECK_STR(dest, "000123456789ABCDEFFF10203040506070809" "0A0"); + CHECK_INT(strlen(dest), 40); + CHECK_INT((unsigned char)dest[41], 0x7f); +} + +static void case_decode(void) { + ot_hash hash; + + test_case("forty digits decode back to the hash"); + CHECK(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809" "0A0")); + CHECK_MEM(hash, sample, sizeof(sample)); +} + +static void case_round_trip(void) { + ot_hash hash; + char text[64]; + size_t i; + + test_case("encode and decode are inverses over every byte value"); + for (i = 0; i < 256; ++i) { + uint8_t probe[20]; + memset(probe, (uint8_t)i, sizeof(probe)); + hex_fromhash(text, probe); + if (!hex_tohash(hash, text) || memcmp(hash, probe, sizeof(probe))) { + test_failf(__FILE__, __LINE__, "round trip failed for byte value %zu", i); + return; + } + } + ++test_checks; +} + +static void case_decode_is_case_insensitive(void) { + ot_hash upper, lower; + + test_case("lower case digits decode the same as upper case"); + CHECK(hex_tohash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01")); + CHECK(hex_tohash(lower, "abcdef0123456789abcdef0123456789abcdef01")); + CHECK_MEM(lower, upper, sizeof(ot_hash)); +} + +static void case_rejects_non_digits(void) { + ot_hash hash; + + test_case("a non hex character anywhere rejects the whole string"); + CHECK_INT(hex_tohash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0); + CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0); + CHECK_INT(hex_tohash(hash, "000123456789ABCDEF:F102030405060708090A"), 0); +} + +static void case_rejects_short_input(void) { + ot_hash hash; + + test_case("a string shorter than forty digits is rejected"); + CHECK_INT(hex_tohash(hash, ""), 0); + CHECK_INT(hex_tohash(hash, "00"), 0); + CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090A"), 0); +} + +static void case_leaves_hash_untouched_on_failure(void) { + ot_hash hash; + + test_case("a rejected string leaves the destination untouched"); + memset(hash, 0x5a, sizeof(hash)); + CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0); + CHECK_MEM(hash, "\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a\x5a", sizeof(hash)); +} + +static void case_stops_at_the_terminator(void) { + /* The saved state parser hands this whole buffers that may end early. The + decoder must not read past the zero that stopped it. */ + char guarded[8]; + ot_hash hash; + + test_case("decoding stops at a terminator rather than running past it"); + memcpy(guarded, "0012", 4); + guarded[4] = 0; + memset(guarded + 5, 'f', 3); + CHECK_INT(hex_tohash(hash, guarded), 0); +} + +static void case_isdigit(void) { + test_case("hex_isdigit accepts exactly the hex digits"); + CHECK(hex_isdigit('0')); + CHECK(hex_isdigit('9')); + CHECK(hex_isdigit('a')); + CHECK(hex_isdigit('f')); + CHECK(hex_isdigit('A')); + CHECK(hex_isdigit('F')); + CHECK_INT(hex_isdigit('g'), 0); + CHECK_INT(hex_isdigit('G'), 0); + CHECK_INT(hex_isdigit(':'), 0); + CHECK_INT(hex_isdigit('\n'), 0); + CHECK_INT(hex_isdigit(0), 0); + CHECK_INT(hex_isdigit(0x80), 0); +} + +void test_hex(void) { + case_encode(); + case_decode(); + case_round_trip(); + case_decode_is_case_insensitive(); + case_rejects_non_digits(); + case_rejects_short_input(); + case_leaves_hash_untouched_on_failure(); + case_stops_at_the_terminator(); + case_isdigit(); +} diff --git a/tests/test.h b/tests/test.h index 19acb2f..6af0083 100644 --- a/tests/test.h +++ b/tests/test.h @@ -80,5 +80,6 @@ void test_scan_urlencoded_query(void); void test_iovec(void); void test_vector(void); void test_random(void); +void test_hex(void); #endif -- cgit v1.3