Fri, 23 May 2025 12:44:24 +0200
make test-compile depend on both static and shared
the shared lib is not needed for the tests,
but when run with coverage, gcov will be confused
when outdated line information is available from
a previous shared build
/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2023 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/test.h" #include "util_allocator.h" #include "cx/buffer.h" CX_TEST(test_buffer_init_wrap_space) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); CxAllocator *alloc = &talloc.base; CX_TEST_DO { CxBuffer buf; void *space = cxMalloc(alloc, 16); cxBufferInit(&buf, space, 16, alloc, CX_BUFFER_DEFAULT); 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); CX_TEST_ASSERT(buf.pos == 0); CX_TEST_ASSERT(buf.size == 0); CX_TEST_ASSERT(buf.capacity == 16); CX_TEST_ASSERT(buf.allocator == alloc); cxBufferDestroy(&buf); CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc)); cxFree(alloc, space); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); } CX_TEST(test_buffer_init_wrap_space_auto_extend) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); CxAllocator *alloc = &talloc.base; CX_TEST_DO { CxBuffer buf; void *space = cxMalloc(alloc, 16); cxBufferInit(&buf, space, 16, alloc, CX_BUFFER_AUTO_EXTEND); 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); CX_TEST_ASSERT(buf.pos == 0); CX_TEST_ASSERT(buf.size == 0); CX_TEST_ASSERT(buf.capacity == 16); CX_TEST_ASSERT(buf.allocator == alloc); cxBufferDestroy(&buf); CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc)); cxFree(alloc, space); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); } CX_TEST(test_buffer_init_wrap_space_auto_free) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); CxAllocator *alloc = &talloc.base; CX_TEST_DO { CxBuffer buf; void *space = cxMalloc(alloc, 16); cxBufferInit(&buf, space, 16, alloc, CX_BUFFER_FREE_CONTENTS); 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); CX_TEST_ASSERT(buf.pos == 0); CX_TEST_ASSERT(buf.size == 0); CX_TEST_ASSERT(buf.capacity == 16); CX_TEST_ASSERT(buf.allocator == alloc); CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc)); cxBufferDestroy(&buf); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); } CX_TEST(test_buffer_init_fresh_space) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); CxAllocator *alloc = &talloc.base; CX_TEST_DO { CxBuffer buf; cxBufferInit(&buf, NULL, 8, alloc, CX_BUFFER_DEFAULT); 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); CX_TEST_ASSERT(buf.pos == 0); CX_TEST_ASSERT(buf.size == 0); CX_TEST_ASSERT(buf.capacity == 8); CX_TEST_ASSERT(buf.allocator == alloc); CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc)); // space is still allocated cxBufferDestroy(&buf); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); } CX_TEST(test_buffer_init_on_heap) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); CxAllocator *alloc = &talloc.base; CX_TEST_DO { CxBuffer *buf; void *space = cxMalloc(alloc, 16); buf = cxBufferCreate(space, 16, alloc, CX_BUFFER_FREE_CONTENTS); CX_TEST_ASSERT(buf != NULL); 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); CX_TEST_ASSERT(buf->pos == 0); CX_TEST_ASSERT(buf->size == 0); CX_TEST_ASSERT(buf->capacity == 16); CX_TEST_ASSERT(buf->allocator == alloc); cxBufferFree(buf); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); } CX_TEST(test_buffer_minimum_capacity_sufficient) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); CxAllocator *alloc = &talloc.base; CX_TEST_DO { void *space = cxMalloc(alloc, 8); CxBuffer buf; cxBufferInit(&buf, space, 8, alloc, CX_BUFFER_FREE_CONTENTS); memcpy(space, "Testing", 8); buf.size = 8; cxBufferMinimumCapacity(&buf, 6); CX_TEST_ASSERT(buf.capacity == 8); CX_TEST_ASSERT(buf.size == 8); CX_TEST_ASSERT(memcmp(buf.space, "Testing", 8) == 0); cxBufferDestroy(&buf); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); } CX_TEST(test_buffer_minimum_capacity_extend) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); CxAllocator *alloc = &talloc.base; CX_TEST_DO { void *space = cxMalloc(alloc, 8); CxBuffer buf; cxBufferInit(&buf, space, 8, alloc, CX_BUFFER_FREE_CONTENTS); // NO auto extend! memcpy(space, "Testing", 8); buf.size = 8; cxBufferMinimumCapacity(&buf, 16); CX_TEST_ASSERT(buf.capacity == 16); CX_TEST_ASSERT(buf.size == 8); CX_TEST_ASSERT(memcmp(buf.space, "Testing", 8) == 0); cxBufferDestroy(&buf); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); } CX_TEST(test_buffer_shrink) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); CxAllocator *alloc = &talloc.base; CX_TEST_DO { CxBuffer buf; cxBufferInit(&buf, NULL, 16, alloc, CX_BUFFER_FREE_CONTENTS); cxBufferPutString(&buf, "Testing"); cxBufferTerminate(&buf); CX_TEST_ASSERT(buf.capacity == 16); CX_TEST_ASSERT(buf.size == 7); cxBufferShrink(&buf, 4); CX_TEST_ASSERT(buf.capacity == 11); CX_TEST_ASSERT(buf.size == 7); CX_TEST_ASSERT(memcmp(buf.space, "Testing", 8) == 0); cxBufferDestroy(&buf); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); } CX_TEST(test_buffer_shrink_copy_on_write) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); CxAllocator *alloc = &talloc.base; CX_TEST_DO { CxBuffer buf; const char* space = "Testing"; cxBufferInit(&buf, (void*)space, 16, alloc, CX_BUFFER_COPY_ON_WRITE); buf.size = 8; CX_TEST_ASSERT(buf.capacity == 16); cxBufferShrink(&buf, 4); CX_TEST_ASSERT(buf.capacity == 16); CX_TEST_ASSERT(buf.size == 8); CX_TEST_ASSERT(memcmp(buf.space, "Testing", 8) == 0); CX_TEST_ASSERT(talloc.alloc_total == 0); cxBufferDestroy(&buf); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); } CX_TEST(test_buffer_shrink_copy_on_extend) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); CxAllocator *alloc = &talloc.base; CX_TEST_DO { CxBuffer buf; char space[16] = "Testing"; cxBufferInit(&buf, space, 16, alloc, CX_BUFFER_COPY_ON_EXTEND); buf.size = 8; CX_TEST_ASSERT(buf.capacity == 16); cxBufferShrink(&buf, 4); CX_TEST_ASSERT(buf.capacity == 16); CX_TEST_ASSERT(buf.size == 8); CX_TEST_ASSERT(memcmp(buf.space, "Testing", 8) == 0); CX_TEST_ASSERT(talloc.alloc_total == 0); cxBufferDestroy(&buf); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); } cx_testing_allocator_destroy(&talloc); } CX_TEST(test_buffer_clear) { char space[16]; strcpy(space, "clear test"); CxBuffer buf; cxBufferInit(&buf, space, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); CX_TEST_DO { CX_TEST_ASSERT(buf.size == 0); // only clear the used part of the buffer cxBufferClear(&buf); CX_TEST_ASSERT(0 == memcmp(space, "clear test", 10)); buf.size = 5; buf.pos = 3; cxBufferClear(&buf); CX_TEST_ASSERT(0 == memcmp(space, "\0\0\0\0\0 test", 10)); CX_TEST_ASSERT(buf.size == 0); CX_TEST_ASSERT(buf.pos == 0); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_clear_copy_on_write) { char space[16]; strcpy(space, "clear test"); CxBuffer buf; cxBufferInit(&buf, space, 16, cxDefaultAllocator, CX_BUFFER_COPY_ON_WRITE); CX_TEST_DO { buf.size = 5; buf.pos = 3; cxBufferClear(&buf); CX_TEST_ASSERT(0 == memcmp(space, "clear test", 10)); CX_TEST_ASSERT(buf.size == 0); CX_TEST_ASSERT(buf.pos == 0); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_reset) { char space[16]; strcpy(space, "reset test"); CxBuffer buf; cxBufferInit(&buf, space, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); CX_TEST_DO { buf.size = 5; buf.pos = 3; cxBufferReset(&buf); CX_TEST_ASSERT(0 == memcmp(space, "reset test", 10)); CX_TEST_ASSERT(buf.size == 0); CX_TEST_ASSERT(buf.pos == 0); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_set_zero) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, 0, SEEK_SET); CX_TEST_ASSERT(result == 0); CX_TEST_ASSERT(buf.pos == 0); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_set_valid) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, 5, SEEK_SET); CX_TEST_ASSERT(result == 0); CX_TEST_ASSERT(buf.pos == 5); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_set_invalid) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, 7, SEEK_SET); CX_TEST_ASSERT(result != 0); CX_TEST_ASSERT(buf.pos == 3); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_cur_zero) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, 0, SEEK_CUR); CX_TEST_ASSERT(result == 0); CX_TEST_ASSERT(buf.pos == 3); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_cur_valid_positive) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, 2, SEEK_CUR); CX_TEST_ASSERT(result == 0); CX_TEST_ASSERT(buf.pos == 5); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_cur_valid_negative) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, -3, SEEK_CUR); CX_TEST_ASSERT(result == 0); CX_TEST_ASSERT(buf.pos == 0); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_cur_invalid_positive) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, 4, SEEK_CUR); CX_TEST_ASSERT(result != 0); CX_TEST_ASSERT(buf.pos == 3); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_cur_invalid_negative) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, -4, SEEK_CUR); CX_TEST_ASSERT(result != 0); CX_TEST_ASSERT(buf.pos == 3); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_end_zero) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, 0, SEEK_END); CX_TEST_ASSERT(result == 0); CX_TEST_ASSERT(buf.pos == 6); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_end_valid) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, -6, SEEK_END); CX_TEST_ASSERT(result == 0); CX_TEST_ASSERT(buf.pos == 0); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_end_invalid) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, 1, SEEK_END); CX_TEST_ASSERT(result != 0); CX_TEST_ASSERT(buf.pos == 3); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_seek_whence_invalid) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; buf.pos = 3; CX_TEST_DO { int result = cxBufferSeek(&buf, 2, 9000); CX_TEST_ASSERT(result != 0); CX_TEST_ASSERT(buf.size == 6); CX_TEST_ASSERT(buf.pos == 3); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_eof_reached) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = buf.pos = 3; CX_TEST_DO { CX_TEST_ASSERT(cxBufferEof(&buf)); buf.pos = buf.size - 1; CX_TEST_ASSERT(!cxBufferEof(&buf)); cxBufferPut(&buf, 'a'); CX_TEST_ASSERT(cxBufferEof(&buf)); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_eof_not_reached) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); buf.size = 6; CX_TEST_DO { buf.pos = buf.size - 1; CX_TEST_ASSERT(!cxBufferEof(&buf)); buf.pos = 0; cxBufferWrite("test", 1, 5, &buf); CX_TEST_ASSERT(!cxBufferEof(&buf)); } cxBufferDestroy(&buf); } #define TEST_BUFFER_SHIFT_SETUP(buf) \ CxTestingAllocator talloc; \ cx_testing_allocator_init(&talloc); \ CxAllocator *alloc = &talloc.base; \ CxBuffer buf; \ cxBufferInit(&buf, NULL, 16, alloc, CX_BUFFER_DEFAULT); \ memcpy(buf.space, "test____XXXXXXXX", 16); \ buf.capacity = 8; \ buf.pos = 4; \ buf.size = 4 #define TEST_BUFFER_SHIFT_TEARDOWN(buf) \ cxBufferDestroy(&buf); \ CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); \ cx_testing_allocator_destroy(&talloc) CX_TEST(test_buffer_shift_left_zero) { TEST_BUFFER_SHIFT_SETUP(buf); CX_TEST_DO { int ret = cxBufferShiftLeft(&buf, 0); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 4); CX_TEST_ASSERT(buf.size == 4); CX_TEST_ASSERT(memcmp(buf.space, "test____XXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_left_zero_offset_interface) { TEST_BUFFER_SHIFT_SETUP(buf); CX_TEST_DO { int ret = cxBufferShift(&buf, -0); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 4); CX_TEST_ASSERT(buf.size == 4); CX_TEST_ASSERT(memcmp(buf.space, "test____XXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_left_standard) { TEST_BUFFER_SHIFT_SETUP(buf); CX_TEST_DO { int ret = cxBufferShiftLeft(&buf, 2); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 2); CX_TEST_ASSERT(buf.size == 2); CX_TEST_ASSERT(memcmp(buf.space, "stst____XXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_left_overshift) { TEST_BUFFER_SHIFT_SETUP(buf); CX_TEST_DO { int ret = cxBufferShiftLeft(&buf, 6); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 0); CX_TEST_ASSERT(buf.size == 0); CX_TEST_ASSERT(memcmp(buf.space, "test____XXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_left_overshift_pos_only) { TEST_BUFFER_SHIFT_SETUP(buf); buf.pos = 2; CX_TEST_DO { int ret = cxBufferShiftLeft(&buf, 3); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 0); CX_TEST_ASSERT(buf.size == 1); CX_TEST_ASSERT(memcmp(buf.space, "test____XXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_left_offset_interface) { TEST_BUFFER_SHIFT_SETUP(buf); buf.pos = 3; CX_TEST_DO { int ret = cxBufferShift(&buf, -2); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 1); CX_TEST_ASSERT(buf.size == 2); CX_TEST_ASSERT(memcmp(buf.space, "stst____XXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_left_copy_on_write) { TEST_BUFFER_SHIFT_SETUP(buf); buf.flags |= CX_BUFFER_COPY_ON_WRITE; char *original = buf.space; CX_TEST_DO { int ret = cxBufferShiftLeft(&buf, 2); CX_TEST_ASSERT(0 == (buf.flags & CX_BUFFER_COPY_ON_WRITE)); CX_TEST_ASSERT(0 != (buf.flags & CX_BUFFER_FREE_CONTENTS)); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 2); CX_TEST_ASSERT(buf.size == 2); CX_TEST_ASSERT(memcmp(original, "test____XXXXXXXX", 16) == 0); CX_TEST_ASSERT(memcmp(buf.space, "st", 2) == 0); cxFree(buf.allocator, original); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_right_zero) { TEST_BUFFER_SHIFT_SETUP(buf); CX_TEST_DO { int ret = cxBufferShiftRight(&buf, 0); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 4); CX_TEST_ASSERT(buf.size == 4); CX_TEST_ASSERT(memcmp(buf.space, "test____XXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_right_zero_offset_interface) { TEST_BUFFER_SHIFT_SETUP(buf); CX_TEST_DO { int ret = cxBufferShift(&buf, +0); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 4); CX_TEST_ASSERT(buf.size == 4); CX_TEST_ASSERT(memcmp(buf.space, "test____XXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_right_standard) { TEST_BUFFER_SHIFT_SETUP(buf); CX_TEST_DO { int ret = cxBufferShiftRight(&buf, 3); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 7); CX_TEST_ASSERT(buf.size == 7); CX_TEST_ASSERT(memcmp(buf.space, "testest_XXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_right_overshift_discard) { TEST_BUFFER_SHIFT_SETUP(buf); CX_TEST_DO { int ret = cxBufferShiftRight(&buf, 6); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 8); CX_TEST_ASSERT(buf.size == 8); CX_TEST_ASSERT(buf.capacity == 8); CX_TEST_ASSERT(memcmp(buf.space, "test__teXXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_right_overshift_extend) { TEST_BUFFER_SHIFT_SETUP(buf); buf.flags |= CX_BUFFER_AUTO_EXTEND; CX_TEST_DO { int ret = cxBufferShiftRight(&buf, 6); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 10); CX_TEST_ASSERT(buf.size == 10); CX_TEST_ASSERT(buf.capacity >= 10); // cannot assert more than 10 bytes because // the buffer was required to reallocate the space CX_TEST_ASSERT(memcmp(buf.space, "test__test", 10) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_right_offset_interface) { TEST_BUFFER_SHIFT_SETUP(buf); buf.pos = 3; CX_TEST_DO { int ret = cxBufferShift(&buf, 2); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 5); CX_TEST_ASSERT(buf.size == 6); CX_TEST_ASSERT(memcmp(buf.space, "tetest__XXXXXXXX", 16) == 0); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } CX_TEST(test_buffer_shift_right_copy_on_write) { TEST_BUFFER_SHIFT_SETUP(buf); buf.flags |= CX_BUFFER_COPY_ON_WRITE; char *original = buf.space; CX_TEST_DO { int ret = cxBufferShiftRight(&buf, 3); CX_TEST_ASSERT(0 == (buf.flags & CX_BUFFER_COPY_ON_WRITE)); CX_TEST_ASSERT(0 != (buf.flags & CX_BUFFER_FREE_CONTENTS)); CX_TEST_ASSERT(ret == 0); CX_TEST_ASSERT(buf.pos == 7); CX_TEST_ASSERT(buf.size == 7); CX_TEST_ASSERT(memcmp(original, "test____XXXXXXXX", 16) == 0); CX_TEST_ASSERT(memcmp(buf.space, "testest", 7) == 0); cxFree(buf.allocator, original); TEST_BUFFER_SHIFT_TEARDOWN(buf); } } static size_t mock_write_limited_rate( const void *ptr, size_t size, cx_attr_unused size_t nitems, CxBuffer *buffer ) { return cxBufferWrite(ptr, size, nitems > 2 ? 2 : nitems, buffer); } CX_TEST(test_buffer_write_size_one_fit) { CxBuffer buf; 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); } CX_TEST(test_buffer_write_size_one_discard) { CxBuffer buf; 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); } CX_TEST(test_buffer_write_size_one_extend) { CxBuffer buf; 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); } CX_TEST(test_buffer_write_copy_on_write) { CxBuffer buf; char original[16] = "preparedXXXXXXX"; cxBufferInit(&buf, original, 16, cxDefaultAllocator, CX_BUFFER_COPY_ON_WRITE); buf.capacity = 8; buf.size = 8; buf.pos = 0; 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 == 8); CX_TEST_ASSERT(buf.pos == 7); CX_TEST_ASSERT(buf.capacity == 8); CX_TEST_ASSERT(0 == memcmp(buf.space, "testingd", 8)); CX_TEST_ASSERT(0 == memcmp(original, "preparedXXXXXXX\0", 16)); CX_TEST_ASSERT(0 == (buf.flags & CX_BUFFER_COPY_ON_WRITE)); CX_TEST_ASSERT(0 != (buf.flags & CX_BUFFER_FREE_CONTENTS)); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_write_multibyte_fit) { CxBuffer buf; 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); } CX_TEST(test_buffer_write_multibyte_discard) { CxBuffer buf; 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); } CX_TEST(test_buffer_write_multibyte_extend) { CxBuffer buf; 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); } CX_TEST(test_buffer_append) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); memcpy(buf.space, "prepXXXX\0\0\0\0\0\0\0\0", 16); buf.capacity = 8; buf.size = 6; buf.pos = 4; CX_TEST_DO { size_t written = cxBufferAppend("testing", 1, 7, &buf); CX_TEST_ASSERT(written == 7); CX_TEST_ASSERT(buf.size == 13); CX_TEST_ASSERT(buf.pos == 4); CX_TEST_ASSERT(buf.capacity >= 13); CX_TEST_ASSERT(0 == memcmp(buf.space, "prepXXtesting", 13)); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_append_flush) { CxBuffer buf, target; cxBufferInit(&buf, NULL, 8, cxDefaultAllocator, CX_BUFFER_DEFAULT); cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); memcpy(buf.space, "prepXXXX", 8); buf.capacity = 8; buf.size = 6; buf.pos = 4; CxBufferFlushConfig flush; flush.threshold = 0; flush.blksize = 4; flush.blkmax = 1; flush.target = ⌖ flush.wfunc = cxBufferWriteFunc; cxBufferEnableFlushing(&buf, flush); CX_TEST_DO{ size_t written = cxBufferAppend("testing", 1, 7, &buf); CX_TEST_ASSERT(written == 7); CX_TEST_ASSERT(buf.size == 7); CX_TEST_ASSERTM(buf.pos == 0, "position not correctly reset"); CX_TEST_ASSERT(buf.capacity == 8); CX_TEST_ASSERT(target.size == 6); CX_TEST_ASSERT(target.pos == 6); CX_TEST_ASSERT(0 == memcmp(buf.space, "testing", 7)); CX_TEST_ASSERT(0 == memcmp(target.space, "prepXX", 6)); // second test - position only shifted by one block buf.pos = 6; written = cxBufferAppend("foo", 1, 3, &buf); CX_TEST_ASSERT(written == 3); CX_TEST_ASSERT(buf.size == 6); CX_TEST_ASSERTM(buf.pos == 2, "position not correctly adjusted"); CX_TEST_ASSERT(buf.capacity == 8); CX_TEST_ASSERT(target.size == 10); CX_TEST_ASSERT(target.pos == 10); CX_TEST_ASSERT(0 == memcmp(buf.space, "ingfoo", 6)); CX_TEST_ASSERT(0 == memcmp(target.space, "prepXXtest", 10)); } cxBufferDestroy(&buf); cxBufferDestroy(&target); } CX_TEST(test_buffer_put_fit) { CxBuffer buf; 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); } CX_TEST(test_buffer_put_discard) { CxBuffer buf; 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); } CX_TEST(test_buffer_put_extend) { CxBuffer buf; 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); } CX_TEST(test_buffer_put_copy_on_write) { CxBuffer buf; char original[16] = "preparedXXXXXXX"; cxBufferInit(&buf, original, 16, cxDefaultAllocator, CX_BUFFER_COPY_ON_WRITE); buf.capacity = 8; buf.size = 8; buf.pos = 8; CX_TEST_DO { int c = cxBufferPut(&buf, 0x200 | 'a'); CX_TEST_ASSERT(c == EOF); 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, "prepared", 8)); // discarded, no write happend! CX_TEST_ASSERT(original == buf.space); CX_TEST_ASSERT(0 != (buf.flags & CX_BUFFER_COPY_ON_WRITE)); CX_TEST_ASSERT(0 == (buf.flags & CX_BUFFER_FREE_CONTENTS)); // now actually write somewhere buf.pos = 2; c = cxBufferPut(&buf, 0x200 | 'a'); CX_TEST_ASSERT(c == 'a'); CX_TEST_ASSERT(buf.size == 8); CX_TEST_ASSERT(buf.pos == 3); CX_TEST_ASSERT(buf.capacity == 8); CX_TEST_ASSERT(0 == memcmp(buf.space, "prapared", 8)); CX_TEST_ASSERT(original != buf.space); CX_TEST_ASSERT(0 == memcmp(original, "preparedXXXXXXX\0", 16)); CX_TEST_ASSERT(0 == (buf.flags & CX_BUFFER_COPY_ON_WRITE)); CX_TEST_ASSERT(0 != (buf.flags & CX_BUFFER_FREE_CONTENTS)); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_put_string_fit) { CxBuffer buf; 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); } CX_TEST(test_buffer_put_string_discard) { CxBuffer buf; 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); } CX_TEST(test_buffer_put_string_extend) { CxBuffer buf; 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); } CX_TEST(test_buffer_put_string_copy_on_extend) { CxTestingAllocator talloc; cx_testing_allocator_init(&talloc); const CxAllocator *alloc = &talloc.base; CxBuffer buf; char original[16] = "preparedXXXXXXX"; CX_TEST_DO { cxBufferInit(&buf, original, 16, alloc, CX_BUFFER_COPY_ON_EXTEND); buf.capacity = 8; buf.size = buf.pos = 4; size_t written = cxBufferPutString(&buf, "test"); 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)); CX_TEST_ASSERT(original == buf.space); written = cxBufferPutString(&buf, "ing"); CX_TEST_ASSERT(written == 3); 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)); CX_TEST_ASSERT(original != buf.space); CX_TEST_ASSERT(0 == memcmp(original, "preptestXXXXXXX\0", 16)); CX_TEST_ASSERT(!cx_testing_allocator_verify(&talloc)); cxBufferDestroy(&buf); CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc)); cx_testing_allocator_destroy(&talloc); } } CX_TEST(test_buffer_put_string_copy_on_write) { CxBuffer buf; char original[16] = "preparedXXXXXXX"; cxBufferInit(&buf, original, 16, cxDefaultAllocator, CX_BUFFER_COPY_ON_WRITE); buf.capacity = 8; buf.size = 8; 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)); CX_TEST_ASSERT(original != buf.space); CX_TEST_ASSERT(0 == memcmp(original, "preparedXXXXXXX\0", 16)); CX_TEST_ASSERT(0 == (buf.flags & CX_BUFFER_COPY_ON_WRITE)); CX_TEST_ASSERT(0 != (buf.flags & CX_BUFFER_FREE_CONTENTS)); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_terminate) { CxBuffer buf; cxBufferInit(&buf, NULL, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); memcpy(buf.space, "prepAAAAAA\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(0 != cxBufferTerminate(&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, "preptestAA", 10)); buf.flags |= CX_BUFFER_AUTO_EXTEND; CX_TEST_ASSERT(0 == cxBufferTerminate(&buf)); CX_TEST_ASSERT(buf.size == 8); CX_TEST_ASSERT(buf.pos == 9); CX_TEST_ASSERT(buf.capacity > 8); CX_TEST_ASSERT(0 == memcmp(buf.space, "preptest\0", 9)); } cxBufferDestroy(&buf); } CX_TEST(test_buffer_write_size_overflow) { CxBuffer buf; 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); } CX_TEST(test_buffer_write_capacity_overflow) { CxBuffer buf; 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); } CX_TEST(test_buffer_write_only_overwrite) { CxBuffer buf; 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); } CX_TEST(test_buffer_write_flush_at_capacity) { CxBuffer buf, target; 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 = cxBufferWriteFunc; 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); 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 == 5); CX_TEST_ASSERT(buf.size == 5); CX_TEST_ASSERT(buf.capacity == 8); 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); } CX_TEST(test_buffer_write_flush_at_threshold) { CxBuffer buf, target; 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 = 16; flush.blksize = 32; flush.blkmax = 1; flush.target = ⌖ flush.wfunc = cxBufferWriteFunc; 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); CX_TEST_ASSERT(buf.size == 10); CX_TEST_ASSERT(buf.capacity == 16); CX_TEST_ASSERT(target.pos == 0); CX_TEST_ASSERT(target.size == 0); written = cxBufferWrite("hello world", 1, 11, &buf); CX_TEST_ASSERT(written == 11); CX_TEST_ASSERT(buf.pos == 11); CX_TEST_ASSERT(buf.size == 11); CX_TEST_ASSERT(buf.capacity == 16); 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_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, 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); 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 >= 13); CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoohello,", 13)); } cxBufferDestroy(&buf); cxBufferDestroy(&target); } CX_TEST(test_buffer_write_flush_multibyte) { // this test case tests that flushing works correctly even when the // contents in the buffer are currently not aligned with the item size CxBuffer buf, target; cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); cxBufferInit(&buf, NULL, 8, cxDefaultAllocator, CX_BUFFER_DEFAULT); memset(buf.space, 0, 8); cxBufferPutString(&buf, "pre"); CX_TEST_DO { CxBufferFlushConfig flush; flush.threshold = 0; flush.blksize = 4; // test blksize that is not aligned flush.blkmax = 2; // test with two blocks flush.target = ⌖ flush.wfunc = cxBufferWriteFunc; CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush)); // first case: string fits after flush size_t written = cxBufferWrite("foobar", 3, 2, &buf); CX_TEST_ASSERT(written == 2); CX_TEST_ASSERT(buf.pos == 6); CX_TEST_ASSERT(buf.size == 6); CX_TEST_ASSERT(target.pos == 3); CX_TEST_ASSERT(target.size == 3); CX_TEST_ASSERT(0 == memcmp(buf.space, "foobar", 6)); CX_TEST_ASSERT(0 == memcmp(target.space, "pre", 3)); // second case: string does not fit, data is relayed, but only two blocks! written = cxBufferWrite("bazfooBAR", 3, 3, &buf); CX_TEST_ASSERT(written == 3); CX_TEST_ASSERT(buf.pos == 3); CX_TEST_ASSERT(buf.size == 3); CX_TEST_ASSERT(target.pos == 15); CX_TEST_ASSERT(target.size == 15); CX_TEST_ASSERT(0 == memcmp(buf.space, "BAR", 3)); CX_TEST_ASSERT(0 == memcmp(target.space, "prefoobarbazfoo", 15)); // third case: everything can be relayed, block size is large enough buf.flush->blkmax = 3; written = cxBufferWrite("abcdef012", 3, 3, &buf); CX_TEST_ASSERT(written == 3); CX_TEST_ASSERT(buf.pos == 0); CX_TEST_ASSERT(buf.size == 0); CX_TEST_ASSERT(target.pos == 27); CX_TEST_ASSERT(target.size == 27); CX_TEST_ASSERT(0 == memcmp(target.space, "prefoobarbazfooBARabcdef012", 27)); } cxBufferDestroy(&buf); cxBufferDestroy(&target); } CX_TEST(test_buffer_write_flush_misaligned) { // this test case tests that flushing works correctly even when the // contents in the buffer are currently not aligned with the item size CxBuffer buf, target; 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 = cxBufferWriteFunc; CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush)); // first case: string fits after flush size_t written = cxBufferWrite("foobar", 3, 2, &buf); CX_TEST_ASSERT(written == 2); CX_TEST_ASSERT(buf.pos == 7); CX_TEST_ASSERT(buf.size == 7); CX_TEST_ASSERT(target.pos == 3); CX_TEST_ASSERT(target.size == 3); CX_TEST_ASSERT(0 == memcmp(buf.space, "pfoobar", 7)); CX_TEST_ASSERT(0 == memcmp(target.space, "pre", 3)); // second case: string does not fit, relaying not possible due to misalignment // string will be truncated (two items fit into buffer, the third does not) written = cxBufferWrite("bazfoobar", 3, 3, &buf); CX_TEST_ASSERT(written == 2); CX_TEST_ASSERT(buf.pos == 7); CX_TEST_ASSERT(buf.size == 7); CX_TEST_ASSERT(target.pos == 9); CX_TEST_ASSERT(target.size == 9); CX_TEST_ASSERT(0 == memcmp(buf.space, "rbazfoo", 7)); CX_TEST_ASSERT(0 == memcmp(target.space, "prepfooba", 9)); } cxBufferDestroy(&buf); cxBufferDestroy(&target); } CX_TEST(test_buffer_write_flush_target_full) { CxBuffer buf, target; // target does NOT auto-extend and can get completely full cxBufferInit(&target, NULL, 8, cxDefaultAllocator, CX_BUFFER_DEFAULT); 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 = cxBufferWriteFunc; CX_TEST_ASSERT(0 == cxBufferEnableFlushing(&buf, flush)); // step one - flush 4 existing bytes, write 6 new bytes size_t written = cxBufferWrite("foobar", 1, 6, &buf); CX_TEST_ASSERT(written == 6); CX_TEST_ASSERT(buf.pos == 6); CX_TEST_ASSERT(buf.size == 6); CX_TEST_ASSERT(target.pos == 4); CX_TEST_ASSERT(target.size == 4); CX_TEST_ASSERT(0 == memcmp(buf.space, "foobar", 6)); CX_TEST_ASSERT(0 == memcmp(target.space, "prep", 4)); // step two - can only flush 4 more bytes, but rest fits into buffer written = cxBufferWrite("xyz", 1, 3, &buf); CX_TEST_ASSERT(written == 3); CX_TEST_ASSERT(buf.pos == 5); CX_TEST_ASSERT(buf.size == 5); CX_TEST_ASSERT(target.pos == 8); CX_TEST_ASSERT(target.size == 8); CX_TEST_ASSERT(0 == memcmp(buf.space, "arxyz", 5)); CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoob", 8)); // step three - cannot flush more, but can write 3 more bytes written = cxBufferWrite("123456", 1, 6, &buf); CX_TEST_ASSERT(written == 3); CX_TEST_ASSERT(buf.pos == 8); CX_TEST_ASSERT(buf.size == 8); CX_TEST_ASSERT(target.pos == 8); CX_TEST_ASSERT(target.size == 8); CX_TEST_ASSERT(0 == memcmp(buf.space, "arxyz123", 8)); CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoob", 8)); // final test, cannot write anything more written = cxBufferWrite("baz", 1, 3, &buf); CX_TEST_ASSERT(written == 0); CX_TEST_ASSERT(buf.pos == 8); CX_TEST_ASSERT(buf.size == 8); CX_TEST_ASSERT(target.pos == 8); CX_TEST_ASSERT(target.size == 8); CX_TEST_ASSERT(0 == memcmp(buf.space, "arxyz123", 8)); CX_TEST_ASSERT(0 == memcmp(target.space, "prepfoob", 8)); } 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 = cxBufferWriteFunc; 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); 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"); cx_test_register(suite, test_buffer_init_wrap_space); cx_test_register(suite, test_buffer_init_wrap_space_auto_extend); cx_test_register(suite, test_buffer_init_wrap_space_auto_free); cx_test_register(suite, test_buffer_init_fresh_space); cx_test_register(suite, test_buffer_init_on_heap); cx_test_register(suite, test_buffer_minimum_capacity_sufficient); cx_test_register(suite, test_buffer_minimum_capacity_extend); cx_test_register(suite, test_buffer_shrink); cx_test_register(suite, test_buffer_shrink_copy_on_write); cx_test_register(suite, test_buffer_shrink_copy_on_extend); cx_test_register(suite, test_buffer_clear); cx_test_register(suite, test_buffer_clear_copy_on_write); cx_test_register(suite, test_buffer_reset); cx_test_register(suite, test_buffer_seek_set_zero); cx_test_register(suite, test_buffer_seek_set_valid); cx_test_register(suite, test_buffer_seek_set_invalid); cx_test_register(suite, test_buffer_seek_cur_zero); cx_test_register(suite, test_buffer_seek_cur_valid_positive); cx_test_register(suite, test_buffer_seek_cur_valid_negative); cx_test_register(suite, test_buffer_seek_cur_invalid_positive); cx_test_register(suite, test_buffer_seek_cur_invalid_negative); cx_test_register(suite, test_buffer_seek_end_zero); cx_test_register(suite, test_buffer_seek_end_valid); cx_test_register(suite, test_buffer_seek_end_invalid); cx_test_register(suite, test_buffer_seek_whence_invalid); cx_test_register(suite, test_buffer_eof_reached); cx_test_register(suite, test_buffer_eof_not_reached); cx_test_register(suite, test_buffer_shift_left_zero); cx_test_register(suite, test_buffer_shift_left_zero_offset_interface); cx_test_register(suite, test_buffer_shift_left_standard); cx_test_register(suite, test_buffer_shift_left_overshift); cx_test_register(suite, test_buffer_shift_left_overshift_pos_only); cx_test_register(suite, test_buffer_shift_left_offset_interface); cx_test_register(suite, test_buffer_shift_left_copy_on_write); cx_test_register(suite, test_buffer_shift_right_zero); cx_test_register(suite, test_buffer_shift_right_zero_offset_interface); cx_test_register(suite, test_buffer_shift_right_standard); 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_shift_right_copy_on_write); 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_write_copy_on_write); cx_test_register(suite, test_buffer_append); cx_test_register(suite, test_buffer_append_flush); 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_copy_on_write); 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_put_string_copy_on_extend); cx_test_register(suite, test_buffer_put_string_copy_on_write); cx_test_register(suite, test_buffer_terminate); 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_and_buffer_too_small); cx_test_register(suite, test_buffer_write_flush_multibyte); cx_test_register(suite, test_buffer_write_flush_misaligned); cx_test_register(suite, test_buffer_write_flush_target_full); 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); 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; }