migrate remaining buffer tests - relates to #342

Thu, 04 Jan 2024 21:05:32 +0100

author
Mike Becker <universe@uap-core.de>
date
Thu, 04 Jan 2024 21:05:32 +0100
changeset 793
db1c8dfe403a
parent 792
3ca984931e1d
child 794
23c6e3e846a8

migrate remaining buffer tests - relates to #342

tests/test_buffer.c file | annotate | diff | comparison | revisions
tests/test_buffer.cpp file | annotate | diff | comparison | revisions
--- 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 = &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 = &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 = &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;
 }
--- 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 <gtest/gtest.h>
-#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 = &target;
-        buf.flush_func = reinterpret_cast<cx_write_func>(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);
-}

mercurial