summaryrefslogtreecommitdiff
path: root/tests/t_hex.c
blob: 7cce8ac28d8378ba0d16e56a87c1479c03ff53d1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/* This software was written by Dirk Engling <erdgeist@erdgeist.org>
   It is considered beerware. Prost. Skol. Cheers or whatever.

   $id$ */

/* System */
#include <stdint.h>
#include <string.h>

/* 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();
}