# HG changeset patch # User Mike Becker # Date 1704398732 -3600 # Node ID db1c8dfe403ab0c07175a11753379b66b67cb690 # Parent 3ca984931e1d39292e2163828fae47e9ac781e3f migrate remaining buffer tests - relates to #342 diff -r 3ca984931e1d -r db1c8dfe403a tests/test_buffer.c --- a/tests/test_buffer.c Wed Jan 03 22:17:40 2024 +0100 +++ b/tests/test_buffer.c Thu Jan 04 21:05:32 2024 +0100 @@ -430,7 +430,7 @@ #define TEST_BUFFER_SHIFT_TEARDOWN(buf) \ cxBufferDestroy(&buf); \ CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); \ - cx_testing_allocator_destroy(&talloc); + cx_testing_allocator_destroy(&talloc) CX_TEST(test_buffer_shift_left_zero) { @@ -585,6 +585,529 @@ } } +static size_t mock_write_limited_rate( + void const *ptr, + size_t size, + __attribute__((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); + } +} + +CX_TEST(test_buffer_write_size_one_fit) { + 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; + const char *data = "test"; + CX_TEST_DO { + size_t written = cxBufferWrite(data, 1, 4, &buf); + CX_TEST_ASSERT(written == 4); + CX_TEST_ASSERT(buf.size == 8); + CX_TEST_ASSERT(buf.pos == 8); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "preptest", 8)); + } + + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_write_size_one_discard) { + 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; + const char *data = "testing"; + CX_TEST_DO { + size_t written = cxBufferWrite(data, 1, 7, &buf); + CX_TEST_ASSERT(written == 4); + CX_TEST_ASSERT(buf.size == 8); + CX_TEST_ASSERT(buf.pos == 8); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "preptest\0", 9)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_write_size_one_extend) { + 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.flags |= CX_BUFFER_AUTO_EXTEND; + const char *data = "testing"; + CX_TEST_DO { + size_t written = cxBufferWrite(data, 1, 7, &buf); + CX_TEST_ASSERT(written == 7); + CX_TEST_ASSERT(buf.size == 11); + CX_TEST_ASSERT(buf.pos == 11); + CX_TEST_ASSERT(buf.capacity >= 11); + CX_TEST_ASSERT(0 == memcmp(buf.space, "preptesting", 11)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_write_multibyte_fit) { + 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; + const char *data = "test"; + CX_TEST_DO { + size_t written = cxBufferWrite(data, 2, 2, &buf); + CX_TEST_ASSERT(written == 2); + CX_TEST_ASSERT(buf.size == 8); + CX_TEST_ASSERT(buf.pos == 8); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "preptest", 8)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_write_multibyte_discard) { + 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 = 4; + buf.pos = 3; + const char *data = "testing"; + CX_TEST_DO { + size_t written = cxBufferWrite(data, 2, 4, &buf); + // remember: whole elements are discarded if they do not fit + CX_TEST_ASSERT(written == 2); + CX_TEST_ASSERT(buf.size == 7); + CX_TEST_ASSERT(buf.pos == 7); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "pretest\0", 8)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_write_multibyte_extend) { + 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 = 4; + buf.pos = 3; + buf.flags |= CX_BUFFER_AUTO_EXTEND; + const char *data = "tester"; + CX_TEST_DO { + size_t written = cxBufferWrite(data, 2, 3, &buf); + // remember: whole elements are discarded if they do not fit + CX_TEST_ASSERT(written == 3); + CX_TEST_ASSERT(buf.size == 9); + CX_TEST_ASSERT(buf.pos == 9); + CX_TEST_ASSERT(buf.capacity >= 9); + CX_TEST_ASSERT(0 == memcmp(buf.space, "pretester", 9)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_put_fit) { + 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; + CX_TEST_DO { + int c = cxBufferPut(&buf, 0x200 | 'a'); + CX_TEST_ASSERT(c == 'a'); + CX_TEST_ASSERT(buf.size == 5); + CX_TEST_ASSERT(buf.pos == 5); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "prepa\0", 6)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_put_discard) { + 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 = 4; + buf.pos = 8; + CX_TEST_DO { + int c = cxBufferPut(&buf, 0x200 | 'a'); + CX_TEST_ASSERT(c == EOF); + CX_TEST_ASSERT(buf.size == 4); + CX_TEST_ASSERT(buf.pos == 8); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "prep\0\0\0\0\0", 9)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_put_extend) { + 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 = 4; + buf.pos = 8; + buf.flags |= CX_BUFFER_AUTO_EXTEND; + CX_TEST_DO { + int c = cxBufferPut(&buf, 0x200 | 'a'); + CX_TEST_ASSERT(c == 'a'); + CX_TEST_ASSERT(buf.size == 9); + CX_TEST_ASSERT(buf.pos == 9); + CX_TEST_ASSERT(buf.capacity >= 9); + CX_TEST_ASSERT(0 == memcmp(buf.space, "prep\0\0\0\0a", 9)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_put_string_fit) { + 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; + const char *data = "test"; + CX_TEST_DO { + size_t written = cxBufferPutString(&buf, data); + CX_TEST_ASSERT(written == 4); + CX_TEST_ASSERT(buf.size == 8); + CX_TEST_ASSERT(buf.pos == 8); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "preptest", 8)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_put_string_discard) { + 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; + const char *data = "testing"; + CX_TEST_DO { + size_t written = cxBufferPutString(&buf, data); + CX_TEST_ASSERT(written == 4); + CX_TEST_ASSERT(buf.size == 8); + CX_TEST_ASSERT(buf.pos == 8); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(0 == memcmp(buf.space, "preptest\0", 9)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_put_string_extend) { + 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.flags |= CX_BUFFER_AUTO_EXTEND; + const char *data = "testing"; + CX_TEST_DO { + size_t written = cxBufferPutString(&buf, data); + CX_TEST_ASSERT(written == 7); + CX_TEST_ASSERT(buf.size == 11); + CX_TEST_ASSERT(buf.pos == 11); + CX_TEST_ASSERT(buf.capacity >= 11); + CX_TEST_ASSERT(0 == memcmp(buf.space, "preptesting", 11)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_write_size_overflow) { + 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; + const char *data = "testing"; + CX_TEST_DO { + size_t written = cxBufferWrite(data, 8, SIZE_MAX / 4, &buf); + CX_TEST_ASSERT(written == 0); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(buf.pos == 4); + CX_TEST_ASSERT(buf.size == 4); + CX_TEST_ASSERT(0 == memcmp(buf.space, "prep\0", 5)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_write_capacity_overflow) { + 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.flags |= CX_BUFFER_AUTO_EXTEND; + const char *data = "testing"; + CX_TEST_DO { + size_t written = cxBufferWrite(data, 1, SIZE_MAX - 2, &buf); + CX_TEST_ASSERT(written == 0); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(buf.pos == 4); + CX_TEST_ASSERT(buf.size == 4); + CX_TEST_ASSERT(0 == memcmp(buf.space, "prep\0", 5)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_write_only_overwrite) { + CxBuffer buf, target; + cxBufferInit(&target, NULL, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); + memcpy(buf.space, "preptest\0\0\0\0\0\0\0\0", 16); + buf.capacity = 8; + buf.pos = 3; + buf.size = 8; + buf.flags |= CX_BUFFER_AUTO_EXTEND; + CX_TEST_DO { + size_t written = cxBufferWrite("XXX", 2, 2, &buf); + CX_TEST_ASSERT(written == 2); + CX_TEST_ASSERT(buf.capacity == 8); + CX_TEST_ASSERT(buf.size == 8); + CX_TEST_ASSERT(buf.pos == 7); + CX_TEST_ASSERT(0 == memcmp(buf.space, "preXXX\0t", 8)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +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; + CX_TEST_DO { + size_t written = cxBufferWrite("foo", 1, 3, &buf); + CX_TEST_ASSERT(written == 3); + CX_TEST_ASSERT(buf.pos == 7); + CX_TEST_ASSERT(buf.size == 7); + CX_TEST_ASSERT(target.pos == 0); + 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.capacity == 8); + CX_TEST_ASSERT(target.pos == 12); + CX_TEST_ASSERT(target.size == 12); + CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoohello", 12)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +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; + CX_TEST_DO { + size_t written = cxBufferWrite("foobar", 1, 6, &buf); + CX_TEST_ASSERT(written == 6); + CX_TEST_ASSERT(buf.pos == 10); + CX_TEST_ASSERT(buf.size == 10); + CX_TEST_ASSERT(buf.capacity >= 10); + CX_TEST_ASSERT(buf.capacity <= 12); + CX_TEST_ASSERT(target.pos == 0); + 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.capacity <= 12); + CX_TEST_ASSERT(target.pos == 15); + CX_TEST_ASSERT(target.size == 15); + CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoobarhello", 15)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_write_flush_rate_limited) { + 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; + CX_TEST_DO { + size_t written = cxBufferWrite("foo", 1, 3, &buf); + CX_TEST_ASSERT(written == 3); + CX_TEST_ASSERT(buf.pos == 7); + CX_TEST_ASSERT(buf.size == 7); + CX_TEST_ASSERT(target.pos == 0); + CX_TEST_ASSERT(target.size == 0); + written = cxBufferWrite("hello, world!", 1, 13, &buf); + // " world!" fits into this buffer, the remaining stuff is flushed out + CX_TEST_ASSERT(written == 13); + CX_TEST_ASSERT(buf.pos == 7); + CX_TEST_ASSERT(buf.size == 7); + CX_TEST_ASSERT(buf.capacity == 8); + 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(0 == memcmp(target.space, "prepfoohello,", 13)); + } + cxBufferDestroy(&buf); + cxBufferDestroy(&target); +} + +CX_TEST(test_buffer_get) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); + memcpy(buf.space, "some data\0\0\0\0\0\0\0", 16); + buf.capacity = 12; + buf.size = 9; + buf.pos = 2; + CX_TEST_DO { + CX_TEST_ASSERT(cxBufferGet(&buf) == 'm'); + CX_TEST_ASSERT(cxBufferGet(&buf) == 'e'); + CX_TEST_ASSERT(cxBufferGet(&buf) == ' '); + CX_TEST_ASSERT(cxBufferGet(&buf) == 'd'); + CX_TEST_ASSERT(buf.pos == 6); + } + cxBufferDestroy(&buf); +} + +CX_TEST(test_buffer_get_eof) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); + memcpy(buf.space, "some data\0\0\0\0\0\0\0", 16); + buf.capacity = 12; + buf.pos = buf.size = 9; + CX_TEST_DO { + CX_TEST_ASSERT(cxBufferGet(&buf) == EOF); + } + cxBufferDestroy(&buf); +} + +CX_TEST(test_buffer_read) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); + memcpy(buf.space, "some data\0\0\0\0\0\0\0", 16); + buf.capacity = 12; + buf.size = 9; + buf.pos = 2; + CX_TEST_DO { + char target[4]; + size_t read = cxBufferRead(&target, 1, 4, &buf); + CX_TEST_ASSERT(read == 4); + CX_TEST_ASSERT(0 == memcmp(&target, "me d", 4)); + CX_TEST_ASSERT(buf.pos == 6); + } + cxBufferDestroy(&buf); +} + +CX_TEST(test_buffer_read_oob) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); + memcpy(buf.space, "some data\0\0\0\0\0\0\0", 16); + buf.capacity = 12; + buf.size = 9; + buf.pos = 6; + CX_TEST_DO { + char target[4]; + size_t read = cxBufferRead(&target, 1, 4, &buf); + CX_TEST_ASSERT(read == 3); + CX_TEST_ASSERT(0 == memcmp(&target, "ata", 3)); + CX_TEST_ASSERT(buf.pos == 9); + } + cxBufferDestroy(&buf); +} + +CX_TEST(test_buffer_read_oob_multibyte) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); + memcpy(buf.space, "some data\0\0\0\0\0\0\0", 16); + buf.capacity = 12; + buf.size = 9; + buf.pos = 6; + CX_TEST_DO { + char target[4]; + target[2] = '\0'; + size_t read = cxBufferRead(&target, 2, 2, &buf); + CX_TEST_ASSERT(read == 1); + CX_TEST_ASSERT(0 == memcmp(&target, "at\0", 3)); + CX_TEST_ASSERT(buf.pos == 8); + } + cxBufferDestroy(&buf); +} + +CX_TEST(test_buffer_read_eof) { + CxBuffer buf; + cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); + memcpy(buf.space, "some data\0\0\0\0\0\0\0", 16); + buf.capacity = 12; + buf.size = buf.pos = 9; + CX_TEST_DO { + char target[4]; + size_t read = cxBufferRead(&target, 1, 1, &buf); + CX_TEST_ASSERT(read == 0); + CX_TEST_ASSERT(buf.pos == 9); + } + cxBufferDestroy(&buf); +} + CxTestSuite *cx_test_suite_buffer(void) { CxTestSuite *suite = cx_test_suite_new("buffer"); @@ -623,6 +1146,30 @@ cx_test_register(suite, test_buffer_shift_right_overshift_discard); cx_test_register(suite, test_buffer_shift_right_overshift_extend); cx_test_register(suite, test_buffer_shift_right_offset_interface); - + cx_test_register(suite, test_buffer_write_size_one_fit); + cx_test_register(suite, test_buffer_write_size_one_discard); + cx_test_register(suite, test_buffer_write_size_one_extend); + cx_test_register(suite, test_buffer_write_multibyte_fit); + cx_test_register(suite, test_buffer_write_multibyte_discard); + cx_test_register(suite, test_buffer_write_multibyte_extend); + cx_test_register(suite, test_buffer_put_fit); + cx_test_register(suite, test_buffer_put_discard); + cx_test_register(suite, test_buffer_put_extend); + cx_test_register(suite, test_buffer_put_string_fit); + cx_test_register(suite, test_buffer_put_string_discard); + cx_test_register(suite, test_buffer_put_string_extend); + cx_test_register(suite, test_buffer_write_size_overflow); + cx_test_register(suite, test_buffer_write_capacity_overflow); + 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_get); + cx_test_register(suite, test_buffer_get_eof); + cx_test_register(suite, test_buffer_read); + cx_test_register(suite, test_buffer_read_oob); + cx_test_register(suite, test_buffer_read_oob_multibyte); + cx_test_register(suite, test_buffer_read_eof); + return suite; } diff -r 3ca984931e1d -r db1c8dfe403a tests/test_buffer.cpp --- a/tests/test_buffer.cpp Wed Jan 03 22:17:40 2024 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,412 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include "cx/buffer.h" - -#include -#include "util_allocator.h" - -class BufferWrite : public ::testing::Test { -protected: - CxBuffer buf{}, target{}; - - void SetUp() override { - cxBufferInit(&target, NULL, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); - cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); - buf.capacity = 8; // artificially reduce capacity to check OOB writes - memset(buf.space, 0, 16); - memcpy(buf.space, "prep", 4); - buf.size = buf.pos = 4; - } - - void TearDown() override { - cxBufferDestroy(&buf); - cxBufferDestroy(&target); - } - - void enableFlushing() { - buf.flush_target = ⌖ - buf.flush_func = reinterpret_cast(cxBufferWrite); - buf.flush_blkmax = 1; - } -}; - -static size_t mock_write_limited_rate( - void const *ptr, - size_t size, - __attribute__((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); - } -} - -TEST_F(BufferWrite, SizeOneFit) { - const char *data = "test"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - size_t written = cxBufferWrite(data, 1, 4, &buf); - CX_TEST_ASSERT(written == 4); - CX_TEST_ASSERT(buf.size == 8); - CX_TEST_ASSERT(buf.pos == 8); - CX_TEST_ASSERT(buf.capacity == 8); - EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); -} - -TEST_F(BufferWrite, SizeOneDiscard) { - const char *data = "testing"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - size_t written = cxBufferWrite(data, 1, 7, &buf); - CX_TEST_ASSERT(written == 4); - CX_TEST_ASSERT(buf.size == 8); - CX_TEST_ASSERT(buf.pos == 8); - CX_TEST_ASSERT(buf.capacity == 8); - EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0); -} - -TEST_F(BufferWrite, SizeOneExtend) { - buf.flags |= CX_BUFFER_AUTO_EXTEND; - const char *data = "testing"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - size_t written = cxBufferWrite(data, 1, 7, &buf); - CX_TEST_ASSERT(written == 7); - CX_TEST_ASSERT(buf.size == 11); - CX_TEST_ASSERT(buf.pos == 11); - EXPECT_GE(buf.capacity, 11); - EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0); -} - -TEST_F(BufferWrite, MultibyteFit) { - const char *data = "test"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - size_t written = cxBufferWrite(data, 2, 2, &buf); - CX_TEST_ASSERT(written == 2); - CX_TEST_ASSERT(buf.size == 8); - CX_TEST_ASSERT(buf.pos == 8); - CX_TEST_ASSERT(buf.capacity == 8); - EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); -} - -TEST_F(BufferWrite, MultibyteDiscard) { - const char *data = "testing"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.size == 4); - buf.pos = 3; - size_t written = cxBufferWrite(data, 2, 4, &buf); - // remember: whole elements are discarded if they do not fit - CX_TEST_ASSERT(written == 2); - CX_TEST_ASSERT(buf.size == 7); - CX_TEST_ASSERT(buf.pos == 7); - CX_TEST_ASSERT(buf.capacity == 8); - EXPECT_EQ(memcmp(buf.space, "pretest\0", 8), 0); -} - -TEST_F(BufferWrite, MultibyteExtend) { - buf.flags |= CX_BUFFER_AUTO_EXTEND; - const char *data = "tester"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.size == 4); - buf.pos = 3; - size_t written = cxBufferWrite(data, 2, 3, &buf); - // remember: whole elements are discarded if they do not fit - CX_TEST_ASSERT(written == 3); - CX_TEST_ASSERT(buf.size == 9); - CX_TEST_ASSERT(buf.pos == 9); - EXPECT_GE(buf.capacity, 9); - EXPECT_EQ(memcmp(buf.space, "pretester", 9), 0); -} - -TEST_F(BufferWrite, PutcWrapperFit) { - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - int c = cxBufferPut(&buf, 0x200 | 'a'); - CX_TEST_ASSERT(c == 'a'); - CX_TEST_ASSERT(buf.size == 5); - CX_TEST_ASSERT(buf.pos == 5); - CX_TEST_ASSERT(buf.capacity == 8); - EXPECT_EQ(memcmp(buf.space, "prepa\0", 6), 0); -} - -TEST_F(BufferWrite, PutcWrapperDiscard) { - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.size == 4); - buf.pos = 8; - int c = cxBufferPut(&buf, 0x200 | 'a'); - CX_TEST_ASSERT(c == EOF); - CX_TEST_ASSERT(buf.size == 4); - CX_TEST_ASSERT(buf.pos == 8); - CX_TEST_ASSERT(buf.capacity == 8); - EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0\0", 9), 0); -} - -TEST_F(BufferWrite, PutcWrapperExtend) { - buf.flags |= CX_BUFFER_AUTO_EXTEND; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.size == 4); - buf.pos = 8; - int c = cxBufferPut(&buf, 0x200 | 'a'); - CX_TEST_ASSERT(c == 'a'); - CX_TEST_ASSERT(buf.size == 9); - CX_TEST_ASSERT(buf.pos == 9); - EXPECT_GE(buf.capacity, 9); - EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0a", 9), 0); -} - -TEST_F(BufferWrite, PutStringWrapperFit) { - const char *data = "test"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - size_t written = cxBufferPutString(&buf, data); - CX_TEST_ASSERT(written == 4); - CX_TEST_ASSERT(buf.size == 8); - CX_TEST_ASSERT(buf.pos == 8); - CX_TEST_ASSERT(buf.capacity == 8); - EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); -} - -TEST_F(BufferWrite, PutStringWrapperDiscard) { - const char *data = "testing"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - size_t written = cxBufferPutString(&buf, data); - CX_TEST_ASSERT(written == 4); - CX_TEST_ASSERT(buf.size == 8); - CX_TEST_ASSERT(buf.pos == 8); - CX_TEST_ASSERT(buf.capacity == 8); - EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0); -} - -TEST_F(BufferWrite, PutStringWrapperExtend) { - buf.flags |= CX_BUFFER_AUTO_EXTEND; - const char *data = "testing"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - size_t written = cxBufferPutString(&buf, data); - CX_TEST_ASSERT(written == 7); - CX_TEST_ASSERT(buf.size == 11); - CX_TEST_ASSERT(buf.pos == 11); - EXPECT_GE(buf.capacity, 11); - EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0); -} - -TEST_F(BufferWrite, MultOverflow) { - const char *data = "testing"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - size_t written = cxBufferWrite(data, 8, SIZE_MAX / 4, &buf); - CX_TEST_ASSERT(written == 0); - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0); -} - -TEST_F(BufferWrite, MaxCapaOverflow) { - buf.flags |= CX_BUFFER_AUTO_EXTEND; - const char *data = "testing"; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - size_t written = cxBufferWrite(data, 1, SIZE_MAX - 2, &buf); - CX_TEST_ASSERT(written == 0); - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - CX_TEST_ASSERT(buf.size == 4); - EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0); -} - -TEST_F(BufferWrite, OnlyOverwrite) { - buf.flags |= CX_BUFFER_AUTO_EXTEND; - CX_TEST_ASSERT(buf.capacity == 8); - memcpy(buf.space, "preptest", 8); - buf.pos = 3; - buf.size = 8; - size_t written = cxBufferWrite("XXX", 2, 2, &buf); - CX_TEST_ASSERT(written == 2); - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.size == 8); - CX_TEST_ASSERT(buf.pos == 7); - EXPECT_EQ(memcmp(buf.space, "preXXX\0t", 8), 0); -} - -TEST_F(BufferWrite, FlushAtCapacity) { - enableFlushing(); - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - size_t written = cxBufferWrite("foo", 1, 3, &buf); - CX_TEST_ASSERT(written == 3); - CX_TEST_ASSERT(buf.pos == 7); - CX_TEST_ASSERT(buf.size == 7); - CX_TEST_ASSERT(target.pos == 0); - 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.capacity == 8); - CX_TEST_ASSERT(target.pos == 12); - CX_TEST_ASSERT(target.size == 12); - EXPECT_EQ(memcmp(target.space, "prepfoohello", 12), 0); -} - -TEST_F(BufferWrite, FlushAtThreshold) { - enableFlushing(); - buf.flush_threshold = 12; - buf.flags |= CX_BUFFER_AUTO_EXTEND; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - size_t written = cxBufferWrite("foobar", 1, 6, &buf); - CX_TEST_ASSERT(written == 6); - CX_TEST_ASSERT(buf.pos == 10); - CX_TEST_ASSERT(buf.size == 10); - ASSERT_GE(buf.capacity, 10); - ASSERT_LE(buf.capacity, 12); - CX_TEST_ASSERT(target.pos == 0); - 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); - EXPECT_LE(buf.capacity, 12); - CX_TEST_ASSERT(target.pos == 15); - CX_TEST_ASSERT(target.size == 15); - EXPECT_EQ(memcmp(target.space, "prepfoobarhello", 15), 0); -} - -TEST_F(BufferWrite, FlushRateLimited) { - enableFlushing(); - // limit the rate of the flush function and the capacity of the target - target.capacity = 16; - target.flags &= ~CX_BUFFER_AUTO_EXTEND; - buf.flush_func = (cx_write_func) mock_write_limited_rate; - CX_TEST_ASSERT(buf.capacity == 8); - CX_TEST_ASSERT(buf.pos == 4); - size_t written = cxBufferWrite("foo", 1, 3, &buf); - CX_TEST_ASSERT(written == 3); - CX_TEST_ASSERT(buf.pos == 7); - CX_TEST_ASSERT(buf.size == 7); - CX_TEST_ASSERT(target.pos == 0); - CX_TEST_ASSERT(target.size == 0); - written = cxBufferWrite("hello, world!", 1, 13, &buf); - // " world!" fits into this buffer, the remaining stuff is flushed out - CX_TEST_ASSERT(written == 13); - CX_TEST_ASSERT(buf.pos == 7); - CX_TEST_ASSERT(buf.size == 7); - CX_TEST_ASSERT(buf.capacity == 8); - EXPECT_EQ(memcmp(buf.space, " world!", 7), 0); - CX_TEST_ASSERT(target.pos == 13); - CX_TEST_ASSERT(target.size == 13); - CX_TEST_ASSERT(target.capacity == 16); - EXPECT_EQ(memcmp(target.space, "prepfoohello,", 13), 0); -} - -class BufferRead : public ::testing::Test { -protected: - CxBuffer buf{}; - - void SetUp() override { - cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); - buf.capacity = 8; // artificially reduce capacity to check OOB writes - memset(buf.space, 0, 16); - memcpy(buf.space, "some data", 9); - buf.size = 9; - } - - void TearDown() override { - cxBufferDestroy(&buf); - } -}; - -TEST_F(BufferRead, GetByte) { - buf.pos = 2; - EXPECT_EQ(cxBufferGet(&buf), 'm'); - EXPECT_EQ(cxBufferGet(&buf), 'e'); - EXPECT_EQ(cxBufferGet(&buf), ' '); - EXPECT_EQ(cxBufferGet(&buf), 'd'); - CX_TEST_ASSERT(buf.pos == 6); -} - -TEST_F(BufferRead, GetEof) { - buf.pos = buf.size; - EXPECT_EQ(cxBufferGet(&buf), EOF); -} - -TEST_F(BufferRead, ReadWithinBounds) { - buf.pos = 2; - char target[4]; - auto read = cxBufferRead(&target, 1, 4, &buf); - CX_TEST_ASSERT(read == 4); - EXPECT_EQ(memcmp(&target, "me d", 4), 0); - CX_TEST_ASSERT(buf.pos == 6); -} - -TEST_F(BufferRead, ReadOutOfBounds) { - buf.pos = 6; - char target[4]; - auto read = cxBufferRead(&target, 1, 4, &buf); - CX_TEST_ASSERT(read == 3); - EXPECT_EQ(memcmp(&target, "ata", 3), 0); - CX_TEST_ASSERT(buf.pos == 9); -} - -TEST_F(BufferRead, ReadOutOfBoundsMultibyte) { - buf.pos = 6; - char target[4]; - target[2] = '\0'; - auto read = cxBufferRead(&target, 2, 2, &buf); - CX_TEST_ASSERT(read == 1); - EXPECT_EQ(memcmp(&target, "at\0", 3), 0); - CX_TEST_ASSERT(buf.pos == 8); -} - -TEST_F(BufferRead, ReadEof) { - buf.pos = 9; - char target[4]; - auto read = cxBufferRead(&target, 1, 1, &buf); - CX_TEST_ASSERT(read == 0); - CX_TEST_ASSERT(buf.pos == 9); -}