Fri, 07 Jul 2023 20:22:30 +0200
improve the CSV example
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 */
29 #include "cx/buffer.h"
31 #include <gtest/gtest.h>
32 #include "util_allocator.h"
34 class BufferFixture : public ::testing::Test {
35 protected:
36 void SetUp() override {
37 cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
38 buf.size = 6;
39 buf.pos = 3;
40 }
42 void TearDown() override {
43 cxBufferDestroy(&buf);
44 }
46 CxBuffer buf{};
47 };
49 static void expect_default_flush_config(CxBuffer *buf) {
50 EXPECT_EQ(buf->flush_blkmax, 0);
51 EXPECT_EQ(buf->flush_blksize, 4096);
52 EXPECT_EQ(buf->flush_threshold, SIZE_MAX);
53 EXPECT_EQ(buf->flush_func, nullptr);
54 EXPECT_EQ(buf->flush_target, nullptr);
55 }
57 TEST(BufferInit, WrapSpace) {
58 CxTestingAllocator alloc;
59 CxBuffer buf;
60 void *space = cxMalloc(&alloc, 16);
61 cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_DEFAULT);
62 expect_default_flush_config(&buf);
63 EXPECT_EQ(buf.space, space);
64 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
65 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0);
66 EXPECT_EQ(buf.pos, 0);
67 EXPECT_EQ(buf.size, 0);
68 EXPECT_EQ(buf.capacity, 16);
69 EXPECT_EQ(buf.allocator, &alloc);
70 cxBufferDestroy(&buf);
71 EXPECT_FALSE(alloc.verify());
72 cxFree(&alloc, space);
73 EXPECT_TRUE(alloc.verify());
74 }
76 TEST(BufferInit, WrapSpaceAutoExtend) {
77 CxTestingAllocator alloc;
78 CxBuffer buf;
79 void *space = cxMalloc(&alloc, 16);
80 cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_AUTO_EXTEND);
81 expect_default_flush_config(&buf);
82 EXPECT_EQ(buf.space, space);
83 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, CX_BUFFER_AUTO_EXTEND);
84 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0);
85 EXPECT_EQ(buf.pos, 0);
86 EXPECT_EQ(buf.size, 0);
87 EXPECT_EQ(buf.capacity, 16);
88 EXPECT_EQ(buf.allocator, &alloc);
89 cxBufferDestroy(&buf);
90 EXPECT_FALSE(alloc.verify());
91 cxFree(&alloc, space);
92 EXPECT_TRUE(alloc.verify());
93 }
95 TEST(BufferInit, WrapSpaceAutoFree) {
96 CxTestingAllocator alloc;
97 CxBuffer buf;
98 void *space = cxMalloc(&alloc, 16);
99 cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_FREE_CONTENTS);
100 expect_default_flush_config(&buf);
101 EXPECT_EQ(buf.space, space);
102 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
103 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS);
104 EXPECT_EQ(buf.pos, 0);
105 EXPECT_EQ(buf.size, 0);
106 EXPECT_EQ(buf.capacity, 16);
107 EXPECT_EQ(buf.allocator, &alloc);
108 EXPECT_FALSE(alloc.verify());
109 cxBufferDestroy(&buf);
110 EXPECT_TRUE(alloc.verify());
111 }
113 TEST(BufferInit, FreshSpace) {
114 CxTestingAllocator alloc;
115 CxBuffer buf;
116 cxBufferInit(&buf, nullptr, 8, &alloc, CX_BUFFER_DEFAULT);
117 expect_default_flush_config(&buf);
118 EXPECT_NE(buf.space, nullptr);
119 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
120 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS);
121 EXPECT_EQ(buf.pos, 0);
122 EXPECT_EQ(buf.size, 0);
123 EXPECT_EQ(buf.capacity, 8);
124 EXPECT_EQ(buf.allocator, &alloc);
125 EXPECT_FALSE(alloc.verify()); // space is still allocated
126 cxBufferDestroy(&buf);
127 EXPECT_TRUE(alloc.verify());
128 }
130 TEST(BufferInit, OnHeap) {
131 CxTestingAllocator alloc;
132 CxBuffer *buf;
133 void *space = cxMalloc(&alloc, 16);
134 buf = cxBufferCreate(space, 16, &alloc, CX_BUFFER_FREE_CONTENTS);
135 EXPECT_NE(buf, nullptr);
136 expect_default_flush_config(buf);
137 EXPECT_EQ(buf->space, space);
138 EXPECT_EQ(buf->flags & CX_BUFFER_AUTO_EXTEND, 0);
139 EXPECT_EQ(buf->flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS);
140 EXPECT_EQ(buf->pos, 0);
141 EXPECT_EQ(buf->size, 0);
142 EXPECT_EQ(buf->capacity, 16);
143 EXPECT_EQ(buf->allocator, &alloc);
144 cxBufferFree(buf);
145 EXPECT_TRUE(alloc.verify());
146 }
148 class BufferShiftFixture : public ::testing::Test {
149 protected:
150 void SetUp() override {
151 ASSERT_TRUE(alloc.verify());
152 cxBufferInit(&buf, nullptr, 16, &alloc, CX_BUFFER_DEFAULT);
153 memcpy(buf.space, "test____________", 16);
154 buf.capacity = 8; // purposely pretend that the buffer has less capacity s.t. we can test beyond the range
155 buf.pos = 4;
156 buf.size = 4;
157 }
159 void TearDown() override {
160 cxBufferDestroy(&buf);
161 EXPECT_TRUE(alloc.verify());
162 }
164 CxTestingAllocator alloc;
165 CxBuffer buf{};
166 };
168 class BufferShiftLeft : public BufferShiftFixture {
169 };
171 TEST_F(BufferShiftLeft, Zero) {
172 ASSERT_EQ(buf.pos, 4);
173 ASSERT_EQ(buf.size, 4);
174 int ret = cxBufferShiftLeft(&buf, 0);
175 EXPECT_EQ(ret, 0);
176 EXPECT_EQ(buf.pos, 4);
177 EXPECT_EQ(buf.size, 4);
178 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
179 }
181 TEST_F(BufferShiftLeft, ZeroOffsetInterface) {
182 ASSERT_EQ(buf.pos, 4);
183 ASSERT_EQ(buf.size, 4);
184 int ret = cxBufferShift(&buf, -0);
185 EXPECT_EQ(ret, 0);
186 EXPECT_EQ(buf.pos, 4);
187 EXPECT_EQ(buf.size, 4);
188 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
189 }
191 TEST_F(BufferShiftLeft, Standard) {
192 ASSERT_EQ(buf.pos, 4);
193 ASSERT_EQ(buf.size, 4);
194 int ret = cxBufferShiftLeft(&buf, 2);
195 EXPECT_EQ(ret, 0);
196 EXPECT_EQ(buf.pos, 2);
197 EXPECT_EQ(buf.size, 2);
198 EXPECT_TRUE(memcmp(buf.space, "stst________", 8) == 0);
199 }
201 TEST_F(BufferShiftLeft, Overshift) {
202 ASSERT_LT(buf.pos, 6);
203 ASSERT_LT(buf.size, 6);
204 int ret = cxBufferShiftLeft(&buf, 6);
205 EXPECT_EQ(ret, 0);
206 EXPECT_EQ(buf.pos, 0);
207 EXPECT_EQ(buf.size, 0);
208 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
209 }
211 TEST_F(BufferShiftLeft, OvershiftPosOnly) {
212 buf.pos = 2;
213 ASSERT_EQ(buf.size, 4);
214 int ret = cxBufferShiftLeft(&buf, 3);
215 EXPECT_EQ(ret, 0);
216 EXPECT_EQ(buf.pos, 0);
217 EXPECT_EQ(buf.size, 1);
218 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
219 }
221 TEST_F(BufferShiftLeft, OffsetInterface) {
222 buf.pos = 3;
223 ASSERT_EQ(buf.size, 4);
224 int ret = cxBufferShift(&buf, -2);
225 EXPECT_EQ(ret, 0);
226 EXPECT_EQ(buf.pos, 1);
227 EXPECT_EQ(buf.size, 2);
228 EXPECT_TRUE(memcmp(buf.space, "stst________", 8) == 0);
229 }
231 class BufferShiftRight : public BufferShiftFixture {
232 };
234 TEST_F(BufferShiftRight, Zero) {
235 ASSERT_EQ(buf.pos, 4);
236 ASSERT_EQ(buf.size, 4);
237 int ret = cxBufferShiftRight(&buf, 0);
238 EXPECT_EQ(ret, 0);
239 EXPECT_EQ(buf.pos, 4);
240 EXPECT_EQ(buf.size, 4);
241 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
242 }
244 TEST_F(BufferShiftRight, ZeroOffsetInterface) {
245 ASSERT_EQ(buf.pos, 4);
246 ASSERT_EQ(buf.size, 4);
247 int ret = cxBufferShift(&buf, +0);
248 EXPECT_EQ(ret, 0);
249 EXPECT_EQ(buf.pos, 4);
250 EXPECT_EQ(buf.size, 4);
251 EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
252 }
254 TEST_F(BufferShiftRight, Standard) {
255 ASSERT_EQ(buf.pos, 4);
256 ASSERT_EQ(buf.size, 4);
257 int ret = cxBufferShiftRight(&buf, 3);
258 EXPECT_EQ(ret, 0);
259 EXPECT_EQ(buf.pos, 7);
260 EXPECT_EQ(buf.size, 7);
261 EXPECT_TRUE(memcmp(buf.space, "testest_____", 8) == 0);
262 }
264 TEST_F(BufferShiftRight, OvershiftDiscard) {
265 ASSERT_EQ(buf.pos, 4);
266 ASSERT_EQ(buf.size, 4);
267 ASSERT_EQ(buf.capacity, 8);
268 int ret = cxBufferShiftRight(&buf, 6);
269 EXPECT_EQ(ret, 0);
270 EXPECT_EQ(buf.pos, 8);
271 EXPECT_EQ(buf.size, 8);
272 EXPECT_EQ(buf.capacity, 8);
273 EXPECT_TRUE(memcmp(buf.space, "test__te____", 8) == 0);
274 }
276 TEST_F(BufferShiftRight, OvershiftExtend) {
277 ASSERT_EQ(buf.pos, 4);
278 ASSERT_EQ(buf.size, 4);
279 ASSERT_EQ(buf.capacity, 8);
280 buf.flags |= CX_BUFFER_AUTO_EXTEND;
281 int ret = cxBufferShiftRight(&buf, 6);
282 EXPECT_EQ(ret, 0);
283 EXPECT_EQ(buf.pos, 10);
284 EXPECT_EQ(buf.size, 10);
285 EXPECT_GE(buf.capacity, 10);
286 EXPECT_TRUE(memcmp(buf.space, "test__test__", 8) == 0);
287 }
289 TEST_F(BufferShiftRight, OffsetInterface) {
290 buf.pos = 3;
291 ASSERT_EQ(buf.size, 4);
292 int ret = cxBufferShift(&buf, 2);
293 EXPECT_EQ(ret, 0);
294 EXPECT_EQ(buf.pos, 5);
295 EXPECT_EQ(buf.size, 6);
296 EXPECT_TRUE(memcmp(buf.space, "tetest______", 8) == 0);
297 }
299 TEST(BufferMinimumCapacity, Sufficient) {
300 CxTestingAllocator alloc;
301 auto space = cxMalloc(&alloc, 8);
302 CxBuffer buf;
303 cxBufferInit(&buf, space, 8, &alloc, CX_BUFFER_FREE_CONTENTS);
304 memcpy(space, "Testing", 8);
305 buf.size = 8;
306 cxBufferMinimumCapacity(&buf, 6);
307 EXPECT_EQ(buf.capacity, 8);
308 EXPECT_EQ(buf.size, 8);
309 EXPECT_TRUE(memcmp(buf.space, "Testing", 8) == 0);
310 cxBufferDestroy(&buf);
311 EXPECT_TRUE(alloc.verify());
312 }
314 TEST(BufferMinimumCapacity, Extend) {
315 CxTestingAllocator alloc;
316 auto space = cxMalloc(&alloc, 8);
317 CxBuffer buf;
318 cxBufferInit(&buf, space, 8, &alloc, CX_BUFFER_FREE_CONTENTS); // NO auto extend!
319 memcpy(space, "Testing", 8);
320 buf.size = 8;
321 cxBufferMinimumCapacity(&buf, 16);
322 EXPECT_EQ(buf.capacity, 16);
323 EXPECT_EQ(buf.size, 8);
324 EXPECT_TRUE(memcmp(buf.space, "Testing", 8) == 0);
325 cxBufferDestroy(&buf);
326 EXPECT_TRUE(alloc.verify());
327 }
329 TEST(BufferClear, Test) {
330 char space[16];
331 strcpy(space, "clear test");
332 CxBuffer buf;
333 cxBufferInit(&buf, space, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
334 ASSERT_EQ(buf.size, 0);
335 // only clear the used part of the buffer
336 cxBufferClear(&buf);
337 EXPECT_EQ(memcmp(space, "clear test", 10), 0);
338 buf.size = 5;
339 buf.pos = 3;
340 cxBufferClear(&buf);
341 EXPECT_EQ(memcmp(space, "\0\0\0\0\0 test", 10), 0);
342 EXPECT_EQ(buf.size, 0);
343 EXPECT_EQ(buf.pos, 0);
344 cxBufferDestroy(&buf);
345 }
347 class BufferWrite : public ::testing::Test {
348 protected:
349 CxBuffer buf{}, target{};
351 void SetUp() override {
352 cxBufferInit(&target, nullptr, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
353 cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
354 buf.capacity = 8; // artificially reduce capacity to check OOB writes
355 memset(buf.space, 0, 16);
356 memcpy(buf.space, "prep", 4);
357 buf.size = buf.pos = 4;
358 }
360 void TearDown() override {
361 cxBufferDestroy(&buf);
362 cxBufferDestroy(&target);
363 }
365 void enableFlushing() {
366 buf.flush_target = ⌖
367 buf.flush_func = reinterpret_cast<cx_write_func>(cxBufferWrite);
368 buf.flush_blkmax = 1;
369 }
370 };
372 static size_t mock_write_limited_rate(
373 void const *ptr,
374 size_t size,
375 __attribute__((unused)) size_t nitems,
376 CxBuffer *buffer
377 ) {
378 // simulate limited target drain capacity
379 static bool full = false;
380 if (full) {
381 full = false;
382 return 0;
383 } else {
384 full = true;
385 return cxBufferWrite(ptr, size, nitems > 2 ? 2 : nitems, buffer);
386 }
387 }
389 TEST_F(BufferWrite, SizeOneFit) {
390 const char *data = "test";
391 ASSERT_EQ(buf.capacity, 8);
392 ASSERT_EQ(buf.pos, 4);
393 ASSERT_EQ(buf.size, 4);
394 size_t written = cxBufferWrite(data, 1, 4, &buf);
395 EXPECT_EQ(written, 4);
396 EXPECT_EQ(buf.size, 8);
397 EXPECT_EQ(buf.pos, 8);
398 EXPECT_EQ(buf.capacity, 8);
399 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
400 }
402 TEST_F(BufferWrite, SizeOneDiscard) {
403 const char *data = "testing";
404 ASSERT_EQ(buf.capacity, 8);
405 ASSERT_EQ(buf.pos, 4);
406 ASSERT_EQ(buf.size, 4);
407 size_t written = cxBufferWrite(data, 1, 7, &buf);
408 EXPECT_EQ(written, 4);
409 EXPECT_EQ(buf.size, 8);
410 EXPECT_EQ(buf.pos, 8);
411 EXPECT_EQ(buf.capacity, 8);
412 EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0);
413 }
415 TEST_F(BufferWrite, SizeOneExtend) {
416 buf.flags |= CX_BUFFER_AUTO_EXTEND;
417 const char *data = "testing";
418 ASSERT_EQ(buf.capacity, 8);
419 ASSERT_EQ(buf.pos, 4);
420 ASSERT_EQ(buf.size, 4);
421 size_t written = cxBufferWrite(data, 1, 7, &buf);
422 EXPECT_EQ(written, 7);
423 EXPECT_EQ(buf.size, 11);
424 EXPECT_EQ(buf.pos, 11);
425 EXPECT_GE(buf.capacity, 11);
426 EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0);
427 }
429 TEST_F(BufferWrite, MultibyteFit) {
430 const char *data = "test";
431 ASSERT_EQ(buf.capacity, 8);
432 ASSERT_EQ(buf.pos, 4);
433 ASSERT_EQ(buf.size, 4);
434 size_t written = cxBufferWrite(data, 2, 2, &buf);
435 EXPECT_EQ(written, 2);
436 EXPECT_EQ(buf.size, 8);
437 EXPECT_EQ(buf.pos, 8);
438 EXPECT_EQ(buf.capacity, 8);
439 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
440 }
442 TEST_F(BufferWrite, MultibyteDiscard) {
443 const char *data = "testing";
444 ASSERT_EQ(buf.capacity, 8);
445 ASSERT_EQ(buf.size, 4);
446 buf.pos = 3;
447 size_t written = cxBufferWrite(data, 2, 4, &buf);
448 // remember: whole elements are discarded if they do not fit
449 EXPECT_EQ(written, 2);
450 EXPECT_EQ(buf.size, 7);
451 EXPECT_EQ(buf.pos, 7);
452 EXPECT_EQ(buf.capacity, 8);
453 EXPECT_EQ(memcmp(buf.space, "pretest\0", 8), 0);
454 }
456 TEST_F(BufferWrite, MultibyteExtend) {
457 buf.flags |= CX_BUFFER_AUTO_EXTEND;
458 const char *data = "tester";
459 ASSERT_EQ(buf.capacity, 8);
460 ASSERT_EQ(buf.size, 4);
461 buf.pos = 3;
462 size_t written = cxBufferWrite(data, 2, 3, &buf);
463 // remember: whole elements are discarded if they do not fit
464 EXPECT_EQ(written, 3);
465 EXPECT_EQ(buf.size, 9);
466 EXPECT_EQ(buf.pos, 9);
467 EXPECT_GE(buf.capacity, 9);
468 EXPECT_EQ(memcmp(buf.space, "pretester", 9), 0);
469 }
471 TEST_F(BufferWrite, PutcWrapperFit) {
472 ASSERT_EQ(buf.capacity, 8);
473 ASSERT_EQ(buf.pos, 4);
474 ASSERT_EQ(buf.size, 4);
475 int c = cxBufferPut(&buf, 0x200 | 'a');
476 EXPECT_EQ(c, 'a');
477 EXPECT_EQ(buf.size, 5);
478 EXPECT_EQ(buf.pos, 5);
479 EXPECT_EQ(buf.capacity, 8);
480 EXPECT_EQ(memcmp(buf.space, "prepa\0", 6), 0);
481 }
483 TEST_F(BufferWrite, PutcWrapperDiscard) {
484 ASSERT_EQ(buf.capacity, 8);
485 ASSERT_EQ(buf.size, 4);
486 buf.pos = 8;
487 int c = cxBufferPut(&buf, 0x200 | 'a');
488 EXPECT_EQ(c, EOF);
489 EXPECT_EQ(buf.size, 4);
490 EXPECT_EQ(buf.pos, 8);
491 EXPECT_EQ(buf.capacity, 8);
492 EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0\0", 9), 0);
493 }
495 TEST_F(BufferWrite, PutcWrapperExtend) {
496 buf.flags |= CX_BUFFER_AUTO_EXTEND;
497 ASSERT_EQ(buf.capacity, 8);
498 ASSERT_EQ(buf.size, 4);
499 buf.pos = 8;
500 int c = cxBufferPut(&buf, 0x200 | 'a');
501 EXPECT_EQ(c, 'a');
502 EXPECT_EQ(buf.size, 9);
503 EXPECT_EQ(buf.pos, 9);
504 EXPECT_GE(buf.capacity, 9);
505 EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0a", 9), 0);
506 }
508 TEST_F(BufferWrite, PutStringWrapperFit) {
509 const char *data = "test";
510 ASSERT_EQ(buf.capacity, 8);
511 ASSERT_EQ(buf.pos, 4);
512 ASSERT_EQ(buf.size, 4);
513 size_t written = cxBufferPutString(&buf, data);
514 EXPECT_EQ(written, 4);
515 EXPECT_EQ(buf.size, 8);
516 EXPECT_EQ(buf.pos, 8);
517 EXPECT_EQ(buf.capacity, 8);
518 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
519 }
521 TEST_F(BufferWrite, PutStringWrapperDiscard) {
522 const char *data = "testing";
523 ASSERT_EQ(buf.capacity, 8);
524 ASSERT_EQ(buf.pos, 4);
525 ASSERT_EQ(buf.size, 4);
526 size_t written = cxBufferPutString(&buf, data);
527 EXPECT_EQ(written, 4);
528 EXPECT_EQ(buf.size, 8);
529 EXPECT_EQ(buf.pos, 8);
530 EXPECT_EQ(buf.capacity, 8);
531 EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0);
532 }
534 TEST_F(BufferWrite, PutStringWrapperExtend) {
535 buf.flags |= CX_BUFFER_AUTO_EXTEND;
536 const char *data = "testing";
537 ASSERT_EQ(buf.capacity, 8);
538 ASSERT_EQ(buf.pos, 4);
539 ASSERT_EQ(buf.size, 4);
540 size_t written = cxBufferPutString(&buf, data);
541 EXPECT_EQ(written, 7);
542 EXPECT_EQ(buf.size, 11);
543 EXPECT_EQ(buf.pos, 11);
544 EXPECT_GE(buf.capacity, 11);
545 EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0);
546 }
548 TEST_F(BufferWrite, MultOverflow) {
549 const char *data = "testing";
550 ASSERT_EQ(buf.capacity, 8);
551 ASSERT_EQ(buf.pos, 4);
552 ASSERT_EQ(buf.size, 4);
553 size_t written = cxBufferWrite(data, 8, SIZE_MAX / 4, &buf);
554 EXPECT_EQ(written, 0);
555 EXPECT_EQ(buf.capacity, 8);
556 EXPECT_EQ(buf.pos, 4);
557 EXPECT_EQ(buf.size, 4);
558 EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0);
559 }
561 TEST_F(BufferWrite, MaxCapaOverflow) {
562 buf.flags |= CX_BUFFER_AUTO_EXTEND;
563 const char *data = "testing";
564 ASSERT_EQ(buf.capacity, 8);
565 ASSERT_EQ(buf.pos, 4);
566 ASSERT_EQ(buf.size, 4);
567 size_t written = cxBufferWrite(data, 1, SIZE_MAX - 2, &buf);
568 EXPECT_EQ(written, 0);
569 EXPECT_EQ(buf.capacity, 8);
570 EXPECT_EQ(buf.pos, 4);
571 EXPECT_EQ(buf.size, 4);
572 EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0);
573 }
575 TEST_F(BufferWrite, OnlyOverwrite) {
576 buf.flags |= CX_BUFFER_AUTO_EXTEND;
577 ASSERT_EQ(buf.capacity, 8);
578 memcpy(buf.space, "preptest", 8);
579 buf.pos = 3;
580 buf.size = 8;
581 size_t written = cxBufferWrite("XXX", 2, 2, &buf);
582 EXPECT_EQ(written, 2);
583 EXPECT_EQ(buf.capacity, 8);
584 EXPECT_EQ(buf.size, 8);
585 EXPECT_EQ(buf.pos, 7);
586 EXPECT_EQ(memcmp(buf.space, "preXXX\0t", 8), 0);
587 }
589 TEST_F(BufferWrite, FlushAtCapacity) {
590 enableFlushing();
591 ASSERT_EQ(buf.capacity, 8);
592 ASSERT_EQ(buf.pos, 4);
593 size_t written = cxBufferWrite("foo", 1, 3, &buf);
594 EXPECT_EQ(written, 3);
595 ASSERT_EQ(buf.pos, 7);
596 ASSERT_EQ(buf.size, 7);
597 ASSERT_EQ(target.pos, 0);
598 ASSERT_EQ(target.size, 0);
599 written = cxBufferWrite("hello", 1, 5, &buf);
600 EXPECT_EQ(written, 5);
601 EXPECT_EQ(buf.pos, 0);
602 EXPECT_EQ(buf.size, 0);
603 EXPECT_EQ(buf.capacity, 8);
604 EXPECT_EQ(target.pos, 12);
605 ASSERT_EQ(target.size, 12);
606 EXPECT_EQ(memcmp(target.space, "prepfoohello", 12), 0);
607 }
609 TEST_F(BufferWrite, FlushAtThreshold) {
610 enableFlushing();
611 buf.flush_threshold = 12;
612 buf.flags |= CX_BUFFER_AUTO_EXTEND;
613 ASSERT_EQ(buf.capacity, 8);
614 ASSERT_EQ(buf.pos, 4);
615 size_t written = cxBufferWrite("foobar", 1, 6, &buf);
616 EXPECT_EQ(written, 6);
617 ASSERT_EQ(buf.pos, 10);
618 ASSERT_EQ(buf.size, 10);
619 ASSERT_GE(buf.capacity, 10);
620 ASSERT_LE(buf.capacity, 12);
621 ASSERT_EQ(target.pos, 0);
622 ASSERT_EQ(target.size, 0);
623 written = cxBufferWrite("hello", 1, 5, &buf);
624 EXPECT_EQ(written, 5);
625 EXPECT_EQ(buf.pos, 0);
626 EXPECT_EQ(buf.size, 0);
627 EXPECT_LE(buf.capacity, 12);
628 EXPECT_EQ(target.pos, 15);
629 ASSERT_EQ(target.size, 15);
630 EXPECT_EQ(memcmp(target.space, "prepfoobarhello", 15), 0);
631 }
633 TEST_F(BufferWrite, FlushRateLimited) {
634 enableFlushing();
635 // limit the rate of the flush function and the capacity of the target
636 target.capacity = 16;
637 target.flags &= ~CX_BUFFER_AUTO_EXTEND;
638 buf.flush_func = (cx_write_func) mock_write_limited_rate;
639 ASSERT_EQ(buf.capacity, 8);
640 ASSERT_EQ(buf.pos, 4);
641 size_t written = cxBufferWrite("foo", 1, 3, &buf);
642 EXPECT_EQ(written, 3);
643 ASSERT_EQ(buf.pos, 7);
644 ASSERT_EQ(buf.size, 7);
645 ASSERT_EQ(target.pos, 0);
646 ASSERT_EQ(target.size, 0);
647 written = cxBufferWrite("hello, world!", 1, 13, &buf);
648 // " world!" fits into this buffer, the remaining stuff is flushed out
649 EXPECT_EQ(written, 13);
650 EXPECT_EQ(buf.pos, 7);
651 EXPECT_EQ(buf.size, 7);
652 EXPECT_EQ(buf.capacity, 8);
653 EXPECT_EQ(memcmp(buf.space, " world!", 7), 0);
654 EXPECT_EQ(target.pos, 13);
655 ASSERT_EQ(target.size, 13);
656 EXPECT_EQ(target.capacity, 16);
657 EXPECT_EQ(memcmp(target.space, "prepfoohello,", 13), 0);
658 }
660 class BufferSeek : public BufferFixture {
661 };
663 TEST_F(BufferSeek, SetZero) {
664 int result = cxBufferSeek(&buf, 0, SEEK_SET);
665 EXPECT_EQ(result, 0);
666 EXPECT_EQ(buf.pos, 0);
667 }
669 TEST_F(BufferSeek, SetValid) {
670 int result = cxBufferSeek(&buf, 5, SEEK_SET);
671 EXPECT_EQ(result, 0);
672 EXPECT_EQ(buf.pos, 5);
673 }
675 TEST_F(BufferSeek, SetInvalid) {
676 ASSERT_EQ(buf.pos, 3);
677 int result = cxBufferSeek(&buf, 6, SEEK_SET);
678 EXPECT_NE(result, 0);
679 EXPECT_EQ(buf.pos, 3);
680 }
682 TEST_F(BufferSeek, CurZero) {
683 ASSERT_EQ(buf.pos, 3);
684 int result = cxBufferSeek(&buf, 0, SEEK_CUR);
685 EXPECT_EQ(result, 0);
686 EXPECT_EQ(buf.pos, 3);
687 }
689 TEST_F(BufferSeek, CurValidPositive) {
690 ASSERT_EQ(buf.pos, 3);
691 int result = cxBufferSeek(&buf, 2, SEEK_CUR);
692 EXPECT_EQ(result, 0);
693 EXPECT_EQ(buf.pos, 5);
694 }
696 TEST_F(BufferSeek, CurValidNegative) {
697 ASSERT_EQ(buf.pos, 3);
698 int result = cxBufferSeek(&buf, -3, SEEK_CUR);
699 EXPECT_EQ(result, 0);
700 EXPECT_EQ(buf.pos, 0);
701 }
703 TEST_F(BufferSeek, CurInvalidPositive) {
704 ASSERT_EQ(buf.pos, 3);
705 int result = cxBufferSeek(&buf, 3, SEEK_CUR);
706 EXPECT_NE(result, 0);
707 EXPECT_EQ(buf.pos, 3);
708 }
710 TEST_F(BufferSeek, CurInvalidNegative) {
711 ASSERT_EQ(buf.pos, 3);
712 int result = cxBufferSeek(&buf, -4, SEEK_CUR);
713 EXPECT_NE(result, 0);
714 EXPECT_EQ(buf.pos, 3);
715 }
717 TEST_F(BufferSeek, EndZero) {
718 ASSERT_EQ(buf.size, 6);
719 int result = cxBufferSeek(&buf, 0, SEEK_END);
720 // the (past-the-)end position is always invalid
721 EXPECT_NE(result, 0);
722 EXPECT_EQ(buf.pos, 3);
723 }
725 TEST_F(BufferSeek, EndValid) {
726 ASSERT_EQ(buf.size, 6);
727 int result = cxBufferSeek(&buf, -6, SEEK_END);
728 EXPECT_EQ(result, 0);
729 EXPECT_EQ(buf.pos, 0);
730 }
732 TEST_F(BufferSeek, EndInvalid) {
733 ASSERT_EQ(buf.size, 6);
734 int result = cxBufferSeek(&buf, 1, SEEK_END);
735 EXPECT_NE(result, 0);
736 EXPECT_EQ(buf.pos, 3);
737 }
739 TEST_F(BufferSeek, WhenceInvalid) {
740 ASSERT_EQ(buf.size, 6);
741 ASSERT_EQ(buf.pos, 3);
742 int result = cxBufferSeek(&buf, 2, 9000);
743 EXPECT_NE(result, 0);
744 EXPECT_EQ(buf.size, 6);
745 EXPECT_EQ(buf.pos, 3);
746 }
748 class BufferEof : public BufferFixture {
749 };
751 TEST_F(BufferEof, Reached) {
752 buf.pos = buf.size;
753 EXPECT_TRUE(cxBufferEof(&buf));
754 buf.pos = buf.size - 1;
755 ASSERT_FALSE(cxBufferEof(&buf));
756 cxBufferPut(&buf, 'a');
757 EXPECT_TRUE(cxBufferEof(&buf));
758 }
760 TEST_F(BufferEof, NotReached) {
761 buf.pos = buf.size - 1;
762 EXPECT_FALSE(cxBufferEof(&buf));
763 buf.pos = 0;
764 cxBufferWrite("test", 1, 5, &buf);
765 EXPECT_FALSE(cxBufferEof(&buf));
766 }
768 class BufferRead : public ::testing::Test {
769 protected:
770 CxBuffer buf{};
772 void SetUp() override {
773 cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
774 buf.capacity = 8; // artificially reduce capacity to check OOB writes
775 memset(buf.space, 0, 16);
776 memcpy(buf.space, "some data", 9);
777 buf.size = 9;
778 }
780 void TearDown() override {
781 cxBufferDestroy(&buf);
782 }
783 };
785 TEST_F(BufferRead, GetByte) {
786 buf.pos = 2;
787 EXPECT_EQ(cxBufferGet(&buf), 'm');
788 EXPECT_EQ(cxBufferGet(&buf), 'e');
789 EXPECT_EQ(cxBufferGet(&buf), ' ');
790 EXPECT_EQ(cxBufferGet(&buf), 'd');
791 EXPECT_EQ(buf.pos, 6);
792 }
794 TEST_F(BufferRead, GetEof) {
795 buf.pos = buf.size;
796 EXPECT_EQ(cxBufferGet(&buf), EOF);
797 }
799 TEST_F(BufferRead, ReadWithinBounds) {
800 buf.pos = 2;
801 char target[4];
802 auto read = cxBufferRead(&target, 1, 4, &buf);
803 ASSERT_EQ(read, 4);
804 EXPECT_EQ(memcmp(&target, "me d", 4), 0);
805 EXPECT_EQ(buf.pos, 6);
806 }
808 TEST_F(BufferRead, ReadOutOfBounds) {
809 buf.pos = 6;
810 char target[4];
811 auto read = cxBufferRead(&target, 1, 4, &buf);
812 ASSERT_EQ(read, 3);
813 EXPECT_EQ(memcmp(&target, "ata", 3), 0);
814 EXPECT_EQ(buf.pos, 9);
815 }
817 TEST_F(BufferRead, ReadOutOfBoundsMultibyte) {
818 buf.pos = 6;
819 char target[4];
820 target[2] = '\0';
821 auto read = cxBufferRead(&target, 2, 2, &buf);
822 ASSERT_EQ(read, 1);
823 EXPECT_EQ(memcmp(&target, "at\0", 3), 0);
824 EXPECT_EQ(buf.pos, 8);
825 }
827 TEST_F(BufferRead, ReadEof) {
828 buf.pos = 9;
829 char target[4];
830 auto read = cxBufferRead(&target, 1, 1, &buf);
831 ASSERT_EQ(read, 0);
832 EXPECT_EQ(buf.pos, 9);
833 }