summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorDirk Engling <erdgeist@erdgeist.org>2026-10-24 02:01:47 +0200
committerDirk Engling <erdgeist@erdgeist.org>2026-10-24 02:01:47 +0200
commitf5f11f5d58c65eb9c977f0e8271c1b5c08726ff1 (patch)
treee389910c9fe9088f31e77bb783b0675a6ca6f728 /tests
parentc22fa15f5c44a43e291c15f737515e35fb9dc6ba (diff)
Add tests for the hex converter
Diffstat (limited to 'tests')
-rw-r--r--tests/runner.c1
-rw-r--r--tests/t_hex.c128
-rw-r--r--tests/test.h1
3 files changed, 130 insertions, 0 deletions
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
18 {"ot_iovec", test_iovec}, 18 {"ot_iovec", test_iovec},
19 {"ot_vector", test_vector}, 19 {"ot_vector", test_vector},
20 {"ot_random", test_random}, 20 {"ot_random", test_random},
21 {"ot_hex", test_hex},
21 {NULL, NULL}}; 22 {NULL, NULL}};
22 23
23/* Run every module, or only those whose name contains argv[1] */ 24/* 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 @@
1/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
2 It is considered beerware. Prost. Skol. Cheers or whatever.
3
4 $id$ */
5
6/* System */
7#include <stdint.h>
8#include <string.h>
9
10/* Opentracker */
11#include "ot_hex.h"
12#include "test.h"
13
14static 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};
15
16static void case_encode(void) {
17 char dest[64];
18
19 test_case("a hash encodes to forty upper case digits and a terminator");
20 memset(dest, 0x7f, sizeof(dest));
21 CHECK_PTR(hex_fromhash(dest, sample), dest);
22 CHECK_STR(dest, "000123456789ABCDEFFF10203040506070809" "0A0");
23 CHECK_INT(strlen(dest), 40);
24 CHECK_INT((unsigned char)dest[41], 0x7f);
25}
26
27static void case_decode(void) {
28 ot_hash hash;
29
30 test_case("forty digits decode back to the hash");
31 CHECK(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809" "0A0"));
32 CHECK_MEM(hash, sample, sizeof(sample));
33}
34
35static void case_round_trip(void) {
36 ot_hash hash;
37 char text[64];
38 size_t i;
39
40 test_case("encode and decode are inverses over every byte value");
41 for (i = 0; i < 256; ++i) {
42 uint8_t probe[20];
43 memset(probe, (uint8_t)i, sizeof(probe));
44 hex_fromhash(text, probe);
45 if (!hex_tohash(hash, text) || memcmp(hash, probe, sizeof(probe))) {
46 test_failf(__FILE__, __LINE__, "round trip failed for byte value %zu", i);
47 return;
48 }
49 }
50 ++test_checks;
51}
52
53static void case_decode_is_case_insensitive(void) {
54 ot_hash upper, lower;
55
56 test_case("lower case digits decode the same as upper case");
57 CHECK(hex_tohash(upper, "ABCDEF0123456789ABCDEF0123456789ABCDEF01"));
58 CHECK(hex_tohash(lower, "abcdef0123456789abcdef0123456789abcdef01"));
59 CHECK_MEM(lower, upper, sizeof(ot_hash));
60}
61
62static void case_rejects_non_digits(void) {
63 ot_hash hash;
64
65 test_case("a non hex character anywhere rejects the whole string");
66 CHECK_INT(hex_tohash(hash, "g00123456789ABCDEFFF102030405060708090A0"), 0);
67 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090Ag"), 0);
68 CHECK_INT(hex_tohash(hash, "000123456789ABCDEF:F102030405060708090A"), 0);
69}
70
71static void case_rejects_short_input(void) {
72 ot_hash hash;
73
74 test_case("a string shorter than forty digits is rejected");
75 CHECK_INT(hex_tohash(hash, ""), 0);
76 CHECK_INT(hex_tohash(hash, "00"), 0);
77 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF102030405060708090A"), 0);
78}
79
80static void case_leaves_hash_untouched_on_failure(void) {
81 ot_hash hash;
82
83 test_case("a rejected string leaves the destination untouched");
84 memset(hash, 0x5a, sizeof(hash));
85 CHECK_INT(hex_tohash(hash, "000123456789ABCDEFFF10203040506070809XX0"), 0);
86 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));
87}
88
89static void case_stops_at_the_terminator(void) {
90 /* The saved state parser hands this whole buffers that may end early. The
91 decoder must not read past the zero that stopped it. */
92 char guarded[8];
93 ot_hash hash;
94
95 test_case("decoding stops at a terminator rather than running past it");
96 memcpy(guarded, "0012", 4);
97 guarded[4] = 0;
98 memset(guarded + 5, 'f', 3);
99 CHECK_INT(hex_tohash(hash, guarded), 0);
100}
101
102static void case_isdigit(void) {
103 test_case("hex_isdigit accepts exactly the hex digits");
104 CHECK(hex_isdigit('0'));
105 CHECK(hex_isdigit('9'));
106 CHECK(hex_isdigit('a'));
107 CHECK(hex_isdigit('f'));
108 CHECK(hex_isdigit('A'));
109 CHECK(hex_isdigit('F'));
110 CHECK_INT(hex_isdigit('g'), 0);
111 CHECK_INT(hex_isdigit('G'), 0);
112 CHECK_INT(hex_isdigit(':'), 0);
113 CHECK_INT(hex_isdigit('\n'), 0);
114 CHECK_INT(hex_isdigit(0), 0);
115 CHECK_INT(hex_isdigit(0x80), 0);
116}
117
118void test_hex(void) {
119 case_encode();
120 case_decode();
121 case_round_trip();
122 case_decode_is_case_insensitive();
123 case_rejects_non_digits();
124 case_rejects_short_input();
125 case_leaves_hash_untouched_on_failure();
126 case_stops_at_the_terminator();
127 case_isdigit();
128}
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);
80void test_iovec(void); 80void test_iovec(void);
81void test_vector(void); 81void test_vector(void);
82void test_random(void); 82void test_random(void);
83void test_hex(void);
83 84
84#endif 85#endif