summaryrefslogtreecommitdiff
path: root/tests/t_buf.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/t_buf.c')
-rw-r--r--tests/t_buf.c205
1 files changed, 205 insertions, 0 deletions
diff --git a/tests/t_buf.c b/tests/t_buf.c
new file mode 100644
index 0000000..dcfb0bb
--- /dev/null
+++ b/tests/t_buf.c
@@ -0,0 +1,205 @@
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_buf.h"
12#include "test.h"
13
14static void case_starts_empty(void) {
15 ot_buf buf = {NULL, 0};
16
17 test_case("a fresh buffer holds nothing and has no data pointer");
18 CHECK_PTR(buf.data, NULL);
19 CHECK_INT(buf.size, 0);
20}
21
22static void case_cat_appends(void) {
23 ot_buf buf = {NULL, 0};
24
25 test_case("appends concatenate in order and report success");
26 CHECK_INT(buf_cat(&buf, "GET /ann", 8), 0);
27 CHECK(buf.data != NULL);
28 CHECK_INT(buf.size, 8);
29 CHECK_INT(buf_cat(&buf, "ounce", 5), 0);
30 CHECK_INT(buf.size, 13);
31 CHECK_MEM(buf.data, "GET /announce", 13);
32 buf_reset(&buf);
33}
34
35static void case_cat_of_nothing_allocates_nothing(void) {
36 ot_buf buf = {NULL, 0};
37
38 /* handle_read tells an untouched buffer from a partial request by the data
39 pointer, so a zero length append must not quietly allocate one */
40 test_case("appending zero bytes leaves the buffer untouched");
41 CHECK_INT(buf_cat(&buf, "ignored", 0), 0);
42 CHECK_PTR(buf.data, NULL);
43 CHECK_INT(buf.size, 0);
44 buf_reset(&buf);
45}
46
47static void case_grows_across_many_appends(void) {
48 ot_buf buf = {NULL, 0};
49 char chunk[100];
50 size_t i;
51
52 test_case("content survives growing one packet at a time");
53 memset(chunk, 'x', sizeof(chunk));
54 for (i = 0; i < 100; ++i)
55 if (buf_cat(&buf, chunk, sizeof(chunk))) {
56 test_failf(__FILE__, __LINE__, "append %zu was refused", i);
57 buf_reset(&buf);
58 return;
59 }
60 ++test_checks;
61 CHECK_INT(buf.size, 10000);
62 for (i = 0; i < 10000; ++i)
63 if (buf.data[i] != 'x') {
64 test_failf(__FILE__, __LINE__, "byte %zu was not preserved across growth", i);
65 buf_reset(&buf);
66 return;
67 }
68 ++test_checks;
69 buf_reset(&buf);
70}
71
72static void case_grows_one_byte_at_a_time(void) {
73 ot_buf buf = {NULL, 0};
74 size_t i;
75
76 /* The shape a trickling client produces. Nothing here is optimised for it,
77 so the point is only that it stays correct. */
78 test_case("a byte at a time accumulates correctly");
79 for (i = 0; i < 2048; ++i) {
80 char c = (char)('a' + (i % 26));
81 if (buf_cat(&buf, &c, 1)) {
82 test_failf(__FILE__, __LINE__, "append %zu was refused", i);
83 buf_reset(&buf);
84 return;
85 }
86 }
87 ++test_checks;
88 CHECK_INT(buf.size, 2048);
89 for (i = 0; i < 2048; ++i)
90 if (buf.data[i] != (char)('a' + (i % 26))) {
91 test_failf(__FILE__, __LINE__, "byte %zu differs", i);
92 buf_reset(&buf);
93 return;
94 }
95 ++test_checks;
96 buf_reset(&buf);
97}
98
99static void case_truncate_shrinks_only(void) {
100 ot_buf buf = {NULL, 0};
101
102 test_case("truncate drops the tail but never grows");
103 buf_cat(&buf, "0123456789", 10);
104 buf_truncate(&buf, 4);
105 CHECK_INT(buf.size, 4);
106 CHECK_MEM(buf.data, "0123", 4);
107
108 buf_truncate(&buf, 9);
109 CHECK_INT(buf.size, 4);
110 buf_truncate(&buf, 4);
111 CHECK_INT(buf.size, 4);
112
113 buf_truncate(&buf, 0);
114 CHECK_INT(buf.size, 0);
115 buf_reset(&buf);
116}
117
118static void case_append_after_truncate(void) {
119 ot_buf buf = {NULL, 0};
120
121 test_case("appending after a truncate overwrites the dropped tail");
122 buf_cat(&buf, "keepdrop", 8);
123 buf_truncate(&buf, 4);
124 CHECK_INT(buf_cat(&buf, "MORE", 4), 0);
125 CHECK_INT(buf.size, 8);
126 CHECK_MEM(buf.data, "keepMORE", 8);
127 buf_reset(&buf);
128}
129
130static void case_reset_clears_everything(void) {
131 ot_buf buf = {NULL, 0};
132
133 test_case("reset releases the buffer and restores the initial state");
134 buf_cat(&buf, "something", 9);
135 buf_reset(&buf);
136 CHECK_PTR(buf.data, NULL);
137 CHECK_INT(buf.size, 0);
138}
139
140static void case_reset_of_empty_is_safe(void) {
141 ot_buf buf = {NULL, 0};
142
143 test_case("resetting a buffer that was never used is harmless");
144 buf_reset(&buf);
145 buf_reset(&buf);
146 CHECK_PTR(buf.data, NULL);
147 CHECK_INT(buf.size, 0);
148}
149
150static void case_refused_append_is_harmless(void) {
151 ot_buf buf = {NULL, 0};
152
153 /* A length that would wrap the size is rejected before src is read and
154 before anything is allocated, so a short string is safe to pass here.
155 A refused append must not destroy the partial request already collected:
156 the caller answers and closes rather than losing both. */
157 test_case("a length that would wrap the size is refused");
158 CHECK_INT(buf_cat(&buf, "already here", 12), 0);
159 CHECK_INT(buf_cat(&buf, "x", (size_t)-1), -1);
160
161 test_case("and earlier content survives it");
162 CHECK_INT(buf.size, 12);
163 CHECK_MEM(buf.data, "already here", 12);
164
165 test_case("and the buffer still works afterwards");
166 CHECK_INT(buf_cat(&buf, "!", 1), 0);
167 CHECK_INT(buf.size, 13);
168 CHECK_MEM(buf.data, "already here!", 13);
169 buf_reset(&buf);
170}
171
172static void case_accumulates_a_split_request(void) {
173 /* What handle_read actually does: collect a request that arrived in
174 pieces, then hand over the whole of it */
175 ot_buf buf = {NULL, 0};
176 const char *pieces[] = {"GET /announce?info_hash=", "0123456789abcdefghij", " HTTP/1.0\r\n\r\n"};
177 size_t i;
178
179 test_case("a request split over several reads is reassembled");
180 for (i = 0; i < 3; ++i)
181 if (buf_cat(&buf, pieces[i], strlen(pieces[i]))) {
182 test_failf(__FILE__, __LINE__, "piece %zu was refused", i);
183 buf_reset(&buf);
184 return;
185 }
186 ++test_checks;
187
188 CHECK_INT(buf.size, 24 + 20 + 13);
189 CHECK_MEM(buf.data, "GET /announce?info_hash=0123456789abcdefghij HTTP/1.0\r\n\r\n", 57);
190 buf_reset(&buf);
191}
192
193void test_buf(void) {
194 case_starts_empty();
195 case_cat_appends();
196 case_cat_of_nothing_allocates_nothing();
197 case_grows_across_many_appends();
198 case_grows_one_byte_at_a_time();
199 case_truncate_shrinks_only();
200 case_append_after_truncate();
201 case_reset_clears_everything();
202 case_reset_of_empty_is_safe();
203 case_refused_append_is_harmless();
204 case_accumulates_a_split_request();
205}