Mon, 18 Dec 2023 15:13:26 +0100
add cxBufferReset() - resolves #338
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 TEST(BufferReset, Test) {
348 char space[16];
349 strcpy(space, "reset test");
350 CxBuffer buf;
351 cxBufferInit(&buf, space, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
352 buf.size = 5;
353 buf.pos = 3;
354 cxBufferReset(&buf);
355 EXPECT_EQ(memcmp(space, "reset test", 10), 0);
356 EXPECT_EQ(buf.size, 0);
357 EXPECT_EQ(buf.pos, 0);
358 cxBufferDestroy(&buf);
359 }
361 class BufferWrite : public ::testing::Test {
362 protected:
363 CxBuffer buf{}, target{};
365 void SetUp() override {
366 cxBufferInit(&target, nullptr, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
367 cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
368 buf.capacity = 8; // artificially reduce capacity to check OOB writes
369 memset(buf.space, 0, 16);
370 memcpy(buf.space, "prep", 4);
371 buf.size = buf.pos = 4;
372 }
374 void TearDown() override {
375 cxBufferDestroy(&buf);
376 cxBufferDestroy(&target);
377 }
379 void enableFlushing() {
380 buf.flush_target = ⌖
381 buf.flush_func = reinterpret_cast<cx_write_func>(cxBufferWrite);
382 buf.flush_blkmax = 1;
383 }
384 };
386 static size_t mock_write_limited_rate(
387 void const *ptr,
388 size_t size,
389 __attribute__((unused)) size_t nitems,
390 CxBuffer *buffer
391 ) {
392 // simulate limited target drain capacity
393 static bool full = false;
394 if (full) {
395 full = false;
396 return 0;
397 } else {
398 full = true;
399 return cxBufferWrite(ptr, size, nitems > 2 ? 2 : nitems, buffer);
400 }
401 }
403 TEST_F(BufferWrite, SizeOneFit) {
404 const char *data = "test";
405 ASSERT_EQ(buf.capacity, 8);
406 ASSERT_EQ(buf.pos, 4);
407 ASSERT_EQ(buf.size, 4);
408 size_t written = cxBufferWrite(data, 1, 4, &buf);
409 EXPECT_EQ(written, 4);
410 EXPECT_EQ(buf.size, 8);
411 EXPECT_EQ(buf.pos, 8);
412 EXPECT_EQ(buf.capacity, 8);
413 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
414 }
416 TEST_F(BufferWrite, SizeOneDiscard) {
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, 4);
423 EXPECT_EQ(buf.size, 8);
424 EXPECT_EQ(buf.pos, 8);
425 EXPECT_EQ(buf.capacity, 8);
426 EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0);
427 }
429 TEST_F(BufferWrite, SizeOneExtend) {
430 buf.flags |= CX_BUFFER_AUTO_EXTEND;
431 const char *data = "testing";
432 ASSERT_EQ(buf.capacity, 8);
433 ASSERT_EQ(buf.pos, 4);
434 ASSERT_EQ(buf.size, 4);
435 size_t written = cxBufferWrite(data, 1, 7, &buf);
436 EXPECT_EQ(written, 7);
437 EXPECT_EQ(buf.size, 11);
438 EXPECT_EQ(buf.pos, 11);
439 EXPECT_GE(buf.capacity, 11);
440 EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0);
441 }
443 TEST_F(BufferWrite, MultibyteFit) {
444 const char *data = "test";
445 ASSERT_EQ(buf.capacity, 8);
446 ASSERT_EQ(buf.pos, 4);
447 ASSERT_EQ(buf.size, 4);
448 size_t written = cxBufferWrite(data, 2, 2, &buf);
449 EXPECT_EQ(written, 2);
450 EXPECT_EQ(buf.size, 8);
451 EXPECT_EQ(buf.pos, 8);
452 EXPECT_EQ(buf.capacity, 8);
453 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
454 }
456 TEST_F(BufferWrite, MultibyteDiscard) {
457 const char *data = "testing";
458 ASSERT_EQ(buf.capacity, 8);
459 ASSERT_EQ(buf.size, 4);
460 buf.pos = 3;
461 size_t written = cxBufferWrite(data, 2, 4, &buf);
462 // remember: whole elements are discarded if they do not fit
463 EXPECT_EQ(written, 2);
464 EXPECT_EQ(buf.size, 7);
465 EXPECT_EQ(buf.pos, 7);
466 EXPECT_EQ(buf.capacity, 8);
467 EXPECT_EQ(memcmp(buf.space, "pretest\0", 8), 0);
468 }
470 TEST_F(BufferWrite, MultibyteExtend) {
471 buf.flags |= CX_BUFFER_AUTO_EXTEND;
472 const char *data = "tester";
473 ASSERT_EQ(buf.capacity, 8);
474 ASSERT_EQ(buf.size, 4);
475 buf.pos = 3;
476 size_t written = cxBufferWrite(data, 2, 3, &buf);
477 // remember: whole elements are discarded if they do not fit
478 EXPECT_EQ(written, 3);
479 EXPECT_EQ(buf.size, 9);
480 EXPECT_EQ(buf.pos, 9);
481 EXPECT_GE(buf.capacity, 9);
482 EXPECT_EQ(memcmp(buf.space, "pretester", 9), 0);
483 }
485 TEST_F(BufferWrite, PutcWrapperFit) {
486 ASSERT_EQ(buf.capacity, 8);
487 ASSERT_EQ(buf.pos, 4);
488 ASSERT_EQ(buf.size, 4);
489 int c = cxBufferPut(&buf, 0x200 | 'a');
490 EXPECT_EQ(c, 'a');
491 EXPECT_EQ(buf.size, 5);
492 EXPECT_EQ(buf.pos, 5);
493 EXPECT_EQ(buf.capacity, 8);
494 EXPECT_EQ(memcmp(buf.space, "prepa\0", 6), 0);
495 }
497 TEST_F(BufferWrite, PutcWrapperDiscard) {
498 ASSERT_EQ(buf.capacity, 8);
499 ASSERT_EQ(buf.size, 4);
500 buf.pos = 8;
501 int c = cxBufferPut(&buf, 0x200 | 'a');
502 EXPECT_EQ(c, EOF);
503 EXPECT_EQ(buf.size, 4);
504 EXPECT_EQ(buf.pos, 8);
505 EXPECT_EQ(buf.capacity, 8);
506 EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0\0", 9), 0);
507 }
509 TEST_F(BufferWrite, PutcWrapperExtend) {
510 buf.flags |= CX_BUFFER_AUTO_EXTEND;
511 ASSERT_EQ(buf.capacity, 8);
512 ASSERT_EQ(buf.size, 4);
513 buf.pos = 8;
514 int c = cxBufferPut(&buf, 0x200 | 'a');
515 EXPECT_EQ(c, 'a');
516 EXPECT_EQ(buf.size, 9);
517 EXPECT_EQ(buf.pos, 9);
518 EXPECT_GE(buf.capacity, 9);
519 EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0a", 9), 0);
520 }
522 TEST_F(BufferWrite, PutStringWrapperFit) {
523 const char *data = "test";
524 ASSERT_EQ(buf.capacity, 8);
525 ASSERT_EQ(buf.pos, 4);
526 ASSERT_EQ(buf.size, 4);
527 size_t written = cxBufferPutString(&buf, data);
528 EXPECT_EQ(written, 4);
529 EXPECT_EQ(buf.size, 8);
530 EXPECT_EQ(buf.pos, 8);
531 EXPECT_EQ(buf.capacity, 8);
532 EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
533 }
535 TEST_F(BufferWrite, PutStringWrapperDiscard) {
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, 4);
542 EXPECT_EQ(buf.size, 8);
543 EXPECT_EQ(buf.pos, 8);
544 EXPECT_EQ(buf.capacity, 8);
545 EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0);
546 }
548 TEST_F(BufferWrite, PutStringWrapperExtend) {
549 buf.flags |= CX_BUFFER_AUTO_EXTEND;
550 const char *data = "testing";
551 ASSERT_EQ(buf.capacity, 8);
552 ASSERT_EQ(buf.pos, 4);
553 ASSERT_EQ(buf.size, 4);
554 size_t written = cxBufferPutString(&buf, data);
555 EXPECT_EQ(written, 7);
556 EXPECT_EQ(buf.size, 11);
557 EXPECT_EQ(buf.pos, 11);
558 EXPECT_GE(buf.capacity, 11);
559 EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0);
560 }
562 TEST_F(BufferWrite, MultOverflow) {
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, 8, SIZE_MAX / 4, &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, MaxCapaOverflow) {
576 buf.flags |= CX_BUFFER_AUTO_EXTEND;
577 const char *data = "testing";
578 ASSERT_EQ(buf.capacity, 8);
579 ASSERT_EQ(buf.pos, 4);
580 ASSERT_EQ(buf.size, 4);
581 size_t written = cxBufferWrite(data, 1, SIZE_MAX - 2, &buf);
582 EXPECT_EQ(written, 0);
583 EXPECT_EQ(buf.capacity, 8);
584 EXPECT_EQ(buf.pos, 4);
585 EXPECT_EQ(buf.size, 4);
586 EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0);
587 }
589 TEST_F(BufferWrite, OnlyOverwrite) {
590 buf.flags |= CX_BUFFER_AUTO_EXTEND;
591 ASSERT_EQ(buf.capacity, 8);
592 memcpy(buf.space, "preptest", 8);
593 buf.pos = 3;
594 buf.size = 8;
595 size_t written = cxBufferWrite("XXX", 2, 2, &buf);
596 EXPECT_EQ(written, 2);
597 EXPECT_EQ(buf.capacity, 8);
598 EXPECT_EQ(buf.size, 8);
599 EXPECT_EQ(buf.pos, 7);
600 EXPECT_EQ(memcmp(buf.space, "preXXX\0t", 8), 0);
601 }
603 TEST_F(BufferWrite, FlushAtCapacity) {
604 enableFlushing();
605 ASSERT_EQ(buf.capacity, 8);
606 ASSERT_EQ(buf.pos, 4);
607 size_t written = cxBufferWrite("foo", 1, 3, &buf);
608 EXPECT_EQ(written, 3);
609 ASSERT_EQ(buf.pos, 7);
610 ASSERT_EQ(buf.size, 7);
611 ASSERT_EQ(target.pos, 0);
612 ASSERT_EQ(target.size, 0);
613 written = cxBufferWrite("hello", 1, 5, &buf);
614 EXPECT_EQ(written, 5);
615 EXPECT_EQ(buf.pos, 0);
616 EXPECT_EQ(buf.size, 0);
617 EXPECT_EQ(buf.capacity, 8);
618 EXPECT_EQ(target.pos, 12);
619 ASSERT_EQ(target.size, 12);
620 EXPECT_EQ(memcmp(target.space, "prepfoohello", 12), 0);
621 }
623 TEST_F(BufferWrite, FlushAtThreshold) {
624 enableFlushing();
625 buf.flush_threshold = 12;
626 buf.flags |= CX_BUFFER_AUTO_EXTEND;
627 ASSERT_EQ(buf.capacity, 8);
628 ASSERT_EQ(buf.pos, 4);
629 size_t written = cxBufferWrite("foobar", 1, 6, &buf);
630 EXPECT_EQ(written, 6);
631 ASSERT_EQ(buf.pos, 10);
632 ASSERT_EQ(buf.size, 10);
633 ASSERT_GE(buf.capacity, 10);
634 ASSERT_LE(buf.capacity, 12);
635 ASSERT_EQ(target.pos, 0);
636 ASSERT_EQ(target.size, 0);
637 written = cxBufferWrite("hello", 1, 5, &buf);
638 EXPECT_EQ(written, 5);
639 EXPECT_EQ(buf.pos, 0);
640 EXPECT_EQ(buf.size, 0);
641 EXPECT_LE(buf.capacity, 12);
642 EXPECT_EQ(target.pos, 15);
643 ASSERT_EQ(target.size, 15);
644 EXPECT_EQ(memcmp(target.space, "prepfoobarhello", 15), 0);
645 }
647 TEST_F(BufferWrite, FlushRateLimited) {
648 enableFlushing();
649 // limit the rate of the flush function and the capacity of the target
650 target.capacity = 16;
651 target.flags &= ~CX_BUFFER_AUTO_EXTEND;
652 buf.flush_func = (cx_write_func) mock_write_limited_rate;
653 ASSERT_EQ(buf.capacity, 8);
654 ASSERT_EQ(buf.pos, 4);
655 size_t written = cxBufferWrite("foo", 1, 3, &buf);
656 EXPECT_EQ(written, 3);
657 ASSERT_EQ(buf.pos, 7);
658 ASSERT_EQ(buf.size, 7);
659 ASSERT_EQ(target.pos, 0);
660 ASSERT_EQ(target.size, 0);
661 written = cxBufferWrite("hello, world!", 1, 13, &buf);
662 // " world!" fits into this buffer, the remaining stuff is flushed out
663 EXPECT_EQ(written, 13);
664 EXPECT_EQ(buf.pos, 7);
665 EXPECT_EQ(buf.size, 7);
666 EXPECT_EQ(buf.capacity, 8);
667 EXPECT_EQ(memcmp(buf.space, " world!", 7), 0);
668 EXPECT_EQ(target.pos, 13);
669 ASSERT_EQ(target.size, 13);
670 EXPECT_EQ(target.capacity, 16);
671 EXPECT_EQ(memcmp(target.space, "prepfoohello,", 13), 0);
672 }
674 class BufferSeek : public BufferFixture {
675 };
677 TEST_F(BufferSeek, SetZero) {
678 int result = cxBufferSeek(&buf, 0, SEEK_SET);
679 EXPECT_EQ(result, 0);
680 EXPECT_EQ(buf.pos, 0);
681 }
683 TEST_F(BufferSeek, SetValid) {
684 int result = cxBufferSeek(&buf, 5, SEEK_SET);
685 EXPECT_EQ(result, 0);
686 EXPECT_EQ(buf.pos, 5);
687 }
689 TEST_F(BufferSeek, SetInvalid) {
690 ASSERT_EQ(buf.pos, 3);
691 int result = cxBufferSeek(&buf, 6, SEEK_SET);
692 EXPECT_NE(result, 0);
693 EXPECT_EQ(buf.pos, 3);
694 }
696 TEST_F(BufferSeek, CurZero) {
697 ASSERT_EQ(buf.pos, 3);
698 int result = cxBufferSeek(&buf, 0, SEEK_CUR);
699 EXPECT_EQ(result, 0);
700 EXPECT_EQ(buf.pos, 3);
701 }
703 TEST_F(BufferSeek, CurValidPositive) {
704 ASSERT_EQ(buf.pos, 3);
705 int result = cxBufferSeek(&buf, 2, SEEK_CUR);
706 EXPECT_EQ(result, 0);
707 EXPECT_EQ(buf.pos, 5);
708 }
710 TEST_F(BufferSeek, CurValidNegative) {
711 ASSERT_EQ(buf.pos, 3);
712 int result = cxBufferSeek(&buf, -3, SEEK_CUR);
713 EXPECT_EQ(result, 0);
714 EXPECT_EQ(buf.pos, 0);
715 }
717 TEST_F(BufferSeek, CurInvalidPositive) {
718 ASSERT_EQ(buf.pos, 3);
719 int result = cxBufferSeek(&buf, 3, SEEK_CUR);
720 EXPECT_NE(result, 0);
721 EXPECT_EQ(buf.pos, 3);
722 }
724 TEST_F(BufferSeek, CurInvalidNegative) {
725 ASSERT_EQ(buf.pos, 3);
726 int result = cxBufferSeek(&buf, -4, SEEK_CUR);
727 EXPECT_NE(result, 0);
728 EXPECT_EQ(buf.pos, 3);
729 }
731 TEST_F(BufferSeek, EndZero) {
732 ASSERT_EQ(buf.size, 6);
733 int result = cxBufferSeek(&buf, 0, SEEK_END);
734 // the (past-the-)end position is always invalid
735 EXPECT_NE(result, 0);
736 EXPECT_EQ(buf.pos, 3);
737 }
739 TEST_F(BufferSeek, EndValid) {
740 ASSERT_EQ(buf.size, 6);
741 int result = cxBufferSeek(&buf, -6, SEEK_END);
742 EXPECT_EQ(result, 0);
743 EXPECT_EQ(buf.pos, 0);
744 }
746 TEST_F(BufferSeek, EndInvalid) {
747 ASSERT_EQ(buf.size, 6);
748 int result = cxBufferSeek(&buf, 1, SEEK_END);
749 EXPECT_NE(result, 0);
750 EXPECT_EQ(buf.pos, 3);
751 }
753 TEST_F(BufferSeek, WhenceInvalid) {
754 ASSERT_EQ(buf.size, 6);
755 ASSERT_EQ(buf.pos, 3);
756 int result = cxBufferSeek(&buf, 2, 9000);
757 EXPECT_NE(result, 0);
758 EXPECT_EQ(buf.size, 6);
759 EXPECT_EQ(buf.pos, 3);
760 }
762 class BufferEof : public BufferFixture {
763 };
765 TEST_F(BufferEof, Reached) {
766 buf.pos = buf.size;
767 EXPECT_TRUE(cxBufferEof(&buf));
768 buf.pos = buf.size - 1;
769 ASSERT_FALSE(cxBufferEof(&buf));
770 cxBufferPut(&buf, 'a');
771 EXPECT_TRUE(cxBufferEof(&buf));
772 }
774 TEST_F(BufferEof, NotReached) {
775 buf.pos = buf.size - 1;
776 EXPECT_FALSE(cxBufferEof(&buf));
777 buf.pos = 0;
778 cxBufferWrite("test", 1, 5, &buf);
779 EXPECT_FALSE(cxBufferEof(&buf));
780 }
782 class BufferRead : public ::testing::Test {
783 protected:
784 CxBuffer buf{};
786 void SetUp() override {
787 cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
788 buf.capacity = 8; // artificially reduce capacity to check OOB writes
789 memset(buf.space, 0, 16);
790 memcpy(buf.space, "some data", 9);
791 buf.size = 9;
792 }
794 void TearDown() override {
795 cxBufferDestroy(&buf);
796 }
797 };
799 TEST_F(BufferRead, GetByte) {
800 buf.pos = 2;
801 EXPECT_EQ(cxBufferGet(&buf), 'm');
802 EXPECT_EQ(cxBufferGet(&buf), 'e');
803 EXPECT_EQ(cxBufferGet(&buf), ' ');
804 EXPECT_EQ(cxBufferGet(&buf), 'd');
805 EXPECT_EQ(buf.pos, 6);
806 }
808 TEST_F(BufferRead, GetEof) {
809 buf.pos = buf.size;
810 EXPECT_EQ(cxBufferGet(&buf), EOF);
811 }
813 TEST_F(BufferRead, ReadWithinBounds) {
814 buf.pos = 2;
815 char target[4];
816 auto read = cxBufferRead(&target, 1, 4, &buf);
817 ASSERT_EQ(read, 4);
818 EXPECT_EQ(memcmp(&target, "me d", 4), 0);
819 EXPECT_EQ(buf.pos, 6);
820 }
822 TEST_F(BufferRead, ReadOutOfBounds) {
823 buf.pos = 6;
824 char target[4];
825 auto read = cxBufferRead(&target, 1, 4, &buf);
826 ASSERT_EQ(read, 3);
827 EXPECT_EQ(memcmp(&target, "ata", 3), 0);
828 EXPECT_EQ(buf.pos, 9);
829 }
831 TEST_F(BufferRead, ReadOutOfBoundsMultibyte) {
832 buf.pos = 6;
833 char target[4];
834 target[2] = '\0';
835 auto read = cxBufferRead(&target, 2, 2, &buf);
836 ASSERT_EQ(read, 1);
837 EXPECT_EQ(memcmp(&target, "at\0", 3), 0);
838 EXPECT_EQ(buf.pos, 8);
839 }
841 TEST_F(BufferRead, ReadEof) {
842 buf.pos = 9;
843 char target[4];
844 auto read = cxBufferRead(&target, 1, 1, &buf);
845 ASSERT_EQ(read, 0);
846 EXPECT_EQ(buf.pos, 9);
847 }