tests/test_buffer.cpp

changeset 793
db1c8dfe403a
parent 789
9b2f5661bebd
equal deleted inserted replaced
792:3ca984931e1d 793:db1c8dfe403a
1 /*
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3 *
4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include "cx/buffer.h"
30
31 #include <gtest/gtest.h>
32 #include "util_allocator.h"
33
34 class BufferWrite : public ::testing::Test {
35 protected:
36 CxBuffer buf{}, target{};
37
38 void SetUp() override {
39 cxBufferInit(&target, NULL, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
40 cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
41 buf.capacity = 8; // artificially reduce capacity to check OOB writes
42 memset(buf.space, 0, 16);
43 memcpy(buf.space, "prep", 4);
44 buf.size = buf.pos = 4;
45 }
46
47 void TearDown() override {
48 cxBufferDestroy(&buf);
49 cxBufferDestroy(&target);
50 }
51
52 void enableFlushing() {
53 buf.flush_target = &target;
54 buf.flush_func = reinterpret_cast<cx_write_func>(cxBufferWrite);
55 buf.flush_blkmax = 1;
56 }
57 };
58
59 static size_t mock_write_limited_rate(
60 void const *ptr,
61 size_t size,
62 __attribute__((unused)) size_t nitems,
63 CxBuffer *buffer
64 ) {
65 // simulate limited target drain capacity
66 static bool full = false;
67 if (full) {
68 full = false;
69 return 0;
70 } else {
71 full = true;
72 return cxBufferWrite(ptr, size, nitems > 2 ? 2 : nitems, buffer);
73 }
74 }
75
76 TEST_F(BufferWrite, SizeOneFit) {
77 const char *data = "test";
78 CX_TEST_ASSERT(buf.capacity == 8);
79 CX_TEST_ASSERT(buf.pos == 4);
80 CX_TEST_ASSERT(buf.size == 4);
81 size_t written = cxBufferWrite(data, 1, 4, &buf);
82 CX_TEST_ASSERT(written == 4);
83 CX_TEST_ASSERT(buf.size == 8);
84 CX_TEST_ASSERT(buf.pos == 8);
85 CX_TEST_ASSERT(buf.capacity == 8);
86 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
87 }
88
89 TEST_F(BufferWrite, SizeOneDiscard) {
90 const char *data = "testing";
91 CX_TEST_ASSERT(buf.capacity == 8);
92 CX_TEST_ASSERT(buf.pos == 4);
93 CX_TEST_ASSERT(buf.size == 4);
94 size_t written = cxBufferWrite(data, 1, 7, &buf);
95 CX_TEST_ASSERT(written == 4);
96 CX_TEST_ASSERT(buf.size == 8);
97 CX_TEST_ASSERT(buf.pos == 8);
98 CX_TEST_ASSERT(buf.capacity == 8);
99 EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0);
100 }
101
102 TEST_F(BufferWrite, SizeOneExtend) {
103 buf.flags |= CX_BUFFER_AUTO_EXTEND;
104 const char *data = "testing";
105 CX_TEST_ASSERT(buf.capacity == 8);
106 CX_TEST_ASSERT(buf.pos == 4);
107 CX_TEST_ASSERT(buf.size == 4);
108 size_t written = cxBufferWrite(data, 1, 7, &buf);
109 CX_TEST_ASSERT(written == 7);
110 CX_TEST_ASSERT(buf.size == 11);
111 CX_TEST_ASSERT(buf.pos == 11);
112 EXPECT_GE(buf.capacity, 11);
113 EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0);
114 }
115
116 TEST_F(BufferWrite, MultibyteFit) {
117 const char *data = "test";
118 CX_TEST_ASSERT(buf.capacity == 8);
119 CX_TEST_ASSERT(buf.pos == 4);
120 CX_TEST_ASSERT(buf.size == 4);
121 size_t written = cxBufferWrite(data, 2, 2, &buf);
122 CX_TEST_ASSERT(written == 2);
123 CX_TEST_ASSERT(buf.size == 8);
124 CX_TEST_ASSERT(buf.pos == 8);
125 CX_TEST_ASSERT(buf.capacity == 8);
126 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
127 }
128
129 TEST_F(BufferWrite, MultibyteDiscard) {
130 const char *data = "testing";
131 CX_TEST_ASSERT(buf.capacity == 8);
132 CX_TEST_ASSERT(buf.size == 4);
133 buf.pos = 3;
134 size_t written = cxBufferWrite(data, 2, 4, &buf);
135 // remember: whole elements are discarded if they do not fit
136 CX_TEST_ASSERT(written == 2);
137 CX_TEST_ASSERT(buf.size == 7);
138 CX_TEST_ASSERT(buf.pos == 7);
139 CX_TEST_ASSERT(buf.capacity == 8);
140 EXPECT_EQ(memcmp(buf.space, "pretest\0", 8), 0);
141 }
142
143 TEST_F(BufferWrite, MultibyteExtend) {
144 buf.flags |= CX_BUFFER_AUTO_EXTEND;
145 const char *data = "tester";
146 CX_TEST_ASSERT(buf.capacity == 8);
147 CX_TEST_ASSERT(buf.size == 4);
148 buf.pos = 3;
149 size_t written = cxBufferWrite(data, 2, 3, &buf);
150 // remember: whole elements are discarded if they do not fit
151 CX_TEST_ASSERT(written == 3);
152 CX_TEST_ASSERT(buf.size == 9);
153 CX_TEST_ASSERT(buf.pos == 9);
154 EXPECT_GE(buf.capacity, 9);
155 EXPECT_EQ(memcmp(buf.space, "pretester", 9), 0);
156 }
157
158 TEST_F(BufferWrite, PutcWrapperFit) {
159 CX_TEST_ASSERT(buf.capacity == 8);
160 CX_TEST_ASSERT(buf.pos == 4);
161 CX_TEST_ASSERT(buf.size == 4);
162 int c = cxBufferPut(&buf, 0x200 | 'a');
163 CX_TEST_ASSERT(c == 'a');
164 CX_TEST_ASSERT(buf.size == 5);
165 CX_TEST_ASSERT(buf.pos == 5);
166 CX_TEST_ASSERT(buf.capacity == 8);
167 EXPECT_EQ(memcmp(buf.space, "prepa\0", 6), 0);
168 }
169
170 TEST_F(BufferWrite, PutcWrapperDiscard) {
171 CX_TEST_ASSERT(buf.capacity == 8);
172 CX_TEST_ASSERT(buf.size == 4);
173 buf.pos = 8;
174 int c = cxBufferPut(&buf, 0x200 | 'a');
175 CX_TEST_ASSERT(c == EOF);
176 CX_TEST_ASSERT(buf.size == 4);
177 CX_TEST_ASSERT(buf.pos == 8);
178 CX_TEST_ASSERT(buf.capacity == 8);
179 EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0\0", 9), 0);
180 }
181
182 TEST_F(BufferWrite, PutcWrapperExtend) {
183 buf.flags |= CX_BUFFER_AUTO_EXTEND;
184 CX_TEST_ASSERT(buf.capacity == 8);
185 CX_TEST_ASSERT(buf.size == 4);
186 buf.pos = 8;
187 int c = cxBufferPut(&buf, 0x200 | 'a');
188 CX_TEST_ASSERT(c == 'a');
189 CX_TEST_ASSERT(buf.size == 9);
190 CX_TEST_ASSERT(buf.pos == 9);
191 EXPECT_GE(buf.capacity, 9);
192 EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0a", 9), 0);
193 }
194
195 TEST_F(BufferWrite, PutStringWrapperFit) {
196 const char *data = "test";
197 CX_TEST_ASSERT(buf.capacity == 8);
198 CX_TEST_ASSERT(buf.pos == 4);
199 CX_TEST_ASSERT(buf.size == 4);
200 size_t written = cxBufferPutString(&buf, data);
201 CX_TEST_ASSERT(written == 4);
202 CX_TEST_ASSERT(buf.size == 8);
203 CX_TEST_ASSERT(buf.pos == 8);
204 CX_TEST_ASSERT(buf.capacity == 8);
205 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
206 }
207
208 TEST_F(BufferWrite, PutStringWrapperDiscard) {
209 const char *data = "testing";
210 CX_TEST_ASSERT(buf.capacity == 8);
211 CX_TEST_ASSERT(buf.pos == 4);
212 CX_TEST_ASSERT(buf.size == 4);
213 size_t written = cxBufferPutString(&buf, data);
214 CX_TEST_ASSERT(written == 4);
215 CX_TEST_ASSERT(buf.size == 8);
216 CX_TEST_ASSERT(buf.pos == 8);
217 CX_TEST_ASSERT(buf.capacity == 8);
218 EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0);
219 }
220
221 TEST_F(BufferWrite, PutStringWrapperExtend) {
222 buf.flags |= CX_BUFFER_AUTO_EXTEND;
223 const char *data = "testing";
224 CX_TEST_ASSERT(buf.capacity == 8);
225 CX_TEST_ASSERT(buf.pos == 4);
226 CX_TEST_ASSERT(buf.size == 4);
227 size_t written = cxBufferPutString(&buf, data);
228 CX_TEST_ASSERT(written == 7);
229 CX_TEST_ASSERT(buf.size == 11);
230 CX_TEST_ASSERT(buf.pos == 11);
231 EXPECT_GE(buf.capacity, 11);
232 EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0);
233 }
234
235 TEST_F(BufferWrite, MultOverflow) {
236 const char *data = "testing";
237 CX_TEST_ASSERT(buf.capacity == 8);
238 CX_TEST_ASSERT(buf.pos == 4);
239 CX_TEST_ASSERT(buf.size == 4);
240 size_t written = cxBufferWrite(data, 8, SIZE_MAX / 4, &buf);
241 CX_TEST_ASSERT(written == 0);
242 CX_TEST_ASSERT(buf.capacity == 8);
243 CX_TEST_ASSERT(buf.pos == 4);
244 CX_TEST_ASSERT(buf.size == 4);
245 EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0);
246 }
247
248 TEST_F(BufferWrite, MaxCapaOverflow) {
249 buf.flags |= CX_BUFFER_AUTO_EXTEND;
250 const char *data = "testing";
251 CX_TEST_ASSERT(buf.capacity == 8);
252 CX_TEST_ASSERT(buf.pos == 4);
253 CX_TEST_ASSERT(buf.size == 4);
254 size_t written = cxBufferWrite(data, 1, SIZE_MAX - 2, &buf);
255 CX_TEST_ASSERT(written == 0);
256 CX_TEST_ASSERT(buf.capacity == 8);
257 CX_TEST_ASSERT(buf.pos == 4);
258 CX_TEST_ASSERT(buf.size == 4);
259 EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0);
260 }
261
262 TEST_F(BufferWrite, OnlyOverwrite) {
263 buf.flags |= CX_BUFFER_AUTO_EXTEND;
264 CX_TEST_ASSERT(buf.capacity == 8);
265 memcpy(buf.space, "preptest", 8);
266 buf.pos = 3;
267 buf.size = 8;
268 size_t written = cxBufferWrite("XXX", 2, 2, &buf);
269 CX_TEST_ASSERT(written == 2);
270 CX_TEST_ASSERT(buf.capacity == 8);
271 CX_TEST_ASSERT(buf.size == 8);
272 CX_TEST_ASSERT(buf.pos == 7);
273 EXPECT_EQ(memcmp(buf.space, "preXXX\0t", 8), 0);
274 }
275
276 TEST_F(BufferWrite, FlushAtCapacity) {
277 enableFlushing();
278 CX_TEST_ASSERT(buf.capacity == 8);
279 CX_TEST_ASSERT(buf.pos == 4);
280 size_t written = cxBufferWrite("foo", 1, 3, &buf);
281 CX_TEST_ASSERT(written == 3);
282 CX_TEST_ASSERT(buf.pos == 7);
283 CX_TEST_ASSERT(buf.size == 7);
284 CX_TEST_ASSERT(target.pos == 0);
285 CX_TEST_ASSERT(target.size == 0);
286 written = cxBufferWrite("hello", 1, 5, &buf);
287 CX_TEST_ASSERT(written == 5);
288 CX_TEST_ASSERT(buf.pos == 0);
289 CX_TEST_ASSERT(buf.size == 0);
290 CX_TEST_ASSERT(buf.capacity == 8);
291 CX_TEST_ASSERT(target.pos == 12);
292 CX_TEST_ASSERT(target.size == 12);
293 EXPECT_EQ(memcmp(target.space, "prepfoohello", 12), 0);
294 }
295
296 TEST_F(BufferWrite, FlushAtThreshold) {
297 enableFlushing();
298 buf.flush_threshold = 12;
299 buf.flags |= CX_BUFFER_AUTO_EXTEND;
300 CX_TEST_ASSERT(buf.capacity == 8);
301 CX_TEST_ASSERT(buf.pos == 4);
302 size_t written = cxBufferWrite("foobar", 1, 6, &buf);
303 CX_TEST_ASSERT(written == 6);
304 CX_TEST_ASSERT(buf.pos == 10);
305 CX_TEST_ASSERT(buf.size == 10);
306 ASSERT_GE(buf.capacity, 10);
307 ASSERT_LE(buf.capacity, 12);
308 CX_TEST_ASSERT(target.pos == 0);
309 CX_TEST_ASSERT(target.size == 0);
310 written = cxBufferWrite("hello", 1, 5, &buf);
311 CX_TEST_ASSERT(written == 5);
312 CX_TEST_ASSERT(buf.pos == 0);
313 CX_TEST_ASSERT(buf.size == 0);
314 EXPECT_LE(buf.capacity, 12);
315 CX_TEST_ASSERT(target.pos == 15);
316 CX_TEST_ASSERT(target.size == 15);
317 EXPECT_EQ(memcmp(target.space, "prepfoobarhello", 15), 0);
318 }
319
320 TEST_F(BufferWrite, FlushRateLimited) {
321 enableFlushing();
322 // limit the rate of the flush function and the capacity of the target
323 target.capacity = 16;
324 target.flags &= ~CX_BUFFER_AUTO_EXTEND;
325 buf.flush_func = (cx_write_func) mock_write_limited_rate;
326 CX_TEST_ASSERT(buf.capacity == 8);
327 CX_TEST_ASSERT(buf.pos == 4);
328 size_t written = cxBufferWrite("foo", 1, 3, &buf);
329 CX_TEST_ASSERT(written == 3);
330 CX_TEST_ASSERT(buf.pos == 7);
331 CX_TEST_ASSERT(buf.size == 7);
332 CX_TEST_ASSERT(target.pos == 0);
333 CX_TEST_ASSERT(target.size == 0);
334 written = cxBufferWrite("hello, world!", 1, 13, &buf);
335 // " world!" fits into this buffer, the remaining stuff is flushed out
336 CX_TEST_ASSERT(written == 13);
337 CX_TEST_ASSERT(buf.pos == 7);
338 CX_TEST_ASSERT(buf.size == 7);
339 CX_TEST_ASSERT(buf.capacity == 8);
340 EXPECT_EQ(memcmp(buf.space, " world!", 7), 0);
341 CX_TEST_ASSERT(target.pos == 13);
342 CX_TEST_ASSERT(target.size == 13);
343 CX_TEST_ASSERT(target.capacity == 16);
344 EXPECT_EQ(memcmp(target.space, "prepfoohello,", 13), 0);
345 }
346
347 class BufferRead : public ::testing::Test {
348 protected:
349 CxBuffer buf{};
350
351 void SetUp() override {
352 cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
353 buf.capacity = 8; // artificially reduce capacity to check OOB writes
354 memset(buf.space, 0, 16);
355 memcpy(buf.space, "some data", 9);
356 buf.size = 9;
357 }
358
359 void TearDown() override {
360 cxBufferDestroy(&buf);
361 }
362 };
363
364 TEST_F(BufferRead, GetByte) {
365 buf.pos = 2;
366 EXPECT_EQ(cxBufferGet(&buf), 'm');
367 EXPECT_EQ(cxBufferGet(&buf), 'e');
368 EXPECT_EQ(cxBufferGet(&buf), ' ');
369 EXPECT_EQ(cxBufferGet(&buf), 'd');
370 CX_TEST_ASSERT(buf.pos == 6);
371 }
372
373 TEST_F(BufferRead, GetEof) {
374 buf.pos = buf.size;
375 EXPECT_EQ(cxBufferGet(&buf), EOF);
376 }
377
378 TEST_F(BufferRead, ReadWithinBounds) {
379 buf.pos = 2;
380 char target[4];
381 auto read = cxBufferRead(&target, 1, 4, &buf);
382 CX_TEST_ASSERT(read == 4);
383 EXPECT_EQ(memcmp(&target, "me d", 4), 0);
384 CX_TEST_ASSERT(buf.pos == 6);
385 }
386
387 TEST_F(BufferRead, ReadOutOfBounds) {
388 buf.pos = 6;
389 char target[4];
390 auto read = cxBufferRead(&target, 1, 4, &buf);
391 CX_TEST_ASSERT(read == 3);
392 EXPECT_EQ(memcmp(&target, "ata", 3), 0);
393 CX_TEST_ASSERT(buf.pos == 9);
394 }
395
396 TEST_F(BufferRead, ReadOutOfBoundsMultibyte) {
397 buf.pos = 6;
398 char target[4];
399 target[2] = '\0';
400 auto read = cxBufferRead(&target, 2, 2, &buf);
401 CX_TEST_ASSERT(read == 1);
402 EXPECT_EQ(memcmp(&target, "at\0", 3), 0);
403 CX_TEST_ASSERT(buf.pos == 8);
404 }
405
406 TEST_F(BufferRead, ReadEof) {
407 buf.pos = 9;
408 char target[4];
409 auto read = cxBufferRead(&target, 1, 1, &buf);
410 CX_TEST_ASSERT(read == 0);
411 CX_TEST_ASSERT(buf.pos == 9);
412 }

mercurial