--- a/tests/test_buffer.c Sun Jan 05 14:03:30 2025 +0100 +++ b/tests/test_buffer.c Sun Jan 05 18:19:42 2025 +0100 @@ -31,14 +31,6 @@ #include "cx/buffer.h" -static CX_TEST_SUBROUTINE(expect_default_flush_config, CxBuffer *buf) { - CX_TEST_ASSERT(buf->flush_blkmax == 0); - CX_TEST_ASSERT(buf->flush_blksize == 4096); - CX_TEST_ASSERT(buf->flush_threshold == SIZE_MAX); - CX_TEST_ASSERT(buf->flush_func == NULL); - CX_TEST_ASSERT(buf->flush_target == NULL); -} - CX_TEST(test_buffer_init_wrap_space) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); @@ -47,7 +39,7 @@ CxBuffer buf; void *space = cxMalloc(alloc, 16); cxBufferInit(&buf, space, 16, alloc, CX_BUFFER_DEFAULT); - CX_TEST_CALL_SUBROUTINE(expect_default_flush_config, &buf); + CX_TEST_ASSERT(buf.flush == NULL); CX_TEST_ASSERT(buf.space == space); CX_TEST_ASSERT((buf.flags & CX_BUFFER_AUTO_EXTEND) == 0); CX_TEST_ASSERT((buf.flags & CX_BUFFER_FREE_CONTENTS) == 0); @@ -71,7 +63,7 @@ CxBuffer buf; void *space = cxMalloc(alloc, 16); cxBufferInit(&buf, space, 16, alloc, CX_BUFFER_AUTO_EXTEND); - CX_TEST_CALL_SUBROUTINE(expect_default_flush_config, &buf); + CX_TEST_ASSERT(buf.flush == NULL); CX_TEST_ASSERT(buf.space == space); CX_TEST_ASSERT((buf.flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND); CX_TEST_ASSERT((buf.flags & CX_BUFFER_FREE_CONTENTS) == 0); @@ -95,7 +87,7 @@ CxBuffer buf; void *space = cxMalloc(alloc, 16); cxBufferInit(&buf, space, 16, alloc, CX_BUFFER_FREE_CONTENTS); - CX_TEST_CALL_SUBROUTINE(expect_default_flush_config, &buf); + CX_TEST_ASSERT(buf.flush == NULL); CX_TEST_ASSERT(buf.space == space); CX_TEST_ASSERT((buf.flags & CX_BUFFER_AUTO_EXTEND) == 0); CX_TEST_ASSERT((buf.flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS); @@ -117,7 +109,7 @@ CX_TEST_DO { CxBuffer buf; cxBufferInit(&buf, NULL, 8, alloc, CX_BUFFER_DEFAULT); - CX_TEST_CALL_SUBROUTINE(expect_default_flush_config, &buf); + CX_TEST_ASSERT(buf.flush == NULL); CX_TEST_ASSERT(buf.space != NULL); CX_TEST_ASSERT((buf.flags & CX_BUFFER_AUTO_EXTEND) == 0); CX_TEST_ASSERT((buf.flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS); @@ -141,7 +133,7 @@ void *space = cxMalloc(alloc, 16); buf = cxBufferCreate(space, 16, alloc, CX_BUFFER_FREE_CONTENTS); CX_TEST_ASSERT(buf != NULL); - CX_TEST_CALL_SUBROUTINE(expect_default_flush_config, buf); + CX_TEST_ASSERT(buf->flush == NULL); CX_TEST_ASSERT(buf->space == space); CX_TEST_ASSERT((buf->flags & CX_BUFFER_AUTO_EXTEND) == 0); CX_TEST_ASSERT((buf->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS); @@ -642,15 +634,7 @@ cx_attr_unused size_t nitems, CxBuffer *buffer ) { - // simulate limited target drain capacity - static bool full = false; - if (full) { - full = false; - return 0; - } else { - full = true; - return cxBufferWrite(ptr, size, nitems > 2 ? 2 : nitems, buffer); - } + return cxBufferWrite(ptr, size, nitems > 2 ? 2 : nitems, buffer); } CX_TEST(test_buffer_write_size_one_fit) { @@ -1090,15 +1074,18 @@ CX_TEST(test_buffer_write_flush_at_capacity) { CxBuffer buf, target; - cxBufferInit(&target, NULL, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); - cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); - memcpy(buf.space, "prep\0\0\0\0\0\0\0\0\0\0\0\0", 16); - buf.capacity = 8; - buf.size = buf.pos = 4; - buf.flush_target = ⌖ - buf.flush_func = (cx_write_func)cxBufferWrite; - buf.flush_blkmax = 1; + cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&buf, NULL, 8, cxDefaultAllocator, CX_BUFFER_DEFAULT); + memset(buf.space, 0, 8); + cxBufferPutString(&buf, "prep"); CX_TEST_DO { + CxBufferFlushConfig flush; + flush.threshold = 0; + flush.blksize = 32; + flush.blkmax = 1; + flush.target = ⌖ + flush.wfunc = (cx_write_func)cxBufferWrite; + CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush)); size_t written = cxBufferWrite("foo", 1, 3, &buf); CX_TEST_ASSERT(written == 3); CX_TEST_ASSERT(buf.pos == 7); @@ -1107,12 +1094,13 @@ CX_TEST_ASSERT(target.size == 0); written = cxBufferWrite("hello", 1, 5, &buf); CX_TEST_ASSERT(written == 5); - CX_TEST_ASSERT(buf.pos == 0); - CX_TEST_ASSERT(buf.size == 0); + CX_TEST_ASSERT(buf.pos == 5); + CX_TEST_ASSERT(buf.size == 5); CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(target.pos == 12); - CX_TEST_ASSERT(target.size == 12); - CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoohello", 12)); + CX_TEST_ASSERT(target.pos == 7); + CX_TEST_ASSERT(target.size == 7); + CX_TEST_ASSERT(0 == memcmp(buf.space, "hello", 5)); + CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoo", 7)); } cxBufferDestroy(&buf); cxBufferDestroy(&target); @@ -1120,17 +1108,17 @@ CX_TEST(test_buffer_write_flush_at_threshold) { CxBuffer buf, target; - cxBufferInit(&target, NULL, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); - cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); - memcpy(buf.space, "prep\0\0\0\0\0\0\0\0\0\0\0\0", 16); - buf.capacity = 8; - buf.size = buf.pos = 4; - buf.flush_target = ⌖ - buf.flush_func = (cx_write_func)cxBufferWrite; - buf.flush_blkmax = 1; - buf.flush_threshold = 12; - buf.flags |= CX_BUFFER_AUTO_EXTEND; + cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&buf, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + cxBufferPutString(&buf, "prep"); CX_TEST_DO { + CxBufferFlushConfig flush; + flush.threshold = 12; + flush.blksize = 32; + flush.blkmax = 1; + flush.target = ⌖ + flush.wfunc = (cx_write_func)cxBufferWrite; + CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush)); size_t written = cxBufferWrite("foobar", 1, 6, &buf); CX_TEST_ASSERT(written == 6); CX_TEST_ASSERT(buf.pos == 10); @@ -1141,31 +1129,35 @@ CX_TEST_ASSERT(target.size == 0); written = cxBufferWrite("hello", 1, 5, &buf); CX_TEST_ASSERT(written == 5); - CX_TEST_ASSERT(buf.pos == 0); - CX_TEST_ASSERT(buf.size == 0); + CX_TEST_ASSERT(buf.pos == 5); + CX_TEST_ASSERT(buf.size == 5); CX_TEST_ASSERT(buf.capacity <= 12); - CX_TEST_ASSERT(target.pos == 15); - CX_TEST_ASSERT(target.size == 15); - CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoobarhello", 15)); + CX_TEST_ASSERT(target.pos == 10); + CX_TEST_ASSERT(target.size == 10); + CX_TEST_ASSERT(0 == memcmp(buf.space, "hello", 5)); + CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoobar", 10)); } cxBufferDestroy(&buf); cxBufferDestroy(&target); } -CX_TEST(test_buffer_write_flush_rate_limited) { +CX_TEST(test_buffer_write_flush_rate_limited_and_buffer_too_small) { + // the idea is that the target only accepts two bytes and + // then gives up... accepts another two bytes, gives up, etc. + // and at the same time, the written string is too large for + // the buffer (buffer can take 8, we want to write 13) CxBuffer buf, target; - cxBufferInit(&target, NULL, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); - cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); - memcpy(buf.space, "prep\0\0\0\0\0\0\0\0\0\0\0\0", 16); - buf.capacity = 8; - buf.size = buf.pos = 4; - buf.flush_target = ⌖ - buf.flush_blkmax = 1; - // limit the rate of the flush function and the capacity of the target - buf.flush_func = (cx_write_func) mock_write_limited_rate; - target.capacity = 16; - target.flags &= ~CX_BUFFER_AUTO_EXTEND; + cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&buf, NULL, 8, cxDefaultAllocator, CX_BUFFER_DEFAULT); + cxBufferPutString(&buf, "prep"); CX_TEST_DO { + CxBufferFlushConfig flush; + flush.threshold = 0; + flush.blksize = 32; + flush.blkmax = 1; + flush.target = ⌖ + flush.wfunc = (cx_write_func)mock_write_limited_rate; + CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush)); size_t written = cxBufferWrite("foo", 1, 3, &buf); CX_TEST_ASSERT(written == 3); CX_TEST_ASSERT(buf.pos == 7); @@ -1181,13 +1173,49 @@ CX_TEST_ASSERT(0 == memcmp(buf.space, " world!", 7)); CX_TEST_ASSERT(target.pos == 13); CX_TEST_ASSERT(target.size == 13); - CX_TEST_ASSERT(target.capacity == 16); + CX_TEST_ASSERT(target.capacity >= 13); CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoohello,", 13)); } cxBufferDestroy(&buf); cxBufferDestroy(&target); } +CX_TEST(test_buffer_flush) { + CxBuffer buf, target; + cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&buf, NULL, 8, cxDefaultAllocator, CX_BUFFER_DEFAULT); + cxBufferPutString(&buf, "prepare"); + CX_TEST_DO { + CxBufferFlushConfig flush; + flush.threshold = 0; + flush.blksize = 2; + flush.blkmax = 2; + flush.target = ⌖ + flush.wfunc = (cx_write_func)cxBufferWrite; + CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush)); + CX_TEST_ASSERT(buf.size == 7); + buf.pos = 5; + size_t flushed = cxBufferFlush(&buf); + CX_TEST_ASSERT(flushed == flush.blkmax * flush.blksize); + CX_TEST_ASSERT(buf.pos == 1); + CX_TEST_ASSERT(buf.size == 3); + CX_TEST_ASSERT(target.pos == 4); + CX_TEST_ASSERT(target.size == 4); + CX_TEST_ASSERT(0 == memcmp(buf.space, "are", 3)); + CX_TEST_ASSERT(0 == memcmp(target.space, "prep", 4)); + flushed = cxBufferFlush(&buf); + CX_TEST_ASSERT(flushed == 1); + CX_TEST_ASSERT(buf.pos == 0); + CX_TEST_ASSERT(buf.size == 2); + CX_TEST_ASSERT(target.pos == 5); + CX_TEST_ASSERT(target.size == 5); + CX_TEST_ASSERT(0 == memcmp(buf.space, "re", 2)); + CX_TEST_ASSERT(0 == memcmp(target.space, "prepa", 5)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + CX_TEST(test_buffer_get) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); @@ -1348,7 +1376,8 @@ cx_test_register(suite, test_buffer_write_only_overwrite); cx_test_register(suite, test_buffer_write_flush_at_capacity); cx_test_register(suite, test_buffer_write_flush_at_threshold); - cx_test_register(suite, test_buffer_write_flush_rate_limited); + cx_test_register(suite, test_buffer_write_flush_rate_limited_and_buffer_too_small); + cx_test_register(suite, test_buffer_flush); cx_test_register(suite, test_buffer_get); cx_test_register(suite, test_buffer_get_eof); cx_test_register(suite, test_buffer_read);