# HG changeset patch # User Mike Becker # Date 1675803337 -3600 # Node ID e081643aae2a9e1a6ff7a048376d33774ed7a6cb # Parent bf817b825ed2719847d5afbac6bfc11912f4c3a7 rename test directory to avoid name clash with Makefile target diff -r bf817b825ed2 -r e081643aae2a CMakeLists.txt --- a/CMakeLists.txt Tue Feb 07 21:53:06 2023 +0100 +++ b/CMakeLists.txt Tue Feb 07 21:55:37 2023 +0100 @@ -15,7 +15,7 @@ # Tests enable_testing() -add_subdirectory(test) +add_subdirectory(tests) # Web Documentation add_subdirectory(docs/src) diff -r bf817b825ed2 -r e081643aae2a test/.clang-tidy --- a/test/.clang-tidy Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -# Disable static initialization warning for test code -Checks: '-cert-err58-cpp' diff -r bf817b825ed2 -r e081643aae2a test/CMakeLists.txt --- a/test/CMakeLists.txt Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,32 +0,0 @@ -# Load Google Test Framework -set(CMAKE_CXX_STANDARD 17) - -include(FetchContent) -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG e2239ee6043f73722e7aa812a459f54a28552929 # release 1.11.0 -) -# For Windows: Prevent overriding the parent project's compiler/linker settings -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) -FetchContent_MakeAvailable(googletest) -include(GoogleTest) -message(STATUS "Google Test made available") - -add_executable(ucxtest - test_utils.cpp - test_allocator.cpp - test_compare.cpp - test_string.cpp - test_buffer.cpp - test_list.cpp - test_tree.cpp - test_hash_key.cpp - test_map.cpp - test_basic_mempool.cpp - test_printf.cpp - selftest.cpp - util_allocator.cpp - ) -target_link_libraries(ucxtest PRIVATE ucx_static gtest_main) -gtest_discover_tests(ucxtest) diff -r bf817b825ed2 -r e081643aae2a test/selftest.cpp --- a/test/selftest.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +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 -#include - -TEST(SelfTest, BasicAssertion) { - EXPECT_EQ(7 * 6, 42); -} - -TEST(SelfTest, UcxVersion) { - EXPECT_GE(UCX_VERSION_MAJOR, 3); - EXPECT_GE(UCX_VERSION, 3 << 16); -} diff -r bf817b825ed2 -r e081643aae2a test/test_allocator.cpp --- a/test/test_allocator.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +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/allocator.h" -#include - -TEST(Allocator, DefaultAllocator) { - cx_allocator_class *clazz = cxDefaultAllocator->cl; - ASSERT_NE(clazz, nullptr); -} - -TEST(Allocator, DefaultMalloc) { - void *test = cxMalloc(cxDefaultAllocator, 16); - ASSERT_NE(test, nullptr); - free(test); -} - -TEST(Allocator, DefaultRealloc) { - void *test = calloc(8, 1); - memcpy(test, "Test", 5); - test = cxRealloc(cxDefaultAllocator, test, 16); - ASSERT_NE(test, nullptr); - EXPECT_STREQ(reinterpret_cast(test), "Test"); - free(test); -} - -TEST(Allocator, Reallocate) { - void *test = calloc(8, 1); - memcpy(test, "Test", 5); - int ret = cxReallocate(cxDefaultAllocator, &test, 16); - EXPECT_EQ(ret, 0); - ASSERT_NE(test, nullptr); - EXPECT_STREQ(reinterpret_cast(test), "Test"); - free(test); -} - -TEST(Allocator, DefaultCalloc) { - char *test = reinterpret_cast(cxCalloc(cxDefaultAllocator, 8, 2)); - ASSERT_NE(test, nullptr); - for (int i = 0; i < 16; i++) ASSERT_EQ(test[i], 0); - free(test); -} - -TEST(Allocator, DefaultFree) { - void *test = malloc(16); - EXPECT_NO_FATAL_FAILURE( - cxFree(cxDefaultAllocator, test); - ); -} - -TEST(Allocator, FailingReallocate) { - // Mock an allocator that always returns nullptr on realloc - cx_allocator_class mock_cl; - mock_cl.realloc = []( - [[maybe_unused]]void *p, - [[maybe_unused]]void *d, - [[maybe_unused]]size_t n - ) -> void * { return nullptr; }; - cx_allocator_s mock{&mock_cl, nullptr}; - - void *test = calloc(8, 1); - memcpy(test, "Test", 5); - void *original = test; - int ret = cxReallocate(&mock, &test, 16); - // non-zero return code because of the failure - EXPECT_NE(ret, 0); - // the test pointer was not changed and still points to the same memory - EXPECT_EQ(test, original); - EXPECT_STREQ(reinterpret_cast(test), "Test"); - free(test); -} diff -r bf817b825ed2 -r e081643aae2a test/test_basic_mempool.cpp --- a/test/test_basic_mempool.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,154 +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/basic_mempool.h" -#include "util_allocator.h" -#include - -class CxBasicMempool : public ::testing::Test { -protected: - CxMempool *pool = nullptr; - - void TearDown() override { - if (pool != nullptr) { - cxMempoolDestroy(pool); - } - } -}; - -TEST_F(CxBasicMempool, Create) { - pool = cxBasicMempoolCreate(16); - ASSERT_NE(pool->allocator, nullptr); - ASSERT_NE(pool->cl, nullptr); - EXPECT_NE(pool->cl->destroy, nullptr); - ASSERT_NE(pool->allocator->cl, nullptr); - EXPECT_EQ(pool->allocator->data, pool); - EXPECT_NE(pool->allocator->cl->malloc, nullptr); - EXPECT_NE(pool->allocator->cl->calloc, nullptr); - EXPECT_NE(pool->allocator->cl->realloc, nullptr); - EXPECT_NE(pool->allocator->cl->free, nullptr); - - auto basic_pool = reinterpret_cast(pool); - EXPECT_EQ(basic_pool->size, 16); - EXPECT_EQ(basic_pool->ndata, 0); - EXPECT_NE(basic_pool->data, nullptr); -} - -TEST_F(CxBasicMempool, malloc) { - pool = cxBasicMempoolCreate(4); - auto basic_pool = reinterpret_cast(pool); - EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); - EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); - EXPECT_EQ(basic_pool->ndata, 2); - EXPECT_EQ(basic_pool->size, 4); - EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); - EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); - EXPECT_EQ(basic_pool->ndata, 4); - EXPECT_EQ(basic_pool->size, 4); - EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); - EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); - EXPECT_EQ(basic_pool->ndata, 6); - EXPECT_GE(basic_pool->size, 6); -} - -TEST_F(CxBasicMempool, calloc) { - pool = cxBasicMempoolCreate(4); - - auto test = (int *) cxCalloc(pool->allocator, 2, sizeof(int)); - ASSERT_NE(test, nullptr); - EXPECT_EQ(test[0], 0); - EXPECT_EQ(test[1], 0); -} - -static unsigned test_destructor_called = 0; - -static void test_destructor([[maybe_unused]] void *mem) { - test_destructor_called++; -} - -TEST_F(CxBasicMempool, destructor) { - pool = cxBasicMempoolCreate(4); - auto data = cxMalloc(pool->allocator, sizeof(int)); - *((int *) data) = 13; - cxMempoolSetDestructor(pool, data, test_destructor); - EXPECT_EQ(*((int *) data), 13); - test_destructor_called = 0; - cxFree(pool->allocator, data); - EXPECT_EQ(test_destructor_called, 1); - data = cxMalloc(pool->allocator, sizeof(int)); - cxMempoolSetDestructor(pool, data, test_destructor); - cxMempoolDestroy(pool); - pool = nullptr; - EXPECT_EQ(test_destructor_called, 2); -} - -TEST_F(CxBasicMempool, realloc) { - pool = cxBasicMempoolCreate(4); - auto data = cxMalloc(pool->allocator, sizeof(int)); - *((int *) data) = 13; - cxMempoolSetDestructor(pool, data, test_destructor); - - void *rdata = data; - unsigned n = 1; - while (rdata == data) { - n <<= 1; - ASSERT_LT(n, 65536); // eventually the memory should be moved elsewhere - rdata = cxRealloc(pool->allocator, data, n * sizeof(intptr_t)); - } - - EXPECT_EQ(*((int *) rdata), 13); - // test if destructor is still intact - test_destructor_called = 0; - cxFree(pool->allocator, rdata); - EXPECT_EQ(test_destructor_called, 1); -} - - -TEST_F(CxBasicMempool, free) { - pool = cxBasicMempoolCreate(4); - auto basic_pool = reinterpret_cast(pool); - - void *mem1; - void *mem2; - - mem1 = cxMalloc(pool->allocator, 16); - cxFree(pool->allocator, mem1); - EXPECT_EQ(basic_pool->ndata, 0); - - cxMalloc(pool->allocator, 16); - cxMalloc(pool->allocator, 16); - mem1 = cxMalloc(pool->allocator, 16); - cxMalloc(pool->allocator, 16); - mem2 = cxMalloc(pool->allocator, 16); - - EXPECT_EQ(basic_pool->ndata, 5); - cxFree(pool->allocator, mem1); - EXPECT_EQ(basic_pool->ndata, 4); - cxFree(pool->allocator, mem2); - EXPECT_EQ(basic_pool->ndata, 3); -} \ No newline at end of file diff -r bf817b825ed2 -r e081643aae2a test/test_buffer.cpp --- a/test/test_buffer.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,815 +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 BufferFixture : public ::testing::Test { -protected: - void SetUp() override { - cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); - buf.size = 6; - buf.pos = 3; - } - - void TearDown() override { - cxBufferDestroy(&buf); - } - - CxBuffer buf{}; -}; - -static void expect_default_flush_config(CxBuffer *buf) { - EXPECT_EQ(buf->flush_blkmax, 0); - EXPECT_EQ(buf->flush_blksize, 4096); - EXPECT_EQ(buf->flush_threshold, SIZE_MAX); - EXPECT_EQ(buf->flush_func, nullptr); - EXPECT_EQ(buf->flush_target, nullptr); -} - -TEST(BufferInit, WrapSpace) { - CxTestingAllocator alloc; - CxBuffer buf; - void *space = cxMalloc(&alloc, 16); - cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_DEFAULT); - expect_default_flush_config(&buf); - EXPECT_EQ(buf.space, space); - EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0); - EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0); - EXPECT_EQ(buf.pos, 0); - EXPECT_EQ(buf.size, 0); - EXPECT_EQ(buf.capacity, 16); - EXPECT_EQ(buf.allocator, &alloc); - cxBufferDestroy(&buf); - EXPECT_FALSE(alloc.verify()); - cxFree(&alloc, space); - EXPECT_TRUE(alloc.verify()); -} - -TEST(BufferInit, WrapSpaceAutoExtend) { - CxTestingAllocator alloc; - CxBuffer buf; - void *space = cxMalloc(&alloc, 16); - cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_AUTO_EXTEND); - expect_default_flush_config(&buf); - EXPECT_EQ(buf.space, space); - EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, CX_BUFFER_AUTO_EXTEND); - EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0); - EXPECT_EQ(buf.pos, 0); - EXPECT_EQ(buf.size, 0); - EXPECT_EQ(buf.capacity, 16); - EXPECT_EQ(buf.allocator, &alloc); - cxBufferDestroy(&buf); - EXPECT_FALSE(alloc.verify()); - cxFree(&alloc, space); - EXPECT_TRUE(alloc.verify()); -} - -TEST(BufferInit, WrapSpaceAutoFree) { - CxTestingAllocator alloc; - CxBuffer buf; - void *space = cxMalloc(&alloc, 16); - cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_FREE_CONTENTS); - expect_default_flush_config(&buf); - EXPECT_EQ(buf.space, space); - EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0); - EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS); - EXPECT_EQ(buf.pos, 0); - EXPECT_EQ(buf.size, 0); - EXPECT_EQ(buf.capacity, 16); - EXPECT_EQ(buf.allocator, &alloc); - EXPECT_FALSE(alloc.verify()); - cxBufferDestroy(&buf); - EXPECT_TRUE(alloc.verify()); -} - -TEST(BufferInit, FreshSpace) { - CxTestingAllocator alloc; - CxBuffer buf; - cxBufferInit(&buf, nullptr, 8, &alloc, CX_BUFFER_DEFAULT); - expect_default_flush_config(&buf); - EXPECT_NE(buf.space, nullptr); - EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0); - EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS); - EXPECT_EQ(buf.pos, 0); - EXPECT_EQ(buf.size, 0); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(buf.allocator, &alloc); - EXPECT_FALSE(alloc.verify()); // space is still allocated - cxBufferDestroy(&buf); - EXPECT_TRUE(alloc.verify()); -} - -class BufferShiftFixture : public ::testing::Test { -protected: - void SetUp() override { - ASSERT_TRUE(alloc.verify()); - cxBufferInit(&buf, nullptr, 16, &alloc, CX_BUFFER_DEFAULT); - memcpy(buf.space, "test____________", 16); - buf.capacity = 8; // purposely pretend that the buffer has less capacity s.t. we can test beyond the range - buf.pos = 4; - buf.size = 4; - } - - void TearDown() override { - cxBufferDestroy(&buf); - EXPECT_TRUE(alloc.verify()); - } - - CxTestingAllocator alloc; - CxBuffer buf{}; -}; - -class BufferShiftLeft : public BufferShiftFixture { -}; - -TEST_F(BufferShiftLeft, Zero) { - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - int ret = cxBufferShiftLeft(&buf, 0); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 4); - EXPECT_EQ(buf.size, 4); - EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); -} - -TEST_F(BufferShiftLeft, ZeroOffsetInterface) { - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - int ret = cxBufferShift(&buf, -0); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 4); - EXPECT_EQ(buf.size, 4); - EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); -} - -TEST_F(BufferShiftLeft, Standard) { - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - int ret = cxBufferShiftLeft(&buf, 2); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 2); - EXPECT_EQ(buf.size, 2); - EXPECT_TRUE(memcmp(buf.space, "stst________", 8) == 0); -} - -TEST_F(BufferShiftLeft, Overshift) { - ASSERT_LT(buf.pos, 6); - ASSERT_LT(buf.size, 6); - int ret = cxBufferShiftLeft(&buf, 6); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 0); - EXPECT_EQ(buf.size, 0); - EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); -} - -TEST_F(BufferShiftLeft, OvershiftPosOnly) { - buf.pos = 2; - ASSERT_EQ(buf.size, 4); - int ret = cxBufferShiftLeft(&buf, 3); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 0); - EXPECT_EQ(buf.size, 1); - EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); -} - -TEST_F(BufferShiftLeft, OffsetInterface) { - buf.pos = 3; - ASSERT_EQ(buf.size, 4); - int ret = cxBufferShift(&buf, -2); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 1); - EXPECT_EQ(buf.size, 2); - EXPECT_TRUE(memcmp(buf.space, "stst________", 8) == 0); -} - -class BufferShiftRight : public BufferShiftFixture { -}; - -TEST_F(BufferShiftRight, Zero) { - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - int ret = cxBufferShiftRight(&buf, 0); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 4); - EXPECT_EQ(buf.size, 4); - EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); -} - -TEST_F(BufferShiftRight, ZeroOffsetInterface) { - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - int ret = cxBufferShift(&buf, +0); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 4); - EXPECT_EQ(buf.size, 4); - EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); -} - -TEST_F(BufferShiftRight, Standard) { - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - int ret = cxBufferShiftRight(&buf, 3); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 7); - EXPECT_EQ(buf.size, 7); - EXPECT_TRUE(memcmp(buf.space, "testest_____", 8) == 0); -} - -TEST_F(BufferShiftRight, OvershiftDiscard) { - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - ASSERT_EQ(buf.capacity, 8); - int ret = cxBufferShiftRight(&buf, 6); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 8); - EXPECT_EQ(buf.size, 8); - EXPECT_EQ(buf.capacity, 8); - EXPECT_TRUE(memcmp(buf.space, "test__te____", 8) == 0); -} - -TEST_F(BufferShiftRight, OvershiftExtend) { - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - ASSERT_EQ(buf.capacity, 8); - buf.flags |= CX_BUFFER_AUTO_EXTEND; - int ret = cxBufferShiftRight(&buf, 6); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 10); - EXPECT_EQ(buf.size, 10); - EXPECT_GE(buf.capacity, 10); - EXPECT_TRUE(memcmp(buf.space, "test__test__", 8) == 0); -} - -TEST_F(BufferShiftRight, OffsetInterface) { - buf.pos = 3; - ASSERT_EQ(buf.size, 4); - int ret = cxBufferShift(&buf, 2); - EXPECT_EQ(ret, 0); - EXPECT_EQ(buf.pos, 5); - EXPECT_EQ(buf.size, 6); - EXPECT_TRUE(memcmp(buf.space, "tetest______", 8) == 0); -} - -TEST(BufferMinimumCapacity, Sufficient) { - CxTestingAllocator alloc; - auto 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); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(buf.size, 8); - EXPECT_TRUE(memcmp(buf.space, "Testing", 8) == 0); - cxBufferDestroy(&buf); - EXPECT_TRUE(alloc.verify()); -} - -TEST(BufferMinimumCapacity, Extend) { - CxTestingAllocator alloc; - auto 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); - EXPECT_EQ(buf.capacity, 16); - EXPECT_EQ(buf.size, 8); - EXPECT_TRUE(memcmp(buf.space, "Testing", 8) == 0); - cxBufferDestroy(&buf); - EXPECT_TRUE(alloc.verify()); -} - -TEST(BufferClear, Test) { - char space[16]; - strcpy(space, "clear test"); - CxBuffer buf; - cxBufferInit(&buf, space, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); - ASSERT_EQ(buf.size, 0); - // only clear the used part of the buffer - cxBufferClear(&buf); - EXPECT_EQ(memcmp(space, "clear test", 10), 0); - buf.size = 5; - buf.pos = 3; - cxBufferClear(&buf); - EXPECT_EQ(memcmp(space, "\0\0\0\0\0 test", 10), 0); - EXPECT_EQ(buf.size, 0); - EXPECT_EQ(buf.pos, 0); - cxBufferDestroy(&buf); -} - -class BufferWrite : public ::testing::Test { -protected: - CxBuffer buf{}, target{}; - - void SetUp() override { - cxBufferInit(&target, nullptr, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); - cxBufferInit(&buf, nullptr, 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"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - size_t written = cxBufferWrite(data, 1, 4, &buf); - EXPECT_EQ(written, 4); - EXPECT_EQ(buf.size, 8); - EXPECT_EQ(buf.pos, 8); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); -} - -TEST_F(BufferWrite, SizeOneDiscard) { - const char *data = "testing"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - size_t written = cxBufferWrite(data, 1, 7, &buf); - EXPECT_EQ(written, 4); - EXPECT_EQ(buf.size, 8); - EXPECT_EQ(buf.pos, 8); - EXPECT_EQ(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"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - size_t written = cxBufferWrite(data, 1, 7, &buf); - EXPECT_EQ(written, 7); - EXPECT_EQ(buf.size, 11); - EXPECT_EQ(buf.pos, 11); - EXPECT_GE(buf.capacity, 11); - EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0); -} - -TEST_F(BufferWrite, MultibyteFit) { - const char *data = "test"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - size_t written = cxBufferWrite(data, 2, 2, &buf); - EXPECT_EQ(written, 2); - EXPECT_EQ(buf.size, 8); - EXPECT_EQ(buf.pos, 8); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); -} - -TEST_F(BufferWrite, MultibyteDiscard) { - const char *data = "testing"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.size, 4); - buf.pos = 3; - size_t written = cxBufferWrite(data, 2, 4, &buf); - // remember: whole elements are discarded if they do not fit - EXPECT_EQ(written, 2); - EXPECT_EQ(buf.size, 7); - EXPECT_EQ(buf.pos, 7); - EXPECT_EQ(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"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.size, 4); - buf.pos = 3; - size_t written = cxBufferWrite(data, 2, 3, &buf); - // remember: whole elements are discarded if they do not fit - EXPECT_EQ(written, 3); - EXPECT_EQ(buf.size, 9); - EXPECT_EQ(buf.pos, 9); - EXPECT_GE(buf.capacity, 9); - EXPECT_EQ(memcmp(buf.space, "pretester", 9), 0); -} - -TEST_F(BufferWrite, PutcWrapperFit) { - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - int c = cxBufferPut(&buf, 0x200 | 'a'); - EXPECT_EQ(c, 'a'); - EXPECT_EQ(buf.size, 5); - EXPECT_EQ(buf.pos, 5); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(memcmp(buf.space, "prepa\0", 6), 0); -} - -TEST_F(BufferWrite, PutcWrapperDiscard) { - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.size, 4); - buf.pos = 8; - int c = cxBufferPut(&buf, 0x200 | 'a'); - EXPECT_EQ(c, EOF); - EXPECT_EQ(buf.size, 4); - EXPECT_EQ(buf.pos, 8); - EXPECT_EQ(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; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.size, 4); - buf.pos = 8; - int c = cxBufferPut(&buf, 0x200 | 'a'); - EXPECT_EQ(c, 'a'); - EXPECT_EQ(buf.size, 9); - EXPECT_EQ(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"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - size_t written = cxBufferPutString(&buf, data); - EXPECT_EQ(written, 4); - EXPECT_EQ(buf.size, 8); - EXPECT_EQ(buf.pos, 8); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); -} - -TEST_F(BufferWrite, PutStringWrapperDiscard) { - const char *data = "testing"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - size_t written = cxBufferPutString(&buf, data); - EXPECT_EQ(written, 4); - EXPECT_EQ(buf.size, 8); - EXPECT_EQ(buf.pos, 8); - EXPECT_EQ(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"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - size_t written = cxBufferPutString(&buf, data); - EXPECT_EQ(written, 7); - EXPECT_EQ(buf.size, 11); - EXPECT_EQ(buf.pos, 11); - EXPECT_GE(buf.capacity, 11); - EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0); -} - -TEST_F(BufferWrite, MultOverflow) { - const char *data = "testing"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - size_t written = cxBufferWrite(data, 8, SIZE_MAX / 4, &buf); - EXPECT_EQ(written, 0); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(buf.pos, 4); - EXPECT_EQ(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"; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - ASSERT_EQ(buf.size, 4); - size_t written = cxBufferWrite(data, 1, SIZE_MAX - 2, &buf); - EXPECT_EQ(written, 0); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(buf.pos, 4); - EXPECT_EQ(buf.size, 4); - EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0); -} - -TEST_F(BufferWrite, OnlyOverwrite) { - buf.flags |= CX_BUFFER_AUTO_EXTEND; - ASSERT_EQ(buf.capacity, 8); - memcpy(buf.space, "preptest", 8); - buf.pos = 3; - buf.size = 8; - size_t written = cxBufferWrite("XXX", 2, 2, &buf); - EXPECT_EQ(written, 2); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(buf.size, 8); - EXPECT_EQ(buf.pos, 7); - EXPECT_EQ(memcmp(buf.space, "preXXX\0t", 8), 0); -} - -TEST_F(BufferWrite, FlushAtCapacity) { - enableFlushing(); - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - size_t written = cxBufferWrite("foo", 1, 3, &buf); - EXPECT_EQ(written, 3); - ASSERT_EQ(buf.pos, 7); - ASSERT_EQ(buf.size, 7); - ASSERT_EQ(target.pos, 0); - ASSERT_EQ(target.size, 0); - written = cxBufferWrite("hello", 1, 5, &buf); - EXPECT_EQ(written, 5); - EXPECT_EQ(buf.pos, 0); - EXPECT_EQ(buf.size, 0); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(target.pos, 12); - ASSERT_EQ(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; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - size_t written = cxBufferWrite("foobar", 1, 6, &buf); - EXPECT_EQ(written, 6); - ASSERT_EQ(buf.pos, 10); - ASSERT_EQ(buf.size, 10); - ASSERT_GE(buf.capacity, 10); - ASSERT_LE(buf.capacity, 12); - ASSERT_EQ(target.pos, 0); - ASSERT_EQ(target.size, 0); - written = cxBufferWrite("hello", 1, 5, &buf); - EXPECT_EQ(written, 5); - EXPECT_EQ(buf.pos, 0); - EXPECT_EQ(buf.size, 0); - EXPECT_LE(buf.capacity, 12); - EXPECT_EQ(target.pos, 15); - ASSERT_EQ(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; - ASSERT_EQ(buf.capacity, 8); - ASSERT_EQ(buf.pos, 4); - size_t written = cxBufferWrite("foo", 1, 3, &buf); - EXPECT_EQ(written, 3); - ASSERT_EQ(buf.pos, 7); - ASSERT_EQ(buf.size, 7); - ASSERT_EQ(target.pos, 0); - ASSERT_EQ(target.size, 0); - written = cxBufferWrite("hello, world!", 1, 13, &buf); - // " world!" fits into this buffer, the remaining stuff is flushed out - EXPECT_EQ(written, 13); - EXPECT_EQ(buf.pos, 7); - EXPECT_EQ(buf.size, 7); - EXPECT_EQ(buf.capacity, 8); - EXPECT_EQ(memcmp(buf.space, " world!", 7), 0); - EXPECT_EQ(target.pos, 13); - ASSERT_EQ(target.size, 13); - EXPECT_EQ(target.capacity, 16); - EXPECT_EQ(memcmp(target.space, "prepfoohello,", 13), 0); -} - -class BufferSeek : public BufferFixture { -}; - -TEST_F(BufferSeek, SetZero) { - int result = cxBufferSeek(&buf, 0, SEEK_SET); - EXPECT_EQ(result, 0); - EXPECT_EQ(buf.pos, 0); -} - -TEST_F(BufferSeek, SetValid) { - int result = cxBufferSeek(&buf, 5, SEEK_SET); - EXPECT_EQ(result, 0); - EXPECT_EQ(buf.pos, 5); -} - -TEST_F(BufferSeek, SetInvalid) { - ASSERT_EQ(buf.pos, 3); - int result = cxBufferSeek(&buf, 6, SEEK_SET); - EXPECT_NE(result, 0); - EXPECT_EQ(buf.pos, 3); -} - -TEST_F(BufferSeek, CurZero) { - ASSERT_EQ(buf.pos, 3); - int result = cxBufferSeek(&buf, 0, SEEK_CUR); - EXPECT_EQ(result, 0); - EXPECT_EQ(buf.pos, 3); -} - -TEST_F(BufferSeek, CurValidPositive) { - ASSERT_EQ(buf.pos, 3); - int result = cxBufferSeek(&buf, 2, SEEK_CUR); - EXPECT_EQ(result, 0); - EXPECT_EQ(buf.pos, 5); -} - -TEST_F(BufferSeek, CurValidNegative) { - ASSERT_EQ(buf.pos, 3); - int result = cxBufferSeek(&buf, -3, SEEK_CUR); - EXPECT_EQ(result, 0); - EXPECT_EQ(buf.pos, 0); -} - -TEST_F(BufferSeek, CurInvalidPositive) { - ASSERT_EQ(buf.pos, 3); - int result = cxBufferSeek(&buf, 3, SEEK_CUR); - EXPECT_NE(result, 0); - EXPECT_EQ(buf.pos, 3); -} - -TEST_F(BufferSeek, CurInvalidNegative) { - ASSERT_EQ(buf.pos, 3); - int result = cxBufferSeek(&buf, -4, SEEK_CUR); - EXPECT_NE(result, 0); - EXPECT_EQ(buf.pos, 3); -} - -TEST_F(BufferSeek, EndZero) { - ASSERT_EQ(buf.size, 6); - int result = cxBufferSeek(&buf, 0, SEEK_END); - // the (past-the-)end position is always invalid - EXPECT_NE(result, 0); - EXPECT_EQ(buf.pos, 3); -} - -TEST_F(BufferSeek, EndValid) { - ASSERT_EQ(buf.size, 6); - int result = cxBufferSeek(&buf, -6, SEEK_END); - EXPECT_EQ(result, 0); - EXPECT_EQ(buf.pos, 0); -} - -TEST_F(BufferSeek, EndInvalid) { - ASSERT_EQ(buf.size, 6); - int result = cxBufferSeek(&buf, 1, SEEK_END); - EXPECT_NE(result, 0); - EXPECT_EQ(buf.pos, 3); -} - -TEST_F(BufferSeek, WhenceInvalid) { - ASSERT_EQ(buf.size, 6); - ASSERT_EQ(buf.pos, 3); - int result = cxBufferSeek(&buf, 2, 9000); - EXPECT_NE(result, 0); - EXPECT_EQ(buf.size, 6); - EXPECT_EQ(buf.pos, 3); -} - -class BufferEof : public BufferFixture { -}; - -TEST_F(BufferEof, Reached) { - buf.pos = buf.size; - EXPECT_TRUE(cxBufferEof(&buf)); - buf.pos = buf.size - 1; - ASSERT_FALSE(cxBufferEof(&buf)); - cxBufferPut(&buf, 'a'); - EXPECT_TRUE(cxBufferEof(&buf)); -} - -TEST_F(BufferEof, NotReached) { - buf.pos = buf.size - 1; - EXPECT_FALSE(cxBufferEof(&buf)); - buf.pos = 0; - cxBufferWrite("test", 1, 5, &buf); - EXPECT_FALSE(cxBufferEof(&buf)); -} - -class BufferRead : public ::testing::Test { -protected: - CxBuffer buf{}; - - void SetUp() override { - cxBufferInit(&buf, nullptr, 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'); - EXPECT_EQ(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); - ASSERT_EQ(read, 4); - EXPECT_EQ(memcmp(&target, "me d", 4), 0); - EXPECT_EQ(buf.pos, 6); -} - -TEST_F(BufferRead, ReadOutOfBounds) { - buf.pos = 6; - char target[4]; - auto read = cxBufferRead(&target, 1, 4, &buf); - ASSERT_EQ(read, 3); - EXPECT_EQ(memcmp(&target, "ata", 3), 0); - EXPECT_EQ(buf.pos, 9); -} - -TEST_F(BufferRead, ReadOutOfBoundsMultibyte) { - buf.pos = 6; - char target[4]; - target[2] = '\0'; - auto read = cxBufferRead(&target, 2, 2, &buf); - ASSERT_EQ(read, 1); - EXPECT_EQ(memcmp(&target, "at\0", 3), 0); - EXPECT_EQ(buf.pos, 8); -} - -TEST_F(BufferRead, ReadEof) { - buf.pos = 9; - char target[4]; - auto read = cxBufferRead(&target, 1, 1, &buf); - ASSERT_EQ(read, 0); - EXPECT_EQ(buf.pos, 9); -} diff -r bf817b825ed2 -r e081643aae2a test/test_compare.cpp --- a/test/test_compare.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,127 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2022 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/compare.h" - -#include - -template -static void test_compare( - int (*fnc)( - void const *, - void const * - ) -) { - auto m = std::numeric_limits::max() / 400; - T x, y; - - x = (std::is_signed_v ? -3 : 3) * m; - y = 5 * m; - EXPECT_LT(fnc(&x, &y), 0); - EXPECT_GT(fnc(&y, &x), 0); - - x = 120 * m; - y = 348 * m; - EXPECT_LT(fnc(&x, &y), 0); - EXPECT_GT(fnc(&y, &x), 0); - - if constexpr (std::is_signed_v) { - x = -120 * m; - y = -348 * m; - EXPECT_GT(fnc(&x, &y), 0); - EXPECT_LT(fnc(&y, &x), 0); - } - - x = y; - EXPECT_EQ(fnc(&x, &y), 0); - EXPECT_EQ(fnc(&y, &x), 0); -} - -TEST(Compare, Int) { - test_compare(cx_cmp_int); -} - -TEST(Compare, Longint) { - test_compare(cx_cmp_longint); -} - -TEST(Compare, Longlong) { - test_compare(cx_cmp_longlong); -} - -TEST(Compare, Int16) { - test_compare(cx_cmp_int16); -} - -TEST(Compare, Int32) { - test_compare(cx_cmp_int32); -} - -TEST(Compare, Int64) { - test_compare(cx_cmp_int64); -} - -TEST(Compare, Uint) { - test_compare(cx_cmp_uint); -} - -TEST(Compare, Ulongint) { - test_compare(cx_cmp_ulongint); -} - -TEST(Compare, Ulonglong) { - test_compare(cx_cmp_ulonglong); -} - -TEST(Compare, Uint16) { - test_compare(cx_cmp_uint16); -} - -TEST(Compare, Uint32) { - test_compare(cx_cmp_uint32); -} - -TEST(Compare, Uint64) { - test_compare(cx_cmp_uint64); -} - -TEST(Compare, Float) { - test_compare(cx_cmp_float); -} - -TEST(Compare, Double) { - test_compare(cx_cmp_double); -} - -TEST(Compare, IntPtr) { - test_compare(cx_cmp_intptr); -} - -TEST(Compare, UintPtr) { - test_compare(cx_cmp_uintptr); -} diff -r bf817b825ed2 -r e081643aae2a test/test_hash_key.cpp --- a/test/test_hash_key.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,87 +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/hash_key.h" - -#include - -TEST(cx_hash_key, functions) { - auto str = "my key"; - auto len = strlen(str); - - auto str_key = cx_hash_key_str(str); - auto bytes_key = cx_hash_key_bytes( - reinterpret_cast(str), len); - auto obj_key = cx_hash_key( - reinterpret_cast(str), len); - - EXPECT_EQ(str_key.hash, bytes_key.hash); - EXPECT_EQ(obj_key.hash, bytes_key.hash); - EXPECT_EQ(str_key.len, len); - EXPECT_EQ(bytes_key.len, len); - EXPECT_EQ(bytes_key.len, len); - EXPECT_EQ(str_key.data.cstr, str); - EXPECT_EQ(bytes_key.data.cbytes, reinterpret_cast(str)); - EXPECT_EQ(bytes_key.data.cobj, reinterpret_cast(str)); -} - -TEST(cx_hash_key, empty_string) { - auto str = ""; - - auto str_key = cx_hash_key_str(str); - auto bytes_key = cx_hash_key_bytes( - reinterpret_cast(str), 0); - auto obj_key = cx_hash_key( - reinterpret_cast(str), 0); - - EXPECT_EQ(bytes_key.hash, 4152238450u); - EXPECT_EQ(str_key.hash, 4152238450u); - EXPECT_EQ(obj_key.hash, 4152238450u); - EXPECT_EQ(str_key.len, 0); - EXPECT_EQ(bytes_key.len, 0); - EXPECT_EQ(bytes_key.len, 0); - EXPECT_EQ(str_key.data.cstr, str); - EXPECT_EQ(bytes_key.data.cbytes, reinterpret_cast(str)); - EXPECT_EQ(bytes_key.data.cobj, reinterpret_cast(str)); -} - -TEST(cx_hash_key, null_ptr) { - auto str_key = cx_hash_key_str(nullptr); - auto bytes_key = cx_hash_key_bytes(nullptr, 0); - auto obj_key = cx_hash_key(nullptr, 0); - - EXPECT_EQ(bytes_key.hash, 1574210520u); - EXPECT_EQ(str_key.hash, 1574210520u); - EXPECT_EQ(obj_key.hash, 1574210520u); - EXPECT_EQ(str_key.len, 0); - EXPECT_EQ(bytes_key.len, 0); - EXPECT_EQ(bytes_key.len, 0); - EXPECT_EQ(str_key.data.cstr, nullptr); - EXPECT_EQ(bytes_key.data.cbytes, nullptr); - EXPECT_EQ(bytes_key.data.cobj, nullptr); -} diff -r bf817b825ed2 -r e081643aae2a test/test_list.cpp --- a/test/test_list.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1077 +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/linked_list.h" -#include "cx/array_list.h" -#include "cx/utils.h" -#include "cx/compare.h" -#include "util_allocator.h" - -#include -#include -#include -#include -#include - -struct node { - node *next = nullptr; - node *prev = nullptr; - int data = 0; -}; - -const ptrdiff_t loc_prev = offsetof(struct node, prev); -const ptrdiff_t loc_next = offsetof(struct node, next); -const ptrdiff_t loc_data = offsetof(struct node, data); - -struct node_test_data { - node *begin = nullptr; - - explicit node_test_data(node *begin) : begin(begin) { - auto n = begin; - while (n != nullptr) { - nodes.push_back(n); - n = n->next; - } - } - - node_test_data(node_test_data &) = delete; - - node_test_data(node_test_data &&) = default; - - ~node_test_data() { - for (auto &&n: nodes) delete n; - } - -private: - std::vector nodes; -}; - -static node_test_data create_nodes_test_data(size_t len) { - if (len == 0) return node_test_data{nullptr}; - auto begin = new node; - auto prev = begin; - for (size_t i = 1; i < len; i++) { - auto n = new node; - cx_linked_list_link(prev, n, loc_prev, loc_next); - prev = n; - } - return node_test_data{begin}; -} - -template -static node_test_data create_nodes_test_data( - InputIter begin, - InputIter end -) { - if (begin == end) return node_test_data{nullptr}; - node *first = new node; - first->data = *begin; - node *prev = first; - begin++; - for (; begin != end; begin++) { - auto n = new node; - n->data = *begin; - cx_linked_list_link(prev, n, loc_prev, loc_next); - prev = n; - } - return node_test_data{first}; -} - -static node_test_data create_nodes_test_data(std::initializer_list data) { - return create_nodes_test_data(data.begin(), data.end()); -} - -template -struct int_test_data { - std::array data; - - int_test_data() { - cx_for_n (i, N) data[i] = ::rand(); // NOLINT(cert-msc50-cpp) - } -}; - -TEST(LinkedList_LowLevel, link_unlink) { - node a, b, c; - - cx_linked_list_link(&a, &b, loc_prev, loc_next); - EXPECT_EQ(a.prev, nullptr); - EXPECT_EQ(a.next, &b); - EXPECT_EQ(b.prev, &a); - EXPECT_EQ(b.next, nullptr); - - cx_linked_list_unlink(&a, &b, loc_prev, loc_next); - EXPECT_EQ(a.prev, nullptr); - EXPECT_EQ(a.next, nullptr); - EXPECT_EQ(b.prev, nullptr); - EXPECT_EQ(b.next, nullptr); - - cx_linked_list_link(&b, &c, loc_prev, loc_next); - cx_linked_list_link(&a, &b, loc_prev, loc_next); - cx_linked_list_unlink(&b, &c, loc_prev, loc_next); - EXPECT_EQ(a.prev, nullptr); - EXPECT_EQ(a.next, &b); - EXPECT_EQ(b.prev, &a); - EXPECT_EQ(b.next, nullptr); - EXPECT_EQ(c.prev, nullptr); - EXPECT_EQ(c.next, nullptr); -} - -TEST(LinkedList_LowLevel, cx_linked_list_at) { - node a, b, c, d; - cx_linked_list_link(&a, &b, loc_prev, loc_next); - cx_linked_list_link(&b, &c, loc_prev, loc_next); - cx_linked_list_link(&c, &d, loc_prev, loc_next); - - EXPECT_EQ(cx_linked_list_at(&a, 0, loc_next, 0), &a); - EXPECT_EQ(cx_linked_list_at(&a, 0, loc_next, 1), &b); - EXPECT_EQ(cx_linked_list_at(&a, 0, loc_next, 2), &c); - EXPECT_EQ(cx_linked_list_at(&a, 0, loc_next, 3), &d); - EXPECT_EQ(cx_linked_list_at(&a, 0, loc_next, 4), nullptr); - - EXPECT_EQ(cx_linked_list_at(&b, 1, loc_prev, 0), &a); - EXPECT_EQ(cx_linked_list_at(&b, 1, loc_next, 1), &b); - EXPECT_EQ(cx_linked_list_at(&b, 1, loc_next, 2), &c); - EXPECT_EQ(cx_linked_list_at(&b, 1, loc_next, 3), &d); - EXPECT_EQ(cx_linked_list_at(&b, 1, loc_next, 4), nullptr); - - EXPECT_EQ(cx_linked_list_at(&d, 3, loc_prev, 0), &a); - EXPECT_EQ(cx_linked_list_at(&d, 3, loc_prev, 1), &b); -} - -TEST(LinkedList_LowLevel, cx_linked_list_find) { - auto testdata = create_nodes_test_data({2, 4, 6, 8}); - auto list = testdata.begin; - int s; - - s = 2; - EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 0); - s = 4; - EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 1); - s = 6; - EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 2); - s = 8; - EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 3); - s = 10; - EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 4); - s = -2; - EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 4); -} - -TEST(LinkedList_LowLevel, cx_linked_list_compare) { - auto ta = create_nodes_test_data({2, 4, 6, 8}); - auto tb = create_nodes_test_data({2, 4, 6}); - auto tc = create_nodes_test_data({2, 4, 6, 9}); - auto la = ta.begin, lb = tb.begin, lc = tc.begin; - - EXPECT_GT(cx_linked_list_compare(la, lb, loc_next, loc_data, cx_cmp_int), 0); - EXPECT_LT(cx_linked_list_compare(lb, la, loc_next, loc_data, cx_cmp_int), 0); - EXPECT_GT(cx_linked_list_compare(lc, la, loc_next, loc_data, cx_cmp_int), 0); - EXPECT_LT(cx_linked_list_compare(la, lc, loc_next, loc_data, cx_cmp_int), 0); - EXPECT_EQ(cx_linked_list_compare(la, la, loc_next, loc_data, cx_cmp_int), 0); -} - -TEST(LinkedList_LowLevel, cx_linked_list_add) { - // test with begin, end / prev, next - { - node nodes[4]; - void *begin = nullptr, *end = nullptr; - - cx_linked_list_add(&begin, &end, loc_prev, loc_next, &nodes[0]); - EXPECT_EQ(begin, &nodes[0]); - EXPECT_EQ(end, &nodes[0]); - EXPECT_EQ(nodes[0].prev, nullptr); - EXPECT_EQ(nodes[0].next, nullptr); - - cx_linked_list_add(&begin, &end, loc_prev, loc_next, &nodes[1]); - EXPECT_EQ(begin, &nodes[0]); - EXPECT_EQ(end, &nodes[1]); - EXPECT_EQ(nodes[0].next, &nodes[1]); - EXPECT_EQ(nodes[1].prev, &nodes[0]); - } - - // test with begin only / prev, next - { - node nodes[4]; - void *begin = nullptr; - - cx_linked_list_add(&begin, nullptr, loc_prev, loc_next, &nodes[0]); - EXPECT_EQ(begin, &nodes[0]); - cx_linked_list_add(&begin, nullptr, loc_prev, loc_next, &nodes[1]); - EXPECT_EQ(begin, &nodes[0]); - EXPECT_EQ(nodes[0].next, &nodes[1]); - EXPECT_EQ(nodes[1].prev, &nodes[0]); - - cx_linked_list_add(&begin, nullptr, loc_prev, loc_next, &nodes[2]); - EXPECT_EQ(nodes[1].next, &nodes[2]); - EXPECT_EQ(nodes[2].prev, &nodes[1]); - } - - // test with end only / prev, next - { - node nodes[4]; - void *end = nullptr; - - cx_linked_list_add(nullptr, &end, loc_prev, loc_next, &nodes[0]); - EXPECT_EQ(end, &nodes[0]); - cx_linked_list_add(nullptr, &end, loc_prev, loc_next, &nodes[1]); - EXPECT_EQ(end, &nodes[1]); - EXPECT_EQ(nodes[0].next, &nodes[1]); - EXPECT_EQ(nodes[1].prev, &nodes[0]); - - cx_linked_list_add(nullptr, &end, loc_prev, loc_next, &nodes[2]); - EXPECT_EQ(end, &nodes[2]); - EXPECT_EQ(nodes[1].next, &nodes[2]); - EXPECT_EQ(nodes[2].prev, &nodes[1]); - } - - // test with begin, end / next - { - node nodes[4]; - void *begin = nullptr, *end = nullptr; - - cx_linked_list_add(&begin, &end, -1, loc_next, &nodes[0]); - EXPECT_EQ(begin, &nodes[0]); - EXPECT_EQ(end, &nodes[0]); - cx_linked_list_add(&begin, &end, -1, loc_next, &nodes[1]); - EXPECT_EQ(end, &nodes[1]); - EXPECT_EQ(nodes[0].next, &nodes[1]); - EXPECT_EQ(nodes[1].prev, nullptr); - } -} - -TEST(LinkedList_LowLevel, cx_linked_list_prepend) { - // test with begin, end / prev, next - { - node nodes[4]; - void *begin = nullptr, *end = nullptr; - - cx_linked_list_prepend(&begin, &end, loc_prev, loc_next, &nodes[0]); - EXPECT_EQ(begin, &nodes[0]); - EXPECT_EQ(end, &nodes[0]); - EXPECT_EQ(nodes[0].prev, nullptr); - EXPECT_EQ(nodes[0].next, nullptr); - - cx_linked_list_prepend(&begin, &end, loc_prev, loc_next, &nodes[1]); - EXPECT_EQ(begin, &nodes[1]); - EXPECT_EQ(end, &nodes[0]); - EXPECT_EQ(nodes[1].next, &nodes[0]); - EXPECT_EQ(nodes[0].prev, &nodes[1]); - } - - // test with begin only / prev, next - { - node nodes[4]; - void *begin = nullptr; - - cx_linked_list_prepend(&begin, nullptr, loc_prev, loc_next, &nodes[0]); - EXPECT_EQ(begin, &nodes[0]); - cx_linked_list_prepend(&begin, nullptr, loc_prev, loc_next, &nodes[1]); - EXPECT_EQ(begin, &nodes[1]); - EXPECT_EQ(nodes[1].next, &nodes[0]); - EXPECT_EQ(nodes[0].prev, &nodes[1]); - - cx_linked_list_prepend(&begin, nullptr, loc_prev, loc_next, &nodes[2]); - EXPECT_EQ(begin, &nodes[2]); - EXPECT_EQ(nodes[2].next, &nodes[1]); - EXPECT_EQ(nodes[1].prev, &nodes[2]); - } - - // test with end only / prev, next - { - node nodes[4]; - void *end = nullptr; - - cx_linked_list_prepend(nullptr, &end, loc_prev, loc_next, &nodes[0]); - EXPECT_EQ(end, &nodes[0]); - cx_linked_list_prepend(nullptr, &end, loc_prev, loc_next, &nodes[1]); - EXPECT_EQ(end, &nodes[0]); - EXPECT_EQ(nodes[1].next, &nodes[0]); - EXPECT_EQ(nodes[0].prev, &nodes[1]); - - cx_linked_list_prepend(nullptr, &end, loc_prev, loc_next, &nodes[2]); - EXPECT_EQ(end, &nodes[0]); - EXPECT_EQ(nodes[2].next, &nodes[1]); - EXPECT_EQ(nodes[1].prev, &nodes[2]); - } - - // test with begin, end / next - { - node nodes[4]; - void *begin = nullptr, *end = nullptr; - - cx_linked_list_prepend(&begin, &end, -1, loc_next, &nodes[0]); - EXPECT_EQ(begin, &nodes[0]); - EXPECT_EQ(end, &nodes[0]); - cx_linked_list_prepend(&begin, &end, -1, loc_next, &nodes[1]); - cx_linked_list_prepend(&begin, &end, -1, loc_next, &nodes[2]); - EXPECT_EQ(begin, &nodes[2]); - EXPECT_EQ(end, &nodes[0]); - EXPECT_EQ(nodes[1].next, &nodes[0]); - EXPECT_EQ(nodes[2].next, &nodes[1]); - EXPECT_EQ(nodes[1].prev, nullptr); - EXPECT_EQ(nodes[0].prev, nullptr); - } -} - -TEST(LinkedList_LowLevel, cx_linked_list_insert) { - // insert mid list - { - node nodes[4]; - void *begin = &nodes[0], *end = &nodes[2]; - - cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); - cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); - - cx_linked_list_insert(&begin, &end, loc_prev, loc_next, &nodes[1], &nodes[3]); - EXPECT_EQ(begin, &nodes[0]); - EXPECT_EQ(end, &nodes[2]); - EXPECT_EQ(nodes[1].next, &nodes[3]); - EXPECT_EQ(nodes[2].prev, &nodes[3]); - EXPECT_EQ(nodes[3].prev, &nodes[1]); - EXPECT_EQ(nodes[3].next, &nodes[2]); - } - - // insert end - { - node nodes[4]; - void *begin = &nodes[0], *end = &nodes[2]; - - cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); - cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); - - cx_linked_list_insert(&begin, &end, loc_prev, loc_next, &nodes[2], &nodes[3]); - EXPECT_EQ(begin, &nodes[0]); - EXPECT_EQ(end, &nodes[3]); - EXPECT_EQ(nodes[2].next, &nodes[3]); - EXPECT_EQ(nodes[3].prev, &nodes[2]); - EXPECT_EQ(nodes[3].next, nullptr); - } - - // insert begin - { - node nodes[4]; - void *begin = &nodes[0], *end = &nodes[2]; - - cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); - cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); - - cx_linked_list_insert(&begin, &end, loc_prev, loc_next, nullptr, &nodes[3]); - EXPECT_EQ(begin, &nodes[3]); - EXPECT_EQ(end, &nodes[2]); - EXPECT_EQ(nodes[0].prev, &nodes[3]); - EXPECT_EQ(nodes[3].prev, nullptr); - EXPECT_EQ(nodes[3].next, &nodes[0]); - } -} - -TEST(LinkedList_LowLevel, cx_linked_list_insert_chain) { - // insert mid list - { - node nodes[5]; - void *begin = &nodes[0], *end = &nodes[2]; - - cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); - cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); - cx_linked_list_link(&nodes[3], &nodes[4], loc_prev, loc_next); - - cx_linked_list_insert_chain(&begin, &end, loc_prev, loc_next, &nodes[1], &nodes[3], nullptr); - EXPECT_EQ(begin, &nodes[0]); - EXPECT_EQ(end, &nodes[2]); - EXPECT_EQ(nodes[1].next, &nodes[3]); - EXPECT_EQ(nodes[2].prev, &nodes[4]); - EXPECT_EQ(nodes[3].prev, &nodes[1]); - EXPECT_EQ(nodes[4].next, &nodes[2]); - } - - // insert end - { - node nodes[5]; - void *begin = &nodes[0], *end = &nodes[2]; - - cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); - cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); - cx_linked_list_link(&nodes[3], &nodes[4], loc_prev, loc_next); - - cx_linked_list_insert_chain(&begin, &end, loc_prev, loc_next, &nodes[2], &nodes[3], nullptr); - EXPECT_EQ(begin, &nodes[0]); - EXPECT_EQ(end, &nodes[4]); - EXPECT_EQ(nodes[2].next, &nodes[3]); - EXPECT_EQ(nodes[3].prev, &nodes[2]); - EXPECT_EQ(nodes[4].next, nullptr); - } - - // insert begin - { - node nodes[5]; - void *begin = &nodes[0], *end = &nodes[2]; - - cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); - cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); - cx_linked_list_link(&nodes[3], &nodes[4], loc_prev, loc_next); - - cx_linked_list_insert_chain(&begin, &end, loc_prev, loc_next, nullptr, &nodes[3], nullptr); - EXPECT_EQ(begin, &nodes[3]); - EXPECT_EQ(end, &nodes[2]); - EXPECT_EQ(nodes[0].prev, &nodes[4]); - EXPECT_EQ(nodes[3].prev, nullptr); - EXPECT_EQ(nodes[4].next, &nodes[0]); - } -} - -TEST(LinkedList_LowLevel, cx_linked_list_first) { - auto testdata = create_nodes_test_data(3); - auto begin = testdata.begin; - EXPECT_EQ(cx_linked_list_first(begin, loc_prev), begin); - EXPECT_EQ(cx_linked_list_first(begin->next, loc_prev), begin); - EXPECT_EQ(cx_linked_list_first(begin->next->next, loc_prev), begin); -} - -TEST(LinkedList_LowLevel, cx_linked_list_last) { - auto testdata = create_nodes_test_data(3); - auto begin = testdata.begin; - auto end = begin->next->next; - EXPECT_EQ(cx_linked_list_last(begin, loc_next), end); - EXPECT_EQ(cx_linked_list_last(begin->next, loc_next), end); - EXPECT_EQ(cx_linked_list_last(begin->next->next, loc_next), end); -} - -TEST(LinkedList_LowLevel, cx_linked_list_prev) { - auto testdata = create_nodes_test_data(3); - auto begin = testdata.begin; - EXPECT_EQ(cx_linked_list_prev(begin, loc_next, begin), nullptr); - EXPECT_EQ(cx_linked_list_prev(begin, loc_next, begin->next), begin); - EXPECT_EQ(cx_linked_list_prev(begin, loc_next, begin->next->next), begin->next); -} - -TEST(LinkedList_LowLevel, cx_linked_list_remove) { - auto testdata = create_nodes_test_data({2, 4, 6}); - auto begin = reinterpret_cast(testdata.begin); - auto first = testdata.begin; - auto second = first->next; - auto third = second->next; - auto end = reinterpret_cast(third); - - cx_linked_list_remove(&begin, &end, loc_prev, loc_next, second); - EXPECT_EQ(begin, first); - EXPECT_EQ(end, third); - EXPECT_EQ(first->prev, nullptr); - EXPECT_EQ(first->next, third); - EXPECT_EQ(third->prev, first); - EXPECT_EQ(third->next, nullptr); - - cx_linked_list_remove(&begin, &end, loc_prev, loc_next, third); - EXPECT_EQ(begin, first); - EXPECT_EQ(end, first); - EXPECT_EQ(first->prev, nullptr); - EXPECT_EQ(first->next, nullptr); - - cx_linked_list_remove(&begin, &end, loc_prev, loc_next, first); - EXPECT_EQ(begin, nullptr); - EXPECT_EQ(end, nullptr); -} - -TEST(LinkedList_LowLevel, cx_linked_list_size) { - EXPECT_EQ(cx_linked_list_size(nullptr, loc_next), 0); - - { - auto testdata = create_nodes_test_data(5); - EXPECT_EQ(cx_linked_list_size(testdata.begin, loc_next), 5); - } - - { - auto testdata = create_nodes_test_data(13); - EXPECT_EQ(cx_linked_list_size(testdata.begin, loc_next), 13); - } -} - -TEST(LinkedList_LowLevel, cx_linked_list_sort) { - int_test_data<1500> testdata; - std::array sorted{}; - std::partial_sort_copy(testdata.data.begin(), testdata.data.end(), sorted.begin(), sorted.end()); - - auto scrambled = create_nodes_test_data(testdata.data.begin(), testdata.data.end()); - void *begin = scrambled.begin; - void *end = cx_linked_list_last(begin, loc_next); - - cx_linked_list_sort(&begin, &end, loc_prev, loc_next, loc_data, cx_cmp_int); - - node *check = reinterpret_cast(begin); - node *check_last = nullptr; - cx_for_n (i, sorted.size()) { - EXPECT_EQ(check->data, sorted[i]); - EXPECT_EQ(check->prev, check_last); - if (i < sorted.size() - 1) { - ASSERT_NE(check->next, nullptr); - } - check_last = check; - check = check->next; - } - EXPECT_EQ(check, nullptr); - EXPECT_EQ(end, check_last); -} - -TEST(LinkedList_LowLevel, cx_linked_list_reverse) { - auto testdata = create_nodes_test_data({2, 4, 6, 8}); - auto expected = create_nodes_test_data({8, 6, 4, 2}); - - auto begin = reinterpret_cast(testdata.begin); - auto end = cx_linked_list_last(begin, loc_next); - auto orig_begin = begin, orig_end = end; - - cx_linked_list_reverse(&begin, &end, loc_prev, loc_next); - EXPECT_EQ(end, orig_begin); - EXPECT_EQ(begin, orig_end); - EXPECT_EQ(cx_linked_list_compare(begin, expected.begin, loc_next, loc_data, cx_cmp_int), 0); -} - -class HighLevelTest : public ::testing::Test { - mutable std::unordered_set lists; -protected: - CxTestingAllocator testingAllocator; - - void TearDown() override { - for (auto &&l: lists) cxListDestroy(l); - EXPECT_TRUE(testingAllocator.verify()); - } - - static constexpr size_t testdata_len = 250; - int_test_data testdata; - - auto autofree(CxList *list) const -> CxList * { - if (list != nullptr) lists.insert(list); - return list; - } - - auto linkedListFromTestData() const -> CxList * { - auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int))); - cxListAddArray(list, testdata.data.data(), testdata_len); - return list; - } - - auto pointerLinkedListFromTestData() const -> CxList * { - auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *))); - cxListStorePointers(list); - // note: cannot use cxListAddArray() because we don't have a list of pointers - cx_for_n(i, testdata_len) cxListAdd(list, &testdata.data[i]); - return list; - } - - auto arrayListFromTestData() const -> CxList * { - auto list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), testdata_len)); - cxListAddArray(list, testdata.data.data(), testdata_len); - return list; - } - - void verifyCreate(CxList *list) const { - EXPECT_EQ(list->content_destructor_type, CX_DESTRUCTOR_NONE); - EXPECT_EQ(list->size, 0); - EXPECT_EQ(list->allocator, &testingAllocator); - EXPECT_EQ(list->cmpfunc, cx_cmp_int); - } - - void verifyAdd( - CxList *list, - bool as_pointer - ) { - auto len = testdata_len; - cx_for_n (i, len) EXPECT_EQ(cxListAdd(list, &testdata.data[i]), 0); - EXPECT_EQ(list->size, len); - EXPECT_GE(list->capacity, list->size); - cx_for_n (i, len) EXPECT_EQ(*(int *) cxListAt(list, i), testdata.data[i]); - cx_for_n (i, len) ++testdata.data[i]; - if (as_pointer) { - cx_for_n (i, len) EXPECT_EQ(*(int *) cxListAt(list, i), testdata.data[i]); - } else { - cx_for_n (i, len) EXPECT_EQ(*(int *) cxListAt(list, i), testdata.data[i] - 1); - } - } - - static void verifyInsert(CxList *list) { - int a = 5, b = 47, c = 13, d = 42; - - EXPECT_NE(cxListInsert(list, 1, &a), 0); - EXPECT_EQ(list->size, 0); - EXPECT_EQ(cxListInsert(list, 0, &a), 0); - EXPECT_EQ(list->size, 1); - EXPECT_EQ(cxListInsert(list, 0, &b), 0); - EXPECT_EQ(list->size, 2); - EXPECT_EQ(cxListInsert(list, 1, &c), 0); - EXPECT_EQ(list->size, 3); - EXPECT_EQ(cxListInsert(list, 3, &d), 0); - - ASSERT_EQ(list->size, 4); - EXPECT_GE(list->capacity, list->size); - - EXPECT_EQ(*(int *) cxListAt(list, 0), 47); - EXPECT_EQ(*(int *) cxListAt(list, 1), 13); - EXPECT_EQ(*(int *) cxListAt(list, 2), 5); - EXPECT_EQ(*(int *) cxListAt(list, 3), 42); - } - - static void verifyInsertArray( - CxList *list, - bool pointers = false - ) { - int a[5] = {5, 47, 11, 13, 42}; - int b[5] = {9, 18, 72, 50, 7}; - int *aptr[5]; - int *bptr[5]; - cx_for_n(i, 5) { - aptr[i] = &a[i]; - bptr[i] = &b[i]; - } - - size_t inserted; - - if (pointers) { - inserted = cxListInsertArray(list, 0, aptr, 5); - } else { - inserted = cxListInsertArray(list, 0, a, 5); - } - EXPECT_EQ(inserted, 5); - EXPECT_EQ(*(int *) cxListAt(list, 0), 5); - EXPECT_EQ(*(int *) cxListAt(list, 1), 47); - EXPECT_EQ(*(int *) cxListAt(list, 2), 11); - EXPECT_EQ(*(int *) cxListAt(list, 3), 13); - EXPECT_EQ(*(int *) cxListAt(list, 4), 42); - if (pointers) { - inserted = cxListInsertArray(list, 3, bptr, 5); - } else { - inserted = cxListInsertArray(list, 3, b, 5); - } - EXPECT_EQ(inserted, 5); - EXPECT_EQ(*(int *) cxListAt(list, 0), 5); - EXPECT_EQ(*(int *) cxListAt(list, 1), 47); - EXPECT_EQ(*(int *) cxListAt(list, 2), 11); - EXPECT_EQ(*(int *) cxListAt(list, 3), 9); - EXPECT_EQ(*(int *) cxListAt(list, 4), 18); - EXPECT_EQ(*(int *) cxListAt(list, 5), 72); - EXPECT_EQ(*(int *) cxListAt(list, 6), 50); - EXPECT_EQ(*(int *) cxListAt(list, 7), 7); - EXPECT_EQ(*(int *) cxListAt(list, 8), 13); - EXPECT_EQ(*(int *) cxListAt(list, 9), 42); - } - - void verifyRemove(CxList *list) const { - EXPECT_EQ(cxListRemove(list, 2), 0); - EXPECT_EQ(cxListRemove(list, 4), 0); - EXPECT_EQ(list->size, testdata_len - 2); - EXPECT_GE(list->capacity, list->size); - EXPECT_EQ(*(int *) cxListAt(list, 0), testdata.data[0]); - EXPECT_EQ(*(int *) cxListAt(list, 1), testdata.data[1]); - EXPECT_EQ(*(int *) cxListAt(list, 2), testdata.data[3]); - EXPECT_EQ(*(int *) cxListAt(list, 3), testdata.data[4]); - EXPECT_EQ(*(int *) cxListAt(list, 4), testdata.data[6]); - - EXPECT_EQ(cxListRemove(list, 0), 0); - EXPECT_EQ(list->size, testdata_len - 3); - EXPECT_GE(list->capacity, list->size); - EXPECT_EQ(*(int *) cxListAt(list, 0), testdata.data[1]); - EXPECT_EQ(*(int *) cxListAt(list, 1), testdata.data[3]); - - EXPECT_NE(cxListRemove(list, testdata_len), 0); - } - - void verifyAt(CxList *list) const { - auto len = testdata_len; - EXPECT_EQ(list->size, len); - cx_for_n (i, len) { - EXPECT_EQ(*(int *) cxListAt(list, i), testdata.data[i]); - } - EXPECT_EQ(cxListAt(list, list->size), nullptr); - } - - void verifyFind(CxList *list) const { - cx_for_n (attempt, 25) { - size_t exp = rand() % testdata_len; // NOLINT(cert-msc50-cpp) - int val = testdata.data[exp]; - // randomly picked number could occur earlier in list - find first position - cx_for_n (i, exp) { - if (testdata.data[i] == val) { - exp = i; - break; - } - } - EXPECT_EQ(cxListFind(list, &val), exp); - } - } - - void verifySort(CxList *list) const { - std::array expected{}; - std::partial_sort_copy(testdata.data.begin(), testdata.data.end(), expected.begin(), expected.end()); - cxListSort(list); - cx_for_n (i, testdata_len) ASSERT_EQ(*(int *) cxListAt(list, i), expected[i]); - } - - void verifyIterator(CxList *list) const { - int i = 0; - auto iter = cxListBeginMut(list); - cx_foreach(int*, x, iter) { - ASSERT_EQ(iter.index, (size_t) (i + 1) / 2); - ASSERT_EQ(*x, testdata.data[i]); - if (i % 2 == 1) cxIteratorFlagRemoval(iter); - i++; - } - auto len = testdata_len; - EXPECT_EQ(i, len); - ASSERT_EQ(list->size, len / 2); - cx_for_n(j, len / 2) ASSERT_EQ(*(int *) cxListAt(list, j), testdata.data[j * 2]); - } - - static void verifyInsertViaIterator(CxList *list) { - int newdata[] = {10, 20, 30, 40, 50}; - - auto iter = cxListMutIterator(list, 2); - EXPECT_TRUE(cxIteratorValid(iter)); - EXPECT_EQ(iter.index, 2); - EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 2); - cxListInsertAfter(&iter, &newdata[0]); - EXPECT_TRUE(cxIteratorValid(iter)); - EXPECT_EQ(iter.index, 2); - EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 2); - cxListInsertBefore(&iter, &newdata[1]); - EXPECT_TRUE(cxIteratorValid(iter)); - EXPECT_EQ(iter.index, 3); - EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 2); - - iter = cxListBeginMut(list); - cxListInsertBefore(&iter, &newdata[2]); - EXPECT_TRUE(cxIteratorValid(iter)); - EXPECT_EQ(iter.index, 1); - EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 0); - iter = cxListMutIterator(list, list->size); - cxListInsertBefore(&iter, &newdata[3]); - EXPECT_FALSE(cxIteratorValid(iter)); - EXPECT_EQ(iter.index, 9); - iter = cxListMutIterator(list, list->size); - cxListInsertAfter(&iter, &newdata[4]); - EXPECT_FALSE(cxIteratorValid(iter)); - EXPECT_EQ(iter.index, 10); - - int expdata[] = {30, 0, 1, 20, 2, 10, 3, 4, 40, 50}; - cx_for_n (j, 10) EXPECT_EQ(*(int *) cxListAt(list, j), expdata[j]); - } - - void verifyReverse(CxList *list) const { - cxListReverse(list); - cx_for_n(i, testdata_len) { - ASSERT_EQ(*(int *) cxListAt(list, i), testdata.data[testdata_len - 1 - i]); - } - } - - static void verifyCompare( - CxList *left, - CxList *right - ) { - EXPECT_EQ(cxListCompare(left, right), 0); - int x = 42; - cxListAdd(left, &x); - ASSERT_GT(left->size, right->size); - EXPECT_GT(cxListCompare(left, right), 0); - EXPECT_LT(cxListCompare(right, left), 0); - cxListAdd(right, &x); - ASSERT_EQ(left->size, right->size); - EXPECT_EQ(cxListCompare(left, right), 0); - int a = 5, b = 10; - cxListInsert(left, 15, &a); - cxListInsert(right, 15, &b); - ASSERT_EQ(left->size, right->size); - EXPECT_LT(cxListCompare(left, right), 0); - EXPECT_GT(cxListCompare(right, left), 0); - *(int *) cxListAt(left, 15) = 10; - EXPECT_EQ(cxListCompare(left, right), 0); - } -}; - -class LinkedList : public HighLevelTest { -}; - -class PointerLinkedList : public HighLevelTest { -}; - -class ArrayList : public HighLevelTest { -}; - -TEST_F(PointerLinkedList, cxListStorePointers) { - auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, 47)); - EXPECT_FALSE(cxListIsStoringPointers(list)); - cxListStorePointers(list); - EXPECT_EQ(list->itemsize, sizeof(void *)); - EXPECT_NE(list->cl, nullptr); - EXPECT_NE(list->climpl, nullptr); - EXPECT_TRUE(cxListIsStoringPointers(list)); - cxListStoreObjects(list); - EXPECT_NE(list->cl, nullptr); - EXPECT_EQ(list->climpl, nullptr); - EXPECT_FALSE(cxListIsStoringPointers(list)); -} - -TEST_F(LinkedList, cxLinkedListCreate) { - CxList *list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int))); - ASSERT_NE(list, nullptr); - EXPECT_EQ(list->itemsize, sizeof(int)); - EXPECT_EQ(list->capacity, (size_t) -1); - verifyCreate(list); -} - -TEST_F(ArrayList, cxArrayListCreate) { - CxList *list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 8)); - ASSERT_NE(list, nullptr); - EXPECT_EQ(list->itemsize, sizeof(int)); - EXPECT_EQ(list->capacity, 8); - verifyCreate(list); -} - -TEST_F(LinkedList, cxListAdd) { - auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int))); - verifyAdd(list, false); -} - -TEST_F(PointerLinkedList, cxListAdd) { - auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *))); - cxListStorePointers(list); - verifyAdd(list, true); -} - -TEST_F(ArrayList, cxListAdd) { - auto list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 8)); - verifyAdd(list, false); -} - -TEST_F(LinkedList, cxListInsert) { - verifyInsert(autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int)))); -} - -TEST_F(PointerLinkedList, cxListInsert) { - auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *))); - cxListStorePointers(list); - verifyInsert(list); -} - -TEST_F(ArrayList, cxListInsert) { - verifyInsert(autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 2))); -} - -TEST_F(LinkedList, cxListInsertArray) { - verifyInsertArray(autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int)))); -} - -TEST_F(PointerLinkedList, cxListInsertArray) { - auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *))); - cxListStorePointers(list); - verifyInsertArray(list, true); -} - -TEST_F(ArrayList, cxListInsertArray) { - verifyInsertArray(autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 4))); -} - -TEST_F(LinkedList, cxListRemove) { - verifyRemove(linkedListFromTestData()); -} - -TEST_F(PointerLinkedList, cxListRemove) { - verifyRemove(pointerLinkedListFromTestData()); -} - -TEST_F(ArrayList, cxListRemove) { - verifyRemove(arrayListFromTestData()); -} - -TEST_F(LinkedList, cxListAt) { - verifyAt(linkedListFromTestData()); -} - -TEST_F(PointerLinkedList, cxListAt) { - verifyAt(pointerLinkedListFromTestData()); -} - -TEST_F(ArrayList, cxListAt) { - verifyAt(arrayListFromTestData()); -} - -TEST_F(LinkedList, cxListFind) { - verifyFind(linkedListFromTestData()); -} - -TEST_F(PointerLinkedList, cxListFind) { - verifyFind(pointerLinkedListFromTestData()); -} - -TEST_F(ArrayList, cxListFind) { - verifyFind(arrayListFromTestData()); -} - -TEST_F(LinkedList, cxListSort) { - verifySort(linkedListFromTestData()); -} - -TEST_F(PointerLinkedList, cxListSort) { - verifySort(pointerLinkedListFromTestData()); -} - -TEST_F(ArrayList, cxListSort) { - verifySort(arrayListFromTestData()); -} - -TEST_F(LinkedList, Iterator) { - verifyIterator(linkedListFromTestData()); -} - -TEST_F(PointerLinkedList, Iterator) { - verifyIterator(pointerLinkedListFromTestData()); -} - -TEST_F(ArrayList, Iterator) { - verifyIterator(arrayListFromTestData()); -} - -TEST_F(LinkedList, InsertViaIterator) { - int fivenums[] = {0, 1, 2, 3, 4, 5}; - CxList *list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int))); - cxListAddArray(list, fivenums, 5); - verifyInsertViaIterator(list); -} - -TEST_F(PointerLinkedList, InsertViaIterator) { - int fivenums[] = {0, 1, 2, 3, 4, 5}; - auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *))); - cxListStorePointers(list); - // note: cannot use cxListAddArray() because we don't have a list of pointers - cx_for_n(i, 5) cxListAdd(list, &fivenums[i]); - verifyInsertViaIterator(list); -} - -TEST_F(ArrayList, InsertViaIterator) { - int fivenums[] = {0, 1, 2, 3, 4, 5}; - CxList *list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 4)); - cxListAddArray(list, fivenums, 5); - verifyInsertViaIterator(list); -} - -TEST_F(LinkedList, cxListReverse) { - verifyReverse(linkedListFromTestData()); -} - -TEST_F(PointerLinkedList, cxListReverse) { - verifyReverse(pointerLinkedListFromTestData()); -} - -TEST_F(ArrayList, cxListReverse) { - verifyReverse(arrayListFromTestData()); -} - -TEST_F(LinkedList, cxListCompare) { - auto left = linkedListFromTestData(); - auto right = linkedListFromTestData(); - verifyCompare(left, right); -} - -TEST_F(LinkedList, cxListCompareWithPtrList) { - auto left = linkedListFromTestData(); - auto right = pointerLinkedListFromTestData(); - verifyCompare(left, right); -} - -TEST_F(LinkedList, cxListCompareWithArrayList) { - auto left = linkedListFromTestData(); - auto right = arrayListFromTestData(); - verifyCompare(left, right); -} - -TEST_F(PointerLinkedList, cxListCompare) { - auto left = pointerLinkedListFromTestData(); - auto right = pointerLinkedListFromTestData(); - verifyCompare(left, right); -} - -TEST_F(PointerLinkedList, cxListCompareWithNormalList) { - auto left = pointerLinkedListFromTestData(); - auto right = linkedListFromTestData(); - verifyCompare(left, right); -} - -TEST_F(PointerLinkedList, cxListCompareWithArrayList) { - auto left = pointerLinkedListFromTestData(); - auto right = arrayListFromTestData(); - verifyCompare(left, right); -} - -TEST_F(ArrayList, cxListCompare) { - auto left = arrayListFromTestData(); - auto right = arrayListFromTestData(); - verifyCompare(left, right); -} - -TEST_F(ArrayList, cxListCompareWithPtrList) { - auto left = arrayListFromTestData(); - auto right = pointerLinkedListFromTestData(); - verifyCompare(left, right); -} - -TEST_F(ArrayList, cxListCompareWithNormalList) { - auto left = arrayListFromTestData(); - auto right = linkedListFromTestData(); - verifyCompare(left, right); -} - -TEST_F(PointerLinkedList, NoDestructor) { - void *item = cxMalloc(&testingAllocator, sizeof(int)); - auto list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int *)); - cxListStorePointers(list); - cxListAdd(list, item); - ASSERT_FALSE(testingAllocator.verify()); - cxListDestroy(list); - EXPECT_FALSE(testingAllocator.verify()); - cxFree(&testingAllocator, item); - EXPECT_TRUE(testingAllocator.verify()); -} - -TEST_F(PointerLinkedList, SimpleDestructor) { - int item = 0; - auto list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int *)); - cxListStorePointers(list); - list->content_destructor_type = CX_DESTRUCTOR_SIMPLE; - list->simple_destructor = [](void *elem) { *(int *) elem = 42; }; - cxListAdd(list, &item); - cxListDestroy(list); - EXPECT_EQ(item, 42); -} - -TEST_F(PointerLinkedList, AdvancedDestructor) { - void *item = cxMalloc(&testingAllocator, sizeof(int)); - auto list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int *)); - cxListStorePointers(list); - list->content_destructor_type = CX_DESTRUCTOR_ADVANCED; - list->advanced_destructor.data = &testingAllocator; - list->advanced_destructor.func = (cx_destructor_func2) cxFree; - cxListAdd(list, item); - ASSERT_FALSE(testingAllocator.verify()); - cxListDestroy(list); - EXPECT_TRUE(testingAllocator.verify()); -} diff -r bf817b825ed2 -r e081643aae2a test/test_map.cpp --- a/test/test_map.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,272 +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/hash_map.h" -#include "cx/utils.h" -#include "util_allocator.h" - -#include -#include -#include - -struct map_operation { - enum { - put, rm - } op; - char const *key; - char const *value; -}; - -auto generate_map_operations() -> std::vector { - return { - {map_operation::put, "key 1", "test"}, - {map_operation::put, "key 2", "blub"}, - {map_operation::put, "key 3", "hallo"}, - {map_operation::put, "key 2", "foobar"}, - {map_operation::put, "key 4", "value 4"}, - {map_operation::put, "key 5", "value 5"}, - {map_operation::put, "key 6", "value 6"}, - {map_operation::rm, "key 4", nullptr}, - {map_operation::put, "key 7", "value 7"}, - {map_operation::put, "key 8", "value 8"}, - {map_operation::rm, "does not exist", nullptr}, - {map_operation::put, "key 9", "value 9"}, - {map_operation::put, "key 6", "other value"}, - {map_operation::put, "key 7", "something else"}, - {map_operation::rm, "key 8", nullptr}, - {map_operation::rm, "key 2", nullptr}, - {map_operation::put, "key 8", "new value"}, - }; -} - -static void verify_map_contents( - CxMap *map, - std::unordered_map const &refmap -) { - // verify key iterator - { - auto keyiter = cxMapIteratorKeys(map); - std::unordered_set keys; - cx_foreach(CxHashKey*, elem, keyiter) { - keys.insert(std::string(elem->data.cstr, elem->len)); - } - EXPECT_EQ(keyiter.index, map->size); - ASSERT_EQ(keys.size(), map->size); - for (auto &&k: keys) { - EXPECT_NE(refmap.find(k), refmap.end()); - } - } - - // verify value iterator - { - auto valiter = cxMapIteratorValues(map); - std::unordered_set values; // we use that the values in our test data are unique strings - cx_foreach(char const*, elem, valiter) { - values.insert(std::string(elem)); - } - EXPECT_EQ(valiter.index, map->size); - ASSERT_EQ(values.size(), map->size); - for (auto &&v: values) { - EXPECT_NE(std::find_if(refmap.begin(), refmap.end(), - [v](auto const &e) { return e.second == v; }), refmap.end()); - } - } - - // verify pair iterator - { - auto pairiter = cxMapIterator(map); - std::unordered_map pairs; - cx_foreach(CxMapEntry*, entry, pairiter) { - pairs[std::string(entry->key->data.cstr, entry->key->len)] = std::string((char *) entry->value); - } - EXPECT_EQ(pairiter.index, map->size); - ASSERT_EQ(pairs.size(), refmap.size()); - for (auto &&p: pairs) { - ASSERT_EQ(p.second, refmap.at(p.first)); - } - } -} - -TEST(CxHashMap, Create) { - CxTestingAllocator allocator; - auto map = cxHashMapCreate(&allocator, 0); - auto hmap = reinterpret_cast(map); - EXPECT_GT(hmap->bucket_count, 0); - cx_for_n(i, hmap->bucket_count) { - EXPECT_EQ(hmap->buckets[i], nullptr); - } - EXPECT_EQ(map->size, 0); - EXPECT_EQ(map->allocator, &allocator); - - cxMapDestroy(map); - EXPECT_TRUE(allocator.verify()); -} - -TEST(CxHashMap, BasicOperations) { - // create the map - CxTestingAllocator allocator; - auto map = cxHashMapCreate(&allocator, 8); - - // create a reference map - std::unordered_map refmap; - - // generate operations - auto ops = generate_map_operations(); - - // verify iterators for empty map - verify_map_contents(map, refmap); - - // execute operations and verify results - for (auto &&op: ops) { - CxHashKey key = cx_hash_key_str(op.key); - key.hash = 0; // force the hash map to compute the hash - if (op.op == map_operation::put) { - // execute a put operation and verify that the exact value can be read back - refmap[std::string(op.key)] = std::string(op.value); - int result = cxMapPut(map, key, (void *) op.value); - EXPECT_EQ(result, 0); - auto added = cxMapGet(map, key); - EXPECT_EQ(memcmp(op.value, added, strlen(op.value)), 0); - } else { - // execute a remove and verify that the removed element was returned (or nullptr) - auto found = refmap.find(op.key); - auto removed = cxMapRemove(map, key); - if (found == refmap.end()) { - EXPECT_EQ(removed, nullptr); - } else { - EXPECT_EQ(std::string((char *) removed), found->second); - refmap.erase(found); - } - } - // compare the current map state with the reference map - verify_map_contents(map, refmap); - } - - // destroy the map and verify the memory (de)allocations - cxMapDestroy(map); - EXPECT_TRUE(allocator.verify()); -} - -TEST(CxHashMap, RemoveViaIterator) { - CxTestingAllocator allocator; - auto map = cxHashMapCreate(&allocator, 4); - - cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1"); - cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2"); - cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3"); - cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4"); - cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5"); - cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6"); - - auto iter = cxMapMutIterator(map); - cx_foreach(CxMapEntry*, entry, iter) { - if (entry->key->data.cstr[4] % 2 == 1) cxIteratorFlagRemoval(iter); - } - EXPECT_EQ(map->size, 3); - EXPECT_EQ(iter.index, map->size); - - EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 1")), nullptr); - EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 2")), nullptr); - EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 3")), nullptr); - EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 4")), nullptr); - EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 5")), nullptr); - EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 6")), nullptr); - - cxMapDestroy(map); - EXPECT_TRUE(allocator.verify()); -} - -TEST(CxHashMap, RehashNotRequired) { - CxTestingAllocator allocator; - auto map = cxHashMapCreate(&allocator, 8); - - cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1"); - cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2"); - cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3"); - cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4"); - cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5"); - cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6"); - - // 6/8 does not exceed 0.75, therefore the function should not rehash - int result = cxMapRehash(map); - EXPECT_EQ(result, 0); - EXPECT_EQ(reinterpret_cast(map)->bucket_count, 8); - - cxMapDestroy(map); - EXPECT_TRUE(allocator.verify()); -} - -TEST(CxHashMap, Rehash) { - CxTestingAllocator allocator; - auto map = cxHashMapCreate(&allocator, 8); - - cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1"); - cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2"); - cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3"); - cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4"); - cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5"); - cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6"); - cxMapPut(map, cx_hash_key_str("key 7"), (void *) "val 7"); - - int result = cxMapRehash(map); - EXPECT_EQ(result, 0); - EXPECT_EQ(reinterpret_cast(map)->bucket_count, 17); - EXPECT_EQ(map->size, 7); - - EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 1")), "val 1"), 0); - EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 2")), "val 2"), 0); - EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 3")), "val 3"), 0); - EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 4")), "val 4"), 0); - EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 5")), "val 5"), 0); - EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 6")), "val 6"), 0); - EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 7")), "val 7"), 0); - - cxMapDestroy(map); - EXPECT_TRUE(allocator.verify()); -} - -TEST(CxHashMap, Clear) { - CxTestingAllocator allocator; - auto map = cxHashMapCreate(&allocator, 0); - - cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1"); - cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2"); - cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3"); - - EXPECT_EQ(map->size, 3); - - cxMapClear(map); - - EXPECT_EQ(map->size, 0); - EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 1")), nullptr); - EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 2")), nullptr); - EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 3")), nullptr); - - cxMapDestroy(map); - EXPECT_TRUE(allocator.verify()); -} \ No newline at end of file diff -r bf817b825ed2 -r e081643aae2a test/test_printf.cpp --- a/test/test_printf.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,248 +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/printf.h" -#include "cx/buffer.h" - -#include -#include "util_allocator.h" - -class PrintfFixture : public ::testing::Test { -protected: - std::string buf; - CxTestingAllocator alloc; - - void TearDown() override { - buf.clear(); - ASSERT_TRUE(alloc.verify()); - } - - static size_t write_func( - void const *src, - size_t esize, - size_t ecount, - void *target - ) { - auto str = reinterpret_cast(src); - auto buf = reinterpret_cast(target); - EXPECT_EQ(esize, 1); - EXPECT_EQ(strlen(str), ecount); - *buf = str; - return ecount; - } -}; - - -TEST_F(PrintfFixture, BPrintf) { - CxBuffer buf; - cxBufferInit(&buf, nullptr, 64, &alloc, 0); - - auto r = cx_bprintf(&buf, "This %s aged %u years in a %2XSK.", "Test", 10, 0xca); - EXPECT_EQ(r, 34); - EXPECT_EQ(buf.size, 34); - buf.space[r] = '\0'; - EXPECT_STREQ(buf.space, "This Test aged 10 years in a CASK."); - - cxBufferDestroy(&buf); -} - -TEST_F(PrintfFixture, FPrintf) { - auto h = "Hello"; - size_t r; - - r = cx_fprintf(&buf, PrintfFixture::write_func, "teststring"); - EXPECT_EQ(r, 10); - EXPECT_EQ(buf, "teststring"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "[%10s]", h); - EXPECT_EQ(r, 12); - EXPECT_EQ(buf, "[ Hello]"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "[%-10s]", h); - EXPECT_EQ(r, 12); - EXPECT_EQ(buf, "[Hello ]"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "[%*s]", 10, h); - EXPECT_EQ(r, 12); - EXPECT_EQ(buf, "[ Hello]"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "[%-10.*s]", 4, h); - EXPECT_EQ(r, 12); - EXPECT_EQ(buf, "[Hell ]"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "[%-*.*s]", 10, 4, h); - EXPECT_EQ(r, 12); - EXPECT_EQ(buf, "[Hell ]"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "%c", 'A'); - EXPECT_EQ(r, 1); - EXPECT_EQ(buf, "A"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "%i %d %.6i %i %.0i %+i %i", 1, 2, 3, 0, 0, 4, -4); - EXPECT_EQ(r, 19); - EXPECT_EQ(buf, "1 2 000003 0 +4 -4"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "%x %x %X %#x", 5, 10, 10, 6); - EXPECT_EQ(r, 9); - EXPECT_EQ(buf, "5 a A 0x6"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "%o %#o %#o", 10, 10, 4); - EXPECT_EQ(r, 9); - EXPECT_EQ(buf, "12 012 04"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "%05.2f %.2f %5.2f", 1.5, 1.5, 1.5); - EXPECT_EQ(r, 16); - EXPECT_EQ(buf, "01.50 1.50 1.50"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "'%*c'", 5, 'x'); - EXPECT_EQ(r, 7); - EXPECT_EQ(buf, "' x'"); - - r = cx_fprintf(&buf, PrintfFixture::write_func, "'%*c'", -5, 'x'); - EXPECT_EQ(r, 7); - EXPECT_EQ(buf, "'x '"); -} - -TEST_F(PrintfFixture, BPrintfLargeString) { - CxBuffer buf; - cxBufferInit(&buf, nullptr, 64, &alloc, CX_BUFFER_AUTO_EXTEND); - - auto aaa = std::string(512, 'a'); - auto bbb = std::string(512, 'b'); - - auto r = cx_bprintf(&buf, "After %s comes %s.", aaa.data(), bbb.data()); - EXPECT_EQ(r, 1038); - EXPECT_EQ(buf.size, 1038); - cxBufferPut(&buf, 0); - EXPECT_EQ(buf.space, std::string("After ") + aaa + " comes " + bbb + "."); - - cxBufferDestroy(&buf); -} - -TEST_F(PrintfFixture, BPrintfNoCap) { - CxBuffer buf; - char space[20]; - memset(space, 'a', 20); - cxBufferInit(&buf, space, 16, &alloc, 0); - - auto r = cx_bprintf(&buf, "Hello %s with more than %d chars.", "string", 16); - EXPECT_EQ(r, 16); - EXPECT_EQ(buf.size, 16); - EXPECT_EQ(0, memcmp(space, "Hello string witaaaa", 20)); - - cxBufferDestroy(&buf); -} - -TEST_F(PrintfFixture, SPrintf) { - auto h = "Hello"; - - std::vector fl; - cxmutstr r; - - r = cx_asprintf_a(&alloc, "teststring"); - EXPECT_EQ(r.length, 10); - EXPECT_STREQ(r.ptr, "teststring"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "[%10s]", h); - EXPECT_EQ(r.length, 12); - EXPECT_STREQ(r.ptr, "[ Hello]"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "[%-10s]", h); - EXPECT_EQ(r.length, 12); - EXPECT_STREQ(r.ptr, "[Hello ]"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "[%*s]", 10, h); - EXPECT_EQ(r.length, 12); - EXPECT_STREQ(r.ptr, "[ Hello]"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "[%-10.*s]", 4, h); - EXPECT_EQ(r.length, 12); - EXPECT_STREQ(r.ptr, "[Hell ]"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "[%-*.*s]", 10, 4, h); - EXPECT_EQ(r.length, 12); - EXPECT_STREQ(r.ptr, "[Hell ]"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "%c", 'A'); - EXPECT_EQ(r.length, 1); - EXPECT_STREQ(r.ptr, "A"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "%i %d %.6i %i %.0i %+i %i", 1, 2, 3, 0, 0, 4, -4); - EXPECT_EQ(r.length, 19); - EXPECT_STREQ(r.ptr, "1 2 000003 0 +4 -4"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "%x %x %X %#x", 5, 10, 10, 6); - EXPECT_EQ(r.length, 9); - EXPECT_STREQ(r.ptr, "5 a A 0x6"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "%o %#o %#o", 10, 10, 4); - EXPECT_EQ(r.length, 9); - EXPECT_STREQ(r.ptr, "12 012 04"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "%05.2f %.2f %5.2f", 1.5, 1.5, 1.5); - EXPECT_EQ(r.length, 16); - EXPECT_STREQ(r.ptr, "01.50 1.50 1.50"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "'%*c'", 5, 'x'); - EXPECT_EQ(r.length, 7); - EXPECT_STREQ(r.ptr, "' x'"); - fl.push_back(r.ptr); - - r = cx_asprintf_a(&alloc, "'%*c'", -5, 'x'); - EXPECT_EQ(r.length, 7); - EXPECT_STREQ(r.ptr, "'x '"); - fl.push_back(r.ptr); - - for (auto c: fl) { - auto s = cx_mutstrn(c, 0); - cx_strfree_a(&alloc, &s); - } -} - -TEST_F(PrintfFixture, SPrintfLargeString) { - auto aaa = std::string(512, 'a'); - auto bbb = std::string(512, 'b'); - - auto r = cx_asprintf_a(&alloc, "After %s comes %s.", aaa.data(), bbb.data()); - EXPECT_EQ(r.length, 1038); - EXPECT_EQ(r.ptr, std::string("After ") + aaa + " comes " + bbb + "."); - EXPECT_EQ(r.ptr[1038], '\0'); - - cx_strfree_a(&alloc, &r); -} \ No newline at end of file diff -r bf817b825ed2 -r e081643aae2a test/test_string.cpp --- a/test/test_string.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,865 +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/string.h" -#include "util_allocator.h" - -#include - -#define EXPECT_ZERO_TERMINATED(str) EXPECT_EQ((str).ptr[(str).length], '\0') - -TEST(String, construct) { - cxstring s1 = cx_str("1234"); - cxstring s2 = cx_strn("abcd", 2); - cxmutstr s3 = cx_mutstr((char *) "1234"); - cxmutstr s4 = cx_mutstrn((char *) "abcd", 2); - - EXPECT_EQ(s1.length, 4); - EXPECT_EQ(s2.length, 2); - EXPECT_EQ(s3.length, 4); - EXPECT_EQ(s4.length, 2); -} - -TEST(String, strfree) { - CxTestingAllocator alloc; - auto test = (char *) cxMalloc(&alloc, 16); - cxmutstr str = cx_mutstrn(test, 16); - ASSERT_EQ(str.ptr, test); - EXPECT_EQ(str.length, 16); - cx_strfree_a(&alloc, &str); - EXPECT_EQ(str.ptr, nullptr); - EXPECT_EQ(str.length, 0); - EXPECT_TRUE(alloc.verify()); -} - -TEST(String, strdup) { - cxstring str = CX_STR("test"); - cxmutstr dup = cx_strdup(str); - ASSERT_EQ(dup.length, str.length); - EXPECT_STREQ(dup.ptr, str.ptr); - EXPECT_ZERO_TERMINATED(dup); - cx_strfree(&dup); - - str.length = 2; - dup = cx_strdup(str); - ASSERT_EQ(dup.length, str.length); - EXPECT_STREQ(dup.ptr, "te"); - EXPECT_ZERO_TERMINATED(dup); - cx_strfree(&dup); -} - -TEST(String, strlen) { - cxstring s1 = CX_STR("1234"); - cxstring s2 = CX_STR(".:.:."); - cxstring s3 = CX_STR("X"); - - size_t len0 = cx_strlen(0); - size_t len1 = cx_strlen(1, s1); - size_t len2 = cx_strlen(2, s1, s2); - size_t len3 = cx_strlen(3, s1, s2, s3); - - EXPECT_EQ(len0, 0); - EXPECT_EQ(len1, 4); - EXPECT_EQ(len2, 9); - EXPECT_EQ(len3, 10); -} - -TEST(String, strsubs) { - cxstring str = CX_STR("A test string"); - - cxstring sub = cx_strsubs(str, 0); - EXPECT_EQ(cx_strcmp(sub, str), 0); - - sub = cx_strsubs(str, 2); - EXPECT_EQ(cx_strcmp(sub, cx_str("test string")), 0); - - sub = cx_strsubs(str, 7); - EXPECT_EQ(cx_strcmp(sub, cx_str("string")), 0); - - sub = cx_strsubs(str, 15); - EXPECT_EQ(cx_strcmp(sub, cx_str("")), 0); - - sub = cx_strsubsl(str, 2, 4); - EXPECT_EQ(cx_strcmp(sub, cx_str("test")), 0); - - sub = cx_strsubsl(str, 7, 3); - EXPECT_EQ(cx_strcmp(sub, cx_str("str")), 0); - - sub = cx_strsubsl(str, 7, 20); - EXPECT_EQ(cx_strcmp(sub, cx_str("string")), 0); - - // just for coverage, call the _m variant - auto m = cx_strsubs_m(cx_mutstrn(nullptr, 0), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(m), cx_str("")), 0); -} - -TEST(String, strchr) { - cxstring str = CX_STR("I will find you - and I will kill you"); - - cxstring notfound = cx_strchr(str, 'x'); - EXPECT_EQ(notfound.length, 0); - - cxstring result = cx_strchr(str, 'w'); - EXPECT_EQ(result.length, 35); - EXPECT_STREQ(result.ptr, "will find you - and I will kill you"); - - // just for coverage, call the _m variant - auto m = cx_strchr_m(cx_mutstrn(nullptr, 0), 'a'); - EXPECT_EQ(cx_strcmp(cx_strcast(m), cx_str("")), 0); -} - -TEST(String, strrchr) { - cxstring str = CX_STR("I will find you - and I will kill you"); - - cxstring notfound = cx_strrchr(str, 'x'); - EXPECT_EQ(notfound.length, 0); - - cxstring result = cx_strrchr(str, 'w'); - EXPECT_EQ(result.length, 13); - EXPECT_STREQ(result.ptr, "will kill you"); - - // just for coverage, call the _m variant - auto m = cx_strrchr_m(cx_mutstrn(nullptr, 0), 'a'); - EXPECT_EQ(cx_strcmp(cx_strcast(m), cx_str("")), 0); -} - -TEST(String, strstr) { - cxstring str = CX_STR("find the match in this string"); - cxstring longstr = CX_STR( - "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl" - "mnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx" - "yzabcdeababababnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij" - "klmnopqrstuvwxyzaababababababababrstuvwxyzabcdefghijklmnopqrstuv" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "wxyz1234567890"); - cxstring longstrpattern = CX_STR( - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - ); - cxstring longstrresult = CX_STR( - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "abababababababababababababababababababababababababababababababab" - "wxyz1234567890" - ); - - cxstring notfound = cx_strstr(str, cx_str("no match")); - EXPECT_EQ(notfound.length, 0); - - cxstring result = cx_strstr(str, cx_str("match")); - EXPECT_EQ(result.length, 20); - EXPECT_STREQ(result.ptr, "match in this string"); - - result = cx_strstr(str, cx_str("")); - EXPECT_EQ(result.length, str.length); - EXPECT_STREQ(result.ptr, str.ptr); - - result = cx_strstr(longstr, longstrpattern); - EXPECT_EQ(result.length, longstrresult.length); - EXPECT_STREQ(result.ptr, longstrresult.ptr); - - // just for coverage, call the _m variant - auto mstr = cx_strdup(longstr); - auto m = cx_strstr_m(mstr, longstrpattern); - EXPECT_EQ(m.length, longstrresult.length); - EXPECT_STREQ(m.ptr, longstrresult.ptr); - cx_strfree(&mstr); -} - -TEST(String, strcmp) { - cxstring str = CX_STR("compare this"); - - EXPECT_EQ(cx_strcmp(cx_str(""), cx_str("")), 0); - EXPECT_GT(cx_strcmp(str, cx_str("")), 0); - EXPECT_EQ(cx_strcmp(str, cx_str("compare this")), 0); - EXPECT_NE(cx_strcmp(str, cx_str("Compare This")), 0); - EXPECT_LT(cx_strcmp(str, cx_str("compare tool")), 0); - EXPECT_GT(cx_strcmp(str, cx_str("compare shit")), 0); - EXPECT_LT(cx_strcmp(str, cx_str("compare this not")), 0); - EXPECT_GT(cx_strcmp(str, cx_str("compare")), 0); -} - -TEST(String, strcasecmp) { - cxstring str = CX_STR("compare this"); - - EXPECT_EQ(cx_strcasecmp(cx_str(""), cx_str("")), 0); - EXPECT_GT(cx_strcasecmp(str, cx_str("")), 0); - EXPECT_EQ(cx_strcasecmp(str, cx_str("compare this")), 0); - EXPECT_EQ(cx_strcasecmp(str, cx_str("Compare This")), 0); - EXPECT_LT(cx_strcasecmp(str, cx_str("compare tool")), 0); - EXPECT_GT(cx_strcasecmp(str, cx_str("compare shit")), 0); - EXPECT_LT(cx_strcasecmp(str, cx_str("compare this not")), 0); - EXPECT_GT(cx_strcasecmp(str, cx_str("compare")), 0); -} - -TEST(String, strcat) { - cxstring s1 = CX_STR("12"); - cxstring s2 = CX_STR("34"); - cxstring s3 = CX_STR("56"); - cxstring sn = {nullptr, 0}; - - CxTestingAllocator alloc; - - cxmutstr t1 = cx_strcat_a(&alloc, 2, s1, s2); - EXPECT_EQ(cx_strcmp(cx_strcast(t1), cx_str("1234")), 0); - EXPECT_ZERO_TERMINATED(t1); - cx_strfree_a(&alloc, &t1); - - cxmutstr t2 = cx_strcat_a(&alloc, 3, s1, s2, s3); - EXPECT_EQ(cx_strcmp(cx_strcast(t2), cx_str("123456")), 0); - EXPECT_ZERO_TERMINATED(t2); - cx_strfree_a(&alloc, &t2); - - cxmutstr t3 = cx_strcat_a(&alloc, 6, s1, sn, s2, sn, s3, sn); - EXPECT_EQ(cx_strcmp(cx_strcast(t3), cx_str("123456")), 0); - EXPECT_ZERO_TERMINATED(t3); - cx_strfree_a(&alloc, &t3); - - cxmutstr t4 = cx_strcat_a(&alloc, 2, sn, sn); - EXPECT_EQ(cx_strcmp(cx_strcast(t4), cx_str("")), 0); - EXPECT_ZERO_TERMINATED(t4); - cx_strfree_a(&alloc, &t4); - - EXPECT_TRUE(alloc.verify()); - - // use the macro - cxmutstr t5 = cx_strcat(3, s3, s1, s2); - EXPECT_EQ(cx_strcmp(cx_strcast(t5), cx_str("561234")), 0); - EXPECT_ZERO_TERMINATED(t5); - cx_strfree(&t5); -} - -TEST(String, strsplit) { - - cxstring test = cx_str("this,is,a,csv,string"); - size_t capa = 8; - cxstring list[8]; - size_t n; - - // special case: empty string - n = cx_strsplit(test, cx_str(""), capa, list); - ASSERT_EQ(n, 1); - EXPECT_EQ(cx_strcmp(list[0], test), 0); - - // no delimiter occurrence - n = cx_strsplit(test, cx_str("z"), capa, list); - ASSERT_EQ(n, 1); - EXPECT_EQ(cx_strcmp(list[0], test), 0); - - // partially matching delimiter - n = cx_strsplit(test, cx_str("is,not"), capa, list); - ASSERT_EQ(n, 1); - EXPECT_EQ(cx_strcmp(list[0], test), 0); - - // matching single-char delimiter - n = cx_strsplit(test, cx_str(","), capa, list); - ASSERT_EQ(n, 5); - EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0); - EXPECT_EQ(cx_strcmp(list[2], cx_str("a")), 0); - EXPECT_EQ(cx_strcmp(list[3], cx_str("csv")), 0); - EXPECT_EQ(cx_strcmp(list[4], cx_str("string")), 0); - - // matching multi-char delimiter - n = cx_strsplit(test, cx_str("is"), capa, list); - ASSERT_EQ(n, 3); - EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str(",")), 0); - EXPECT_EQ(cx_strcmp(list[2], cx_str(",a,csv,string")), 0); - - // bounded list using single-char delimiter - n = cx_strsplit(test, cx_str(","), 3, list); - ASSERT_EQ(n, 3); - EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0); - EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0); - - // bounded list using multi-char delimiter - n = cx_strsplit(test, cx_str("is"), 2, list); - ASSERT_EQ(n, 2); - EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0); - - // start with delimiter - n = cx_strsplit(test, cx_str("this"), capa, list); - ASSERT_EQ(n, 2); - EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0); - - // end with delimiter - n = cx_strsplit(test, cx_str("string"), capa, list); - ASSERT_EQ(n, 2); - EXPECT_EQ(cx_strcmp(list[0], cx_str("this,is,a,csv,")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); - - - // end with delimiter exceed bound - n = cx_strsplit(cx_str("a,b,c,"), cx_str(","), 3, list); - ASSERT_EQ(n, 3); - EXPECT_EQ(cx_strcmp(list[0], cx_str("a")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("b")), 0); - EXPECT_EQ(cx_strcmp(list[2], cx_str("c,")), 0); - - // exact match - n = cx_strsplit(test, cx_str("this,is,a,csv,string"), capa, list); - ASSERT_EQ(n, 2); - EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); - - // string to be split is only substring - n = cx_strsplit(test, cx_str("this,is,a,csv,string,with,extension"), capa, list); - ASSERT_EQ(n, 1); - EXPECT_EQ(cx_strcmp(list[0], test), 0); - - // subsequent encounter of delimiter (the string between is empty) - n = cx_strsplit(test, cx_str("is,"), capa, list); - ASSERT_EQ(n, 3); - EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); - EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0); - - // call the _m variant just for coverage - auto mtest = cx_strdup(test); - cxmutstr mlist[4]; - n = cx_strsplit_m(mtest, cx_str("is,"), 4, mlist); - ASSERT_EQ(n, 3); - EXPECT_EQ(cx_strcmp(cx_strcast(mlist[0]), cx_str("th")), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(mlist[1]), cx_str("")), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(mlist[2]), cx_str("a,csv,string")), 0); - cx_strfree(&mtest); -} - -TEST(String, strsplit_a) { - CxTestingAllocator alloc; - - cxstring test = cx_str("this,is,a,csv,string"); - size_t capa = 8; - cxstring *list; - size_t n; - - // special case: empty string - n = cx_strsplit_a(&alloc, test, cx_str(""), capa, &list); - ASSERT_EQ(n, 1); - EXPECT_EQ(cx_strcmp(list[0], test), 0); - cxFree(&alloc, list); - - // no delimiter occurrence - n = cx_strsplit_a(&alloc, test, cx_str("z"), capa, &list); - ASSERT_EQ(n, 1); - EXPECT_EQ(cx_strcmp(list[0], test), 0); - cxFree(&alloc, list); - - // partially matching delimiter - n = cx_strsplit_a(&alloc, test, cx_str("is,not"), capa, &list); - ASSERT_EQ(n, 1); - EXPECT_EQ(cx_strcmp(list[0], test), 0); - cxFree(&alloc, list); - - // matching single-char delimiter - n = cx_strsplit_a(&alloc, test, cx_str(","), capa, &list); - ASSERT_EQ(n, 5); - EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0); - EXPECT_EQ(cx_strcmp(list[2], cx_str("a")), 0); - EXPECT_EQ(cx_strcmp(list[3], cx_str("csv")), 0); - EXPECT_EQ(cx_strcmp(list[4], cx_str("string")), 0); - cxFree(&alloc, list); - - // matching multi-char delimiter - n = cx_strsplit_a(&alloc, test, cx_str("is"), capa, &list); - ASSERT_EQ(n, 3); - EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str(",")), 0); - EXPECT_EQ(cx_strcmp(list[2], cx_str(",a,csv,string")), 0); - cxFree(&alloc, list); - - // bounded list using single-char delimiter - n = cx_strsplit_a(&alloc, test, cx_str(","), 3, &list); - ASSERT_EQ(n, 3); - EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0); - EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0); - cxFree(&alloc, list); - - // bounded list using multi-char delimiter - n = cx_strsplit_a(&alloc, test, cx_str("is"), 2, &list); - ASSERT_EQ(n, 2); - EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0); - cxFree(&alloc, list); - - // start with delimiter - n = cx_strsplit_a(&alloc, test, cx_str("this"), capa, &list); - ASSERT_EQ(n, 2); - EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0); - cxFree(&alloc, list); - - // end with delimiter - n = cx_strsplit_a(&alloc, test, cx_str("string"), capa, &list); - ASSERT_EQ(n, 2); - EXPECT_EQ(cx_strcmp(list[0], cx_str("this,is,a,csv,")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); - cxFree(&alloc, list); - - // end with delimiter exceed bound - n = cx_strsplit_a(&alloc, cx_str("a,b,c,"), cx_str(","), 3, &list); - ASSERT_EQ(n, 3); - EXPECT_EQ(cx_strcmp(list[0], cx_str("a")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("b")), 0); - EXPECT_EQ(cx_strcmp(list[2], cx_str("c,")), 0); - cxFree(&alloc, list); - - // exact match - n = cx_strsplit_a(&alloc, test, cx_str("this,is,a,csv,string"), capa, &list); - ASSERT_EQ(n, 2); - EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); - cxFree(&alloc, list); - - // string to be split is only substring - n = cx_strsplit_a(&alloc, test, cx_str("this,is,a,csv,string,with,extension"), capa, &list); - ASSERT_EQ(n, 1); - EXPECT_EQ(cx_strcmp(list[0], test), 0); - cxFree(&alloc, list); - - // subsequent encounter of delimiter (the string between is empty) - n = cx_strsplit_a(&alloc, test, cx_str("is,"), capa, &list); - ASSERT_EQ(n, 3); - EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); - EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); - EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0); - cxFree(&alloc, list); - - // call the _m variant just for coverage - auto mtest = cx_strdup(test); - cxmutstr *mlist; - n = cx_strsplit_ma(&alloc, mtest, cx_str("is,"), 4, &mlist); - ASSERT_EQ(n, 3); - EXPECT_EQ(cx_strcmp(cx_strcast(mlist[0]), cx_str("th")), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(mlist[1]), cx_str("")), 0); - EXPECT_EQ(cx_strcmp(cx_strcast(mlist[2]), cx_str("a,csv,string")), 0); - cxFree(&alloc, mlist); - cx_strfree(&mtest); - - EXPECT_TRUE(alloc.verify()); -} - -TEST(String, strtrim) { - cxstring t1 = cx_strtrim(cx_str(" ein test \t ")); - cxstring t2 = cx_strtrim(cx_str("abc")); - cxstring t3 = cx_strtrim(cx_str(" 123")); - cxstring t4 = cx_strtrim(cx_str("xyz ")); - cxstring t5 = cx_strtrim(cx_str(" ")); - cxstring empty = cx_strtrim(cx_str("")); - - EXPECT_EQ(cx_strcmp(t1, cx_str("ein test")), 0); - EXPECT_EQ(cx_strcmp(t2, cx_str("abc")), 0); - EXPECT_EQ(cx_strcmp(t3, cx_str("123")), 0); - EXPECT_EQ(cx_strcmp(t4, cx_str("xyz")), 0); - EXPECT_EQ(cx_strcmp(t5, cx_str("")), 0); - EXPECT_EQ(cx_strcmp(empty, cx_str("")), 0); - - // call the _m variant just for coverage - cxmutstr m1 = cx_strtrim_m(cx_mutstr((char *) " ein test \t ")); - EXPECT_EQ(cx_strcmp(cx_strcast(m1), cx_str("ein test")), 0); -} - -TEST(String, strprefix) { - cxstring str = CX_STR("test my prefix and my suffix"); - cxstring empty = CX_STR(""); - EXPECT_FALSE(cx_strprefix(empty, cx_str("pref"))); - EXPECT_TRUE(cx_strprefix(str, empty)); - EXPECT_TRUE(cx_strprefix(empty, empty)); - EXPECT_TRUE(cx_strprefix(str, cx_str("test "))); - EXPECT_FALSE(cx_strprefix(str, cx_str("8-) fsck "))); -} - -TEST(String, strsuffix) { - cxstring str = CX_STR("test my prefix and my suffix"); - cxstring empty = CX_STR(""); - EXPECT_FALSE(cx_strsuffix(empty, cx_str("suf"))); - EXPECT_TRUE(cx_strsuffix(str, empty)); - EXPECT_TRUE(cx_strsuffix(empty, empty)); - EXPECT_TRUE(cx_strsuffix(str, cx_str("fix"))); - EXPECT_FALSE(cx_strsuffix(str, cx_str("fox"))); -} - -TEST(String, strcaseprefix) { - cxstring str = CX_STR("test my prefix and my suffix"); - cxstring empty = CX_STR(""); - EXPECT_FALSE(cx_strcaseprefix(empty, cx_str("pREf"))); - EXPECT_TRUE(cx_strcaseprefix(str, empty)); - EXPECT_TRUE(cx_strcaseprefix(empty, empty)); - EXPECT_TRUE(cx_strcaseprefix(str, cx_str("TEST "))); - EXPECT_FALSE(cx_strcaseprefix(str, cx_str("8-) fsck "))); -} - -TEST(String, strcasesuffix) { - cxstring str = CX_STR("test my prefix and my suffix"); - cxstring empty = CX_STR(""); - EXPECT_FALSE(cx_strcasesuffix(empty, cx_str("sUf"))); - EXPECT_TRUE(cx_strcasesuffix(str, empty)); - EXPECT_TRUE(cx_strcasesuffix(empty, empty)); - EXPECT_TRUE(cx_strcasesuffix(str, cx_str("FIX"))); - EXPECT_FALSE(cx_strcasesuffix(str, cx_str("fox"))); -} - -TEST(String, strreplace) { - CxTestingAllocator alloc; - cxstring str = CX_STR("test ababab string aba"); - cxstring longstr = CX_STR( - "xyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacd"); - cxstring notrail = CX_STR("test abab"); - cxstring empty = CX_STR(""); - cxstring astr = CX_STR("aaaaaaaaaa"); - cxstring csstr = CX_STR("test AB ab TEST xyz"); - - cxmutstr repl = cx_strreplace(str, cx_str("abab"), cx_str("muchlonger")); - auto expected = "test muchlongerab string aba"; - - cxmutstr repln = cx_strreplacen(str, cx_str("ab"), cx_str("c"), 2); - auto expectedn = "test ccab string aba"; - - cxmutstr longrepl = cx_strreplace(longstr, cx_str("a"), cx_str("z")); - auto longexpect = "xyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzcd"; - - cxmutstr replnotrail = cx_strreplace(notrail, cx_str("ab"), cx_str("z")); - auto notrailexpect = "test zz"; - - cxmutstr repleq = cx_strreplace(str, str, cx_str("hello")); - auto eqexpect = "hello"; - - cxmutstr replempty1 = cx_strreplace(empty, cx_str("ab"), cx_str("c")); // expect: empty - cxmutstr replempty2 = cx_strreplace(str, cx_str("abab"), empty); - auto emptyexpect2 = "test ab string aba"; - - cxmutstr replpre = cx_strreplace(str, cx_str("test "), cx_str("TEST ")); - auto preexpected = "TEST ababab string aba"; - - cxmutstr replan1 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 1); - auto an1expected = "xaaaaaaaaa"; - - cxmutstr replan4 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 4); - auto an4expected = "xxxxaaaaaa"; - - cxmutstr replan9 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 9); - auto an9expected = "xxxxxxxxxa"; - - cxmutstr replan10 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 10); - auto an10expected = "xxxxxxxxxx"; - - cxmutstr repl1_a = cx_strreplace_a(&alloc, csstr, cx_str("AB"), cx_str("*")); - auto expeced1_a = "test * ab TEST xyz"; - - cxmutstr repl2_a = cx_strreplace_a(&alloc, csstr, cx_str("test"), cx_str("TEST")); - auto expected2_a = "TEST AB ab TEST xyz"; - - - EXPECT_NE(repl.ptr, str.ptr); - EXPECT_ZERO_TERMINATED(repl); - EXPECT_STREQ(repl.ptr, expected); - EXPECT_ZERO_TERMINATED(repln); - EXPECT_STREQ(repln.ptr, expectedn); - EXPECT_ZERO_TERMINATED(longrepl); - EXPECT_STREQ(longrepl.ptr, longexpect); - EXPECT_ZERO_TERMINATED(replnotrail); - EXPECT_STREQ(replnotrail.ptr, notrailexpect); - EXPECT_ZERO_TERMINATED(repleq); - EXPECT_STREQ(repleq.ptr, eqexpect); - EXPECT_ZERO_TERMINATED(replempty1); - EXPECT_STREQ(replempty1.ptr, ""); - EXPECT_ZERO_TERMINATED(replempty2); - EXPECT_STREQ(replempty2.ptr, emptyexpect2); - EXPECT_ZERO_TERMINATED(replpre); - EXPECT_STREQ(replpre.ptr, preexpected); - EXPECT_ZERO_TERMINATED(replan1); - EXPECT_STREQ(replan1.ptr, an1expected); - EXPECT_ZERO_TERMINATED(replan4); - EXPECT_STREQ(replan4.ptr, an4expected); - EXPECT_ZERO_TERMINATED(replan9); - EXPECT_STREQ(replan9.ptr, an9expected); - EXPECT_ZERO_TERMINATED(replan10); - EXPECT_STREQ(replan10.ptr, an10expected); - EXPECT_ZERO_TERMINATED(repl1_a); - EXPECT_STREQ(repl1_a.ptr, expeced1_a); - EXPECT_ZERO_TERMINATED(repl2_a); - EXPECT_STREQ(repl2_a.ptr, expected2_a); - - cx_strfree(&repl); - cx_strfree(&repln); - cx_strfree(&longrepl); - cx_strfree(&replnotrail); - cx_strfree(&repleq); - cx_strfree(&replempty1); - cx_strfree(&replempty2); - cx_strfree(&replpre); - cx_strfree(&replan1); - cx_strfree(&replan4); - cx_strfree(&replan9); - cx_strfree(&replan10); - - cx_strfree_a(&alloc, &repl1_a); - cx_strfree_a(&alloc, &repl2_a); - EXPECT_TRUE(alloc.verify()); -} - -TEST(String, strupper) { - cxmutstr str = cx_strdup(cx_str("thIs 1s @ Te$t")); - cx_strupper(str); - EXPECT_STREQ(str.ptr, "THIS 1S @ TE$T"); - cx_strfree(&str); -} - -TEST(String, strlower) { - cxmutstr str = cx_strdup(cx_str("thIs 1s @ Te$t")); - cx_strlower(str); - EXPECT_STREQ(str.ptr, "this 1s @ te$t"); - cx_strfree(&str); -} - -TEST(String, strtok) { - cxstring str = cx_str("a,comma,separated,string"); - cxstring delim = cx_str(","); - CxStrtokCtx ctx = cx_strtok(str, delim, 3); - EXPECT_EQ(ctx.str.ptr, str.ptr); - EXPECT_EQ(ctx.str.length, str.length); - EXPECT_EQ(ctx.delim.ptr, delim.ptr); - EXPECT_EQ(ctx.delim.length, delim.length); - EXPECT_EQ(ctx.limit, 3); - EXPECT_EQ(ctx.found, 0); - EXPECT_EQ(ctx.pos, 0); - EXPECT_EQ(ctx.next_pos, 0); - EXPECT_EQ(ctx.delim_more, nullptr); - EXPECT_EQ(ctx.delim_more_count, 0); -} - -TEST(String, strtok_m) { - cxmutstr str = cx_strdup(cx_str("a,comma,separated,string")); - cxstring delim = cx_str(","); - CxStrtokCtx ctx = cx_strtok_m(str, delim, 3); - EXPECT_EQ(ctx.str.ptr, str.ptr); - EXPECT_EQ(ctx.str.length, str.length); - EXPECT_EQ(ctx.delim.ptr, delim.ptr); - EXPECT_EQ(ctx.delim.length, delim.length); - EXPECT_EQ(ctx.limit, 3); - EXPECT_EQ(ctx.found, 0); - EXPECT_EQ(ctx.pos, 0); - EXPECT_EQ(ctx.next_pos, 0); - EXPECT_EQ(ctx.delim_more, nullptr); - EXPECT_EQ(ctx.delim_more_count, 0); - cx_strfree(&str); -} - -TEST(String, strtok_delim) { - cxstring str = cx_str("an,arbitrarily|separated;string"); - cxstring delim = cx_str(","); - cxstring delim_more[2] = {CX_STR("|"), CX_STR(";")}; - CxStrtokCtx ctx = cx_strtok(str, delim, 3); - cx_strtok_delim(&ctx, delim_more, 2); - EXPECT_EQ(ctx.str.ptr, str.ptr); - EXPECT_EQ(ctx.str.length, str.length); - EXPECT_EQ(ctx.delim.ptr, delim.ptr); - EXPECT_EQ(ctx.delim.length, delim.length); - EXPECT_EQ(ctx.limit, 3); - EXPECT_EQ(ctx.found, 0); - EXPECT_EQ(ctx.pos, 0); - EXPECT_EQ(ctx.next_pos, 0); - EXPECT_EQ(ctx.delim_more, delim_more); - EXPECT_EQ(ctx.delim_more_count, 2); -} - -TEST(String, strtok_next_easy) { - cxstring str = cx_str("a,comma,separated,string"); - cxstring delim = cx_str(","); - CxStrtokCtx ctx = cx_strtok(str, delim, 3); - bool ret; - cxstring tok; - - ret = cx_strtok_next(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(tok, cx_str("a")), 0); - EXPECT_EQ(ctx.pos, 0); - EXPECT_EQ(ctx.next_pos, 2); - EXPECT_EQ(ctx.delim_pos, 1); - EXPECT_EQ(ctx.found, 1); - - ret = cx_strtok_next(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(tok, cx_str("comma")), 0); - EXPECT_EQ(ctx.pos, 2); - EXPECT_EQ(ctx.next_pos, 8); - EXPECT_EQ(ctx.delim_pos, 7); - EXPECT_EQ(ctx.found, 2); - - ret = cx_strtok_next(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(tok, cx_str("separated")), 0); - EXPECT_EQ(ctx.pos, 8); - EXPECT_EQ(ctx.next_pos, 18); - EXPECT_EQ(ctx.delim_pos, 17); - EXPECT_EQ(ctx.found, 3); - - ret = cx_strtok_next(&ctx, &tok); - ASSERT_FALSE(ret); - EXPECT_EQ(ctx.pos, 8); - EXPECT_EQ(ctx.next_pos, 18); - EXPECT_EQ(ctx.delim_pos, 17); - EXPECT_EQ(ctx.found, 3); -} - -TEST(String, strtok_next_unlimited) { - cxstring str = cx_str("some;-;otherwise;-;separated;-;string;-;"); - cxstring delim = cx_str(";-;"); - CxStrtokCtx ctx = cx_strtok(str, delim, SIZE_MAX); - bool ret; - cxstring tok; - - ret = cx_strtok_next(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(tok, cx_str("some")), 0); - EXPECT_EQ(ctx.pos, 0); - EXPECT_EQ(ctx.next_pos, 7); - EXPECT_EQ(ctx.delim_pos, 4); - EXPECT_EQ(ctx.found, 1); - - ret = cx_strtok_next(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(tok, cx_str("otherwise")), 0); - EXPECT_EQ(ctx.pos, 7); - EXPECT_EQ(ctx.next_pos, 19); - EXPECT_EQ(ctx.delim_pos, 16); - EXPECT_EQ(ctx.found, 2); - - ret = cx_strtok_next(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(tok, cx_str("separated")), 0); - EXPECT_EQ(ctx.pos, 19); - EXPECT_EQ(ctx.next_pos, 31); - EXPECT_EQ(ctx.delim_pos, 28); - EXPECT_EQ(ctx.found, 3); - - ret = cx_strtok_next(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(tok, cx_str("string")), 0); - EXPECT_EQ(ctx.pos, 31); - EXPECT_EQ(ctx.next_pos, 40); - EXPECT_EQ(ctx.delim_pos, 37); - EXPECT_EQ(ctx.found, 4); - - ret = cx_strtok_next(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(tok, cx_str("")), 0); - EXPECT_EQ(ctx.pos, 40); - EXPECT_EQ(ctx.next_pos, 40); - EXPECT_EQ(ctx.delim_pos, 40); - EXPECT_EQ(ctx.found, 5); - - ret = cx_strtok_next(&ctx, &tok); - ASSERT_FALSE(ret); - EXPECT_EQ(ctx.pos, 40); - EXPECT_EQ(ctx.delim_pos, 40); - EXPECT_EQ(ctx.found, 5); -} - -TEST(String, strtok_next_advanced) { - cxmutstr str = cx_strdup(cx_str("an,arbitrarily;||separated;string")); - cxstring delim = cx_str(","); - cxstring delim_more[2] = {CX_STR("||"), CX_STR(";")}; - CxStrtokCtx ctx = cx_strtok_m(str, delim, 10); - cx_strtok_delim(&ctx, delim_more, 2); - bool ret; - cxmutstr tok; - - ret = cx_strtok_next_m(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(cx_strcast(tok), cx_str("an")), 0); - EXPECT_EQ(ctx.pos, 0); - EXPECT_EQ(ctx.next_pos, 3); - EXPECT_EQ(ctx.delim_pos, 2); - EXPECT_EQ(ctx.found, 1); - cx_strupper(tok); - - ret = cx_strtok_next_m(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(cx_strcast(tok), cx_str("arbitrarily")), 0); - EXPECT_EQ(ctx.pos, 3); - EXPECT_EQ(ctx.next_pos, 15); - EXPECT_EQ(ctx.delim_pos, 14); - EXPECT_EQ(ctx.found, 2); - cx_strupper(tok); - - ret = cx_strtok_next_m(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(cx_strcast(tok), cx_str("")), 0); - EXPECT_EQ(ctx.pos, 15); - EXPECT_EQ(ctx.next_pos, 17); - EXPECT_EQ(ctx.delim_pos, 15); - EXPECT_EQ(ctx.found, 3); - cx_strupper(tok); - - ret = cx_strtok_next_m(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(cx_strcast(tok), cx_str("separated")), 0); - EXPECT_EQ(ctx.pos, 17); - EXPECT_EQ(ctx.next_pos, 27); - EXPECT_EQ(ctx.delim_pos, 26); - EXPECT_EQ(ctx.found, 4); - cx_strupper(tok); - - ret = cx_strtok_next_m(&ctx, &tok); - ASSERT_TRUE(ret); - EXPECT_EQ(cx_strcmp(cx_strcast(tok), cx_str("string")), 0); - EXPECT_EQ(ctx.pos, 27); - EXPECT_EQ(ctx.next_pos, 33); - EXPECT_EQ(ctx.delim_pos, 33); - EXPECT_EQ(ctx.found, 5); - cx_strupper(tok); - - ret = cx_strtok_next_m(&ctx, &tok); - ASSERT_FALSE(ret); - EXPECT_EQ(ctx.pos, 27); - EXPECT_EQ(ctx.next_pos, 33); - EXPECT_EQ(ctx.delim_pos, 33); - EXPECT_EQ(ctx.found, 5); - - EXPECT_EQ(cx_strcmp(cx_strcast(str), cx_str("AN,ARBITRARILY;||SEPARATED;STRING")), 0); - - cx_strfree(&str); -} diff -r bf817b825ed2 -r e081643aae2a test/test_tree.cpp --- a/test/test_tree.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +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/tree.h" -#include - -struct TestNode { - TestNode *parent = nullptr; - TestNode *prev = nullptr; - TestNode *next = nullptr; - - TestNode *children_begin = nullptr; - TestNode *children_end = nullptr; -}; - -TEST(Tree, cx_tree_add_sibling) { - // prepare test tree - TestNode root, a; - root.children_begin = &a; - root.children_end = &a; - a.parent = &root; - - // new test nodes - TestNode b, c; - - // test - cx_tree_add_sibling(&a, offsetof(TestNode, prev), offsetof(TestNode, next), offsetof(TestNode, parent), &b); - EXPECT_EQ(b.parent, &root); - EXPECT_EQ(b.prev, &a); - EXPECT_EQ(b.next, nullptr); - EXPECT_EQ(a.next, &b); - - cx_tree_add_sibling(&a, -1, offsetof(TestNode, next), -1, &c); - EXPECT_EQ(c.parent, nullptr); - EXPECT_EQ(c.prev, nullptr); - EXPECT_EQ(c.next, nullptr); - EXPECT_EQ(b.next, &c); -} - -TEST(Tree, cx_tree_add_child) { - TestNode root, a, b, c, a1; - - cx_tree_add_child( - (void **) &root.children_begin, - (void **) &root.children_end, - offsetof(TestNode, prev), - offsetof(TestNode, next), - &a, - offsetof(TestNode, parent), - &root); - EXPECT_EQ(root.children_begin, &a); - EXPECT_EQ(root.children_end, &a); - EXPECT_EQ(a.parent, &root); - EXPECT_EQ(a.prev, nullptr); - EXPECT_EQ(a.next, nullptr); - - cx_tree_add_child( - (void **) &root.children_begin, - (void **) &root.children_end, - offsetof(TestNode, prev), - offsetof(TestNode, next), - &b, - offsetof(TestNode, parent), - &root); - EXPECT_EQ(root.children_begin, &a); - EXPECT_EQ(root.children_begin->next, &b); - EXPECT_EQ(root.children_end, &b); - EXPECT_EQ(b.parent, &root); - EXPECT_EQ(b.prev, &a); - - cx_tree_add_child( - (void **) &root.children_begin, - nullptr, - -1, - offsetof(TestNode, next), - &c, - -1, - &root); - EXPECT_EQ(root.children_end, &b); // children_end unchanged - EXPECT_EQ(b.next, &c); - EXPECT_EQ(c.prev, nullptr); - EXPECT_EQ(c.next, nullptr); - EXPECT_EQ(c.parent, nullptr); - - cx_tree_add_child( - (void **) &a.children_begin, - (void **) &a.children_end, - offsetof(TestNode, prev), - offsetof(TestNode, next), - &a1, - offsetof(TestNode, parent), - &a); - EXPECT_EQ(a.children_begin, &a1); - EXPECT_EQ(a1.parent, &a); - EXPECT_EQ(root.children_begin, &a); - EXPECT_EQ(root.children_begin->children_begin, &a1); -} diff -r bf817b825ed2 -r e081643aae2a test/test_utils.cpp --- a/test/test_utils.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +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/utils.h" - -#include - -TEST(Utils, ForN) { - unsigned j; - j = 0; - cx_for_n(i, 50) { - EXPECT_EQ(i, j); - j++; - } -} - -TEST(Utils, szmul) { - size_t r; - int e; - e = cx_szmul(5, 7, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(35, r); - - size_t s = SIZE_MAX & ~3; - - e = cx_szmul(s / 4, 2, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(s / 2, r); - e = cx_szmul(2, s / 4, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(s / 2, r); - - e = cx_szmul(s / 4, 4, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(s, r); - - e = cx_szmul(4, s / 4, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(s, r); - - e = cx_szmul(s / 4, 5, &r); - EXPECT_NE(0, e); - - e = cx_szmul(5, s / 4, &r); - EXPECT_NE(0, e); - - e = cx_szmul(SIZE_MAX - 4, 0, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(0, r); - - e = cx_szmul(0, SIZE_MAX - 1, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(0, r); - - e = cx_szmul(SIZE_MAX, 0, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(0, r); - - e = cx_szmul(0, SIZE_MAX, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(0, r); - - e = cx_szmul(0, 0, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(0, r); -} - -#ifdef CX_SZMUL_BUILTIN - -// also test the custom implementation -struct Utils_szmul_impl : ::testing::Test { -#undef CX_SZMUL_BUILTIN - -#include "../src/utils.c" - -#define CX_SZMUL_BUILTIN -}; - -TEST_F(Utils_szmul_impl, Test) { - size_t r; - int e; - e = cx_szmul_impl(5, 7, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(35, r); - - size_t s = SIZE_MAX & ~3; - - e = cx_szmul_impl(s / 4, 2, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(s / 2, r); - e = cx_szmul_impl(2, s / 4, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(s / 2, r); - - e = cx_szmul_impl(s / 4, 4, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(s, r); - - e = cx_szmul_impl(4, s / 4, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(s, r); - - e = cx_szmul_impl(s / 4, 5, &r); - EXPECT_NE(0, e); - - e = cx_szmul_impl(5, s / 4, &r); - EXPECT_NE(0, e); - - e = cx_szmul_impl(SIZE_MAX - 4, 0, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(0, r); - - e = cx_szmul_impl(0, SIZE_MAX - 1, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(0, r); - - e = cx_szmul_impl(SIZE_MAX, 0, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(0, r); - - e = cx_szmul_impl(0, SIZE_MAX, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(0, r); - - e = cx_szmul_impl(0, 0, &r); - EXPECT_EQ(0, e); - EXPECT_EQ(0, r); -} - -#endif // CX_SZMUL_BUILTIN diff -r bf817b825ed2 -r e081643aae2a test/util_allocator.cpp --- a/test/util_allocator.cpp Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +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 "util_allocator.h" - -void *cx_malloc_testing(void *d, size_t n) { - auto data = reinterpret_cast(d); - void *ptr = malloc(n); - data->alloc_total++; - if (ptr == nullptr) { - data->alloc_failed++; - } else { - data->tracked.insert(ptr); - } - return ptr; -} - -void *cx_realloc_testing(void *d, void *mem, size_t n) { - auto data = reinterpret_cast(d); - void *ptr = realloc(mem, n); - if (ptr == mem) { - return ptr; - } else { - data->alloc_total++; - if (ptr == nullptr) { - data->alloc_failed++; - } else { - data->free_total++; - if (data->tracked.erase(mem) == 0) { - data->free_failed++; - } - data->tracked.insert(ptr); - } - return ptr; - } -} - -void *cx_calloc_testing(void *d, size_t nelem, size_t n) { - auto data = reinterpret_cast(d); - void *ptr = calloc(nelem, n); - data->alloc_total++; - if (ptr == nullptr) { - data->alloc_failed++; - } else { - data->tracked.insert(ptr); - } - return ptr; -} - -void cx_free_testing(void *d, void *mem) { - auto data = reinterpret_cast(d); - data->free_total++; - if (data->tracked.erase(mem) == 0) { - data->free_failed++; - // do not even attempt to free mem, because it is likely to segfault - } else { - free(mem); - } -} - -cx_allocator_class cx_testing_allocator_class = { - cx_malloc_testing, - cx_realloc_testing, - cx_calloc_testing, - cx_free_testing -}; - -CxTestingAllocator::CxTestingAllocator() : CxAllocator() { - cl = &cx_testing_allocator_class; - data = this; -} - -bool CxTestingAllocator::used() const { - return alloc_total > 0; -} - -bool CxTestingAllocator::verify() const { - return tracked.empty() && alloc_failed == 0 && free_failed == 0 && alloc_total == free_total; -} - -// SELF-TEST - -#include - -TEST(TestingAllocator, ExpectFree) { - CxTestingAllocator allocator; - - ASSERT_TRUE(allocator.verify()); - EXPECT_FALSE(allocator.used()); - auto ptr = cxMalloc(&allocator, 16); - EXPECT_TRUE(allocator.used()); - ASSERT_NE(ptr, nullptr); - EXPECT_FALSE(allocator.verify()); - - cxFree(&allocator, ptr); - EXPECT_TRUE(allocator.verify()); -} - -TEST(TestingAllocator, DetectDoubleFree) { - CxTestingAllocator allocator; - - ASSERT_TRUE(allocator.verify()); - auto ptr = cxMalloc(&allocator, 16); - ASSERT_NE(ptr, nullptr); - - cxFree(&allocator, ptr); - EXPECT_TRUE(allocator.verify()); - ASSERT_NO_FATAL_FAILURE(cxFree(&allocator, ptr)); - EXPECT_FALSE(allocator.verify()); -} - -TEST(TestingAllocator, FreeUntracked) { - CxTestingAllocator allocator; - - auto ptr = malloc(16); - ASSERT_TRUE(allocator.verify()); - ASSERT_NO_FATAL_FAILURE(cxFree(&allocator, ptr)); - EXPECT_FALSE(allocator.verify()); - ASSERT_NO_FATAL_FAILURE(free(ptr)); -} - -TEST(TestingAllocator, FullLifecycleWithRealloc) { - CxTestingAllocator allocator; - ASSERT_TRUE(allocator.verify()); - auto ptr = cxMalloc(&allocator, 16); - ASSERT_NE(ptr, nullptr); - EXPECT_EQ(allocator.tracked.size(), 1); - ptr = cxRealloc(&allocator, ptr, 256); - ASSERT_NE(ptr, nullptr); - EXPECT_EQ(allocator.tracked.size(), 1); - cxFree(&allocator, ptr); - EXPECT_TRUE(allocator.verify()); -} - -TEST(TestingAllocator, CallocInitializes) { - CxTestingAllocator allocator; - const char zeros[16] = {0}; - auto ptr = cxCalloc(&allocator, 16, 1); - EXPECT_EQ(memcmp(ptr, zeros, 16), 0); - cxFree(&allocator, ptr); - EXPECT_TRUE(allocator.verify()); -} diff -r bf817b825ed2 -r e081643aae2a test/util_allocator.h --- a/test/util_allocator.h Tue Feb 07 21:53:06 2023 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +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. - */ - -#ifndef UCX_UTIL_ALLOCATOR_H -#define UCX_UTIL_ALLOCATOR_H - -#include "cx/allocator.h" - -#include - -struct CxTestingAllocator : public CxAllocator { - /** - * Total number of all allocations (malloc, calloc, realloc). - * A realloc() does only count when the memory has to be moved. - */ - unsigned alloc_total = 0; - /** - * Number of failed allocations (malloc, calloc, realloc). - */ - unsigned alloc_failed = 0; - /** - * Total number of freed pointers. - * A reallocation also counts as a free when the memory has to be moved. - */ - unsigned free_total = 0; - /** - * Number of failed free invocations. - * A free() is considered failed, if it has not been performed on tracked memory. - */ - unsigned free_failed = 0; - /** - * The set of tracked memory blocks. - */ - std::set tracked; - - /** - * Constructs a new testing allocator. - */ - CxTestingAllocator(); - - /** - * Verifies that this allocator has been used. - * - * @return true if any allocation was attempted using this allocator - */ - [[nodiscard]] bool used() const; - - /** - * Verifies that all allocated memory blocks are freed and no free occurred twice. - * - * @return true iff all tracked allocations / deallocations were valid - */ - [[nodiscard]] bool verify() const; -}; - -#endif // UCX_UTIL_ALLOCATOR_H diff -r bf817b825ed2 -r e081643aae2a tests/.clang-tidy --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/.clang-tidy Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,2 @@ +# Disable static initialization warning for test code +Checks: '-cert-err58-cpp' diff -r bf817b825ed2 -r e081643aae2a tests/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/CMakeLists.txt Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,32 @@ +# Load Google Test Framework +set(CMAKE_CXX_STANDARD 17) + +include(FetchContent) +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG e2239ee6043f73722e7aa812a459f54a28552929 # release 1.11.0 +) +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) +include(GoogleTest) +message(STATUS "Google Test made available") + +add_executable(ucxtest + test_utils.cpp + test_allocator.cpp + test_compare.cpp + test_string.cpp + test_buffer.cpp + test_list.cpp + test_tree.cpp + test_hash_key.cpp + test_map.cpp + test_basic_mempool.cpp + test_printf.cpp + selftest.cpp + util_allocator.cpp + ) +target_link_libraries(ucxtest PRIVATE ucx_static gtest_main) +gtest_discover_tests(ucxtest) diff -r bf817b825ed2 -r e081643aae2a tests/selftest.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/selftest.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,39 @@ +/* + * 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 +#include + +TEST(SelfTest, BasicAssertion) { + EXPECT_EQ(7 * 6, 42); +} + +TEST(SelfTest, UcxVersion) { + EXPECT_GE(UCX_VERSION_MAJOR, 3); + EXPECT_GE(UCX_VERSION, 3 << 16); +} diff -r bf817b825ed2 -r e081643aae2a tests/test_allocator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_allocator.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,96 @@ +/* + * 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/allocator.h" +#include + +TEST(Allocator, DefaultAllocator) { + cx_allocator_class *clazz = cxDefaultAllocator->cl; + ASSERT_NE(clazz, nullptr); +} + +TEST(Allocator, DefaultMalloc) { + void *test = cxMalloc(cxDefaultAllocator, 16); + ASSERT_NE(test, nullptr); + free(test); +} + +TEST(Allocator, DefaultRealloc) { + void *test = calloc(8, 1); + memcpy(test, "Test", 5); + test = cxRealloc(cxDefaultAllocator, test, 16); + ASSERT_NE(test, nullptr); + EXPECT_STREQ(reinterpret_cast(test), "Test"); + free(test); +} + +TEST(Allocator, Reallocate) { + void *test = calloc(8, 1); + memcpy(test, "Test", 5); + int ret = cxReallocate(cxDefaultAllocator, &test, 16); + EXPECT_EQ(ret, 0); + ASSERT_NE(test, nullptr); + EXPECT_STREQ(reinterpret_cast(test), "Test"); + free(test); +} + +TEST(Allocator, DefaultCalloc) { + char *test = reinterpret_cast(cxCalloc(cxDefaultAllocator, 8, 2)); + ASSERT_NE(test, nullptr); + for (int i = 0; i < 16; i++) ASSERT_EQ(test[i], 0); + free(test); +} + +TEST(Allocator, DefaultFree) { + void *test = malloc(16); + EXPECT_NO_FATAL_FAILURE( + cxFree(cxDefaultAllocator, test); + ); +} + +TEST(Allocator, FailingReallocate) { + // Mock an allocator that always returns nullptr on realloc + cx_allocator_class mock_cl; + mock_cl.realloc = []( + [[maybe_unused]]void *p, + [[maybe_unused]]void *d, + [[maybe_unused]]size_t n + ) -> void * { return nullptr; }; + cx_allocator_s mock{&mock_cl, nullptr}; + + void *test = calloc(8, 1); + memcpy(test, "Test", 5); + void *original = test; + int ret = cxReallocate(&mock, &test, 16); + // non-zero return code because of the failure + EXPECT_NE(ret, 0); + // the test pointer was not changed and still points to the same memory + EXPECT_EQ(test, original); + EXPECT_STREQ(reinterpret_cast(test), "Test"); + free(test); +} diff -r bf817b825ed2 -r e081643aae2a tests/test_basic_mempool.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_basic_mempool.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,154 @@ +/* + * 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/basic_mempool.h" +#include "util_allocator.h" +#include + +class CxBasicMempool : public ::testing::Test { +protected: + CxMempool *pool = nullptr; + + void TearDown() override { + if (pool != nullptr) { + cxMempoolDestroy(pool); + } + } +}; + +TEST_F(CxBasicMempool, Create) { + pool = cxBasicMempoolCreate(16); + ASSERT_NE(pool->allocator, nullptr); + ASSERT_NE(pool->cl, nullptr); + EXPECT_NE(pool->cl->destroy, nullptr); + ASSERT_NE(pool->allocator->cl, nullptr); + EXPECT_EQ(pool->allocator->data, pool); + EXPECT_NE(pool->allocator->cl->malloc, nullptr); + EXPECT_NE(pool->allocator->cl->calloc, nullptr); + EXPECT_NE(pool->allocator->cl->realloc, nullptr); + EXPECT_NE(pool->allocator->cl->free, nullptr); + + auto basic_pool = reinterpret_cast(pool); + EXPECT_EQ(basic_pool->size, 16); + EXPECT_EQ(basic_pool->ndata, 0); + EXPECT_NE(basic_pool->data, nullptr); +} + +TEST_F(CxBasicMempool, malloc) { + pool = cxBasicMempoolCreate(4); + auto basic_pool = reinterpret_cast(pool); + EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); + EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); + EXPECT_EQ(basic_pool->ndata, 2); + EXPECT_EQ(basic_pool->size, 4); + EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); + EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); + EXPECT_EQ(basic_pool->ndata, 4); + EXPECT_EQ(basic_pool->size, 4); + EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); + EXPECT_NE(cxMalloc(pool->allocator, sizeof(int)), nullptr); + EXPECT_EQ(basic_pool->ndata, 6); + EXPECT_GE(basic_pool->size, 6); +} + +TEST_F(CxBasicMempool, calloc) { + pool = cxBasicMempoolCreate(4); + + auto test = (int *) cxCalloc(pool->allocator, 2, sizeof(int)); + ASSERT_NE(test, nullptr); + EXPECT_EQ(test[0], 0); + EXPECT_EQ(test[1], 0); +} + +static unsigned test_destructor_called = 0; + +static void test_destructor([[maybe_unused]] void *mem) { + test_destructor_called++; +} + +TEST_F(CxBasicMempool, destructor) { + pool = cxBasicMempoolCreate(4); + auto data = cxMalloc(pool->allocator, sizeof(int)); + *((int *) data) = 13; + cxMempoolSetDestructor(pool, data, test_destructor); + EXPECT_EQ(*((int *) data), 13); + test_destructor_called = 0; + cxFree(pool->allocator, data); + EXPECT_EQ(test_destructor_called, 1); + data = cxMalloc(pool->allocator, sizeof(int)); + cxMempoolSetDestructor(pool, data, test_destructor); + cxMempoolDestroy(pool); + pool = nullptr; + EXPECT_EQ(test_destructor_called, 2); +} + +TEST_F(CxBasicMempool, realloc) { + pool = cxBasicMempoolCreate(4); + auto data = cxMalloc(pool->allocator, sizeof(int)); + *((int *) data) = 13; + cxMempoolSetDestructor(pool, data, test_destructor); + + void *rdata = data; + unsigned n = 1; + while (rdata == data) { + n <<= 1; + ASSERT_LT(n, 65536); // eventually the memory should be moved elsewhere + rdata = cxRealloc(pool->allocator, data, n * sizeof(intptr_t)); + } + + EXPECT_EQ(*((int *) rdata), 13); + // test if destructor is still intact + test_destructor_called = 0; + cxFree(pool->allocator, rdata); + EXPECT_EQ(test_destructor_called, 1); +} + + +TEST_F(CxBasicMempool, free) { + pool = cxBasicMempoolCreate(4); + auto basic_pool = reinterpret_cast(pool); + + void *mem1; + void *mem2; + + mem1 = cxMalloc(pool->allocator, 16); + cxFree(pool->allocator, mem1); + EXPECT_EQ(basic_pool->ndata, 0); + + cxMalloc(pool->allocator, 16); + cxMalloc(pool->allocator, 16); + mem1 = cxMalloc(pool->allocator, 16); + cxMalloc(pool->allocator, 16); + mem2 = cxMalloc(pool->allocator, 16); + + EXPECT_EQ(basic_pool->ndata, 5); + cxFree(pool->allocator, mem1); + EXPECT_EQ(basic_pool->ndata, 4); + cxFree(pool->allocator, mem2); + EXPECT_EQ(basic_pool->ndata, 3); +} \ No newline at end of file diff -r bf817b825ed2 -r e081643aae2a tests/test_buffer.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_buffer.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,815 @@ +/* + * 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 BufferFixture : public ::testing::Test { +protected: + void SetUp() override { + cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); + buf.size = 6; + buf.pos = 3; + } + + void TearDown() override { + cxBufferDestroy(&buf); + } + + CxBuffer buf{}; +}; + +static void expect_default_flush_config(CxBuffer *buf) { + EXPECT_EQ(buf->flush_blkmax, 0); + EXPECT_EQ(buf->flush_blksize, 4096); + EXPECT_EQ(buf->flush_threshold, SIZE_MAX); + EXPECT_EQ(buf->flush_func, nullptr); + EXPECT_EQ(buf->flush_target, nullptr); +} + +TEST(BufferInit, WrapSpace) { + CxTestingAllocator alloc; + CxBuffer buf; + void *space = cxMalloc(&alloc, 16); + cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_DEFAULT); + expect_default_flush_config(&buf); + EXPECT_EQ(buf.space, space); + EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0); + EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0); + EXPECT_EQ(buf.pos, 0); + EXPECT_EQ(buf.size, 0); + EXPECT_EQ(buf.capacity, 16); + EXPECT_EQ(buf.allocator, &alloc); + cxBufferDestroy(&buf); + EXPECT_FALSE(alloc.verify()); + cxFree(&alloc, space); + EXPECT_TRUE(alloc.verify()); +} + +TEST(BufferInit, WrapSpaceAutoExtend) { + CxTestingAllocator alloc; + CxBuffer buf; + void *space = cxMalloc(&alloc, 16); + cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_AUTO_EXTEND); + expect_default_flush_config(&buf); + EXPECT_EQ(buf.space, space); + EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, CX_BUFFER_AUTO_EXTEND); + EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0); + EXPECT_EQ(buf.pos, 0); + EXPECT_EQ(buf.size, 0); + EXPECT_EQ(buf.capacity, 16); + EXPECT_EQ(buf.allocator, &alloc); + cxBufferDestroy(&buf); + EXPECT_FALSE(alloc.verify()); + cxFree(&alloc, space); + EXPECT_TRUE(alloc.verify()); +} + +TEST(BufferInit, WrapSpaceAutoFree) { + CxTestingAllocator alloc; + CxBuffer buf; + void *space = cxMalloc(&alloc, 16); + cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_FREE_CONTENTS); + expect_default_flush_config(&buf); + EXPECT_EQ(buf.space, space); + EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0); + EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS); + EXPECT_EQ(buf.pos, 0); + EXPECT_EQ(buf.size, 0); + EXPECT_EQ(buf.capacity, 16); + EXPECT_EQ(buf.allocator, &alloc); + EXPECT_FALSE(alloc.verify()); + cxBufferDestroy(&buf); + EXPECT_TRUE(alloc.verify()); +} + +TEST(BufferInit, FreshSpace) { + CxTestingAllocator alloc; + CxBuffer buf; + cxBufferInit(&buf, nullptr, 8, &alloc, CX_BUFFER_DEFAULT); + expect_default_flush_config(&buf); + EXPECT_NE(buf.space, nullptr); + EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0); + EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS); + EXPECT_EQ(buf.pos, 0); + EXPECT_EQ(buf.size, 0); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(buf.allocator, &alloc); + EXPECT_FALSE(alloc.verify()); // space is still allocated + cxBufferDestroy(&buf); + EXPECT_TRUE(alloc.verify()); +} + +class BufferShiftFixture : public ::testing::Test { +protected: + void SetUp() override { + ASSERT_TRUE(alloc.verify()); + cxBufferInit(&buf, nullptr, 16, &alloc, CX_BUFFER_DEFAULT); + memcpy(buf.space, "test____________", 16); + buf.capacity = 8; // purposely pretend that the buffer has less capacity s.t. we can test beyond the range + buf.pos = 4; + buf.size = 4; + } + + void TearDown() override { + cxBufferDestroy(&buf); + EXPECT_TRUE(alloc.verify()); + } + + CxTestingAllocator alloc; + CxBuffer buf{}; +}; + +class BufferShiftLeft : public BufferShiftFixture { +}; + +TEST_F(BufferShiftLeft, Zero) { + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + int ret = cxBufferShiftLeft(&buf, 0); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 4); + EXPECT_EQ(buf.size, 4); + EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); +} + +TEST_F(BufferShiftLeft, ZeroOffsetInterface) { + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + int ret = cxBufferShift(&buf, -0); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 4); + EXPECT_EQ(buf.size, 4); + EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); +} + +TEST_F(BufferShiftLeft, Standard) { + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + int ret = cxBufferShiftLeft(&buf, 2); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 2); + EXPECT_EQ(buf.size, 2); + EXPECT_TRUE(memcmp(buf.space, "stst________", 8) == 0); +} + +TEST_F(BufferShiftLeft, Overshift) { + ASSERT_LT(buf.pos, 6); + ASSERT_LT(buf.size, 6); + int ret = cxBufferShiftLeft(&buf, 6); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 0); + EXPECT_EQ(buf.size, 0); + EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); +} + +TEST_F(BufferShiftLeft, OvershiftPosOnly) { + buf.pos = 2; + ASSERT_EQ(buf.size, 4); + int ret = cxBufferShiftLeft(&buf, 3); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 0); + EXPECT_EQ(buf.size, 1); + EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); +} + +TEST_F(BufferShiftLeft, OffsetInterface) { + buf.pos = 3; + ASSERT_EQ(buf.size, 4); + int ret = cxBufferShift(&buf, -2); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 1); + EXPECT_EQ(buf.size, 2); + EXPECT_TRUE(memcmp(buf.space, "stst________", 8) == 0); +} + +class BufferShiftRight : public BufferShiftFixture { +}; + +TEST_F(BufferShiftRight, Zero) { + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + int ret = cxBufferShiftRight(&buf, 0); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 4); + EXPECT_EQ(buf.size, 4); + EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); +} + +TEST_F(BufferShiftRight, ZeroOffsetInterface) { + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + int ret = cxBufferShift(&buf, +0); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 4); + EXPECT_EQ(buf.size, 4); + EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0); +} + +TEST_F(BufferShiftRight, Standard) { + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + int ret = cxBufferShiftRight(&buf, 3); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 7); + EXPECT_EQ(buf.size, 7); + EXPECT_TRUE(memcmp(buf.space, "testest_____", 8) == 0); +} + +TEST_F(BufferShiftRight, OvershiftDiscard) { + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + ASSERT_EQ(buf.capacity, 8); + int ret = cxBufferShiftRight(&buf, 6); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 8); + EXPECT_EQ(buf.size, 8); + EXPECT_EQ(buf.capacity, 8); + EXPECT_TRUE(memcmp(buf.space, "test__te____", 8) == 0); +} + +TEST_F(BufferShiftRight, OvershiftExtend) { + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + ASSERT_EQ(buf.capacity, 8); + buf.flags |= CX_BUFFER_AUTO_EXTEND; + int ret = cxBufferShiftRight(&buf, 6); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 10); + EXPECT_EQ(buf.size, 10); + EXPECT_GE(buf.capacity, 10); + EXPECT_TRUE(memcmp(buf.space, "test__test__", 8) == 0); +} + +TEST_F(BufferShiftRight, OffsetInterface) { + buf.pos = 3; + ASSERT_EQ(buf.size, 4); + int ret = cxBufferShift(&buf, 2); + EXPECT_EQ(ret, 0); + EXPECT_EQ(buf.pos, 5); + EXPECT_EQ(buf.size, 6); + EXPECT_TRUE(memcmp(buf.space, "tetest______", 8) == 0); +} + +TEST(BufferMinimumCapacity, Sufficient) { + CxTestingAllocator alloc; + auto 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); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(buf.size, 8); + EXPECT_TRUE(memcmp(buf.space, "Testing", 8) == 0); + cxBufferDestroy(&buf); + EXPECT_TRUE(alloc.verify()); +} + +TEST(BufferMinimumCapacity, Extend) { + CxTestingAllocator alloc; + auto 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); + EXPECT_EQ(buf.capacity, 16); + EXPECT_EQ(buf.size, 8); + EXPECT_TRUE(memcmp(buf.space, "Testing", 8) == 0); + cxBufferDestroy(&buf); + EXPECT_TRUE(alloc.verify()); +} + +TEST(BufferClear, Test) { + char space[16]; + strcpy(space, "clear test"); + CxBuffer buf; + cxBufferInit(&buf, space, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT); + ASSERT_EQ(buf.size, 0); + // only clear the used part of the buffer + cxBufferClear(&buf); + EXPECT_EQ(memcmp(space, "clear test", 10), 0); + buf.size = 5; + buf.pos = 3; + cxBufferClear(&buf); + EXPECT_EQ(memcmp(space, "\0\0\0\0\0 test", 10), 0); + EXPECT_EQ(buf.size, 0); + EXPECT_EQ(buf.pos, 0); + cxBufferDestroy(&buf); +} + +class BufferWrite : public ::testing::Test { +protected: + CxBuffer buf{}, target{}; + + void SetUp() override { + cxBufferInit(&target, nullptr, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND); + cxBufferInit(&buf, nullptr, 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"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + size_t written = cxBufferWrite(data, 1, 4, &buf); + EXPECT_EQ(written, 4); + EXPECT_EQ(buf.size, 8); + EXPECT_EQ(buf.pos, 8); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); +} + +TEST_F(BufferWrite, SizeOneDiscard) { + const char *data = "testing"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + size_t written = cxBufferWrite(data, 1, 7, &buf); + EXPECT_EQ(written, 4); + EXPECT_EQ(buf.size, 8); + EXPECT_EQ(buf.pos, 8); + EXPECT_EQ(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"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + size_t written = cxBufferWrite(data, 1, 7, &buf); + EXPECT_EQ(written, 7); + EXPECT_EQ(buf.size, 11); + EXPECT_EQ(buf.pos, 11); + EXPECT_GE(buf.capacity, 11); + EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0); +} + +TEST_F(BufferWrite, MultibyteFit) { + const char *data = "test"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + size_t written = cxBufferWrite(data, 2, 2, &buf); + EXPECT_EQ(written, 2); + EXPECT_EQ(buf.size, 8); + EXPECT_EQ(buf.pos, 8); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); +} + +TEST_F(BufferWrite, MultibyteDiscard) { + const char *data = "testing"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.size, 4); + buf.pos = 3; + size_t written = cxBufferWrite(data, 2, 4, &buf); + // remember: whole elements are discarded if they do not fit + EXPECT_EQ(written, 2); + EXPECT_EQ(buf.size, 7); + EXPECT_EQ(buf.pos, 7); + EXPECT_EQ(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"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.size, 4); + buf.pos = 3; + size_t written = cxBufferWrite(data, 2, 3, &buf); + // remember: whole elements are discarded if they do not fit + EXPECT_EQ(written, 3); + EXPECT_EQ(buf.size, 9); + EXPECT_EQ(buf.pos, 9); + EXPECT_GE(buf.capacity, 9); + EXPECT_EQ(memcmp(buf.space, "pretester", 9), 0); +} + +TEST_F(BufferWrite, PutcWrapperFit) { + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + int c = cxBufferPut(&buf, 0x200 | 'a'); + EXPECT_EQ(c, 'a'); + EXPECT_EQ(buf.size, 5); + EXPECT_EQ(buf.pos, 5); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(memcmp(buf.space, "prepa\0", 6), 0); +} + +TEST_F(BufferWrite, PutcWrapperDiscard) { + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.size, 4); + buf.pos = 8; + int c = cxBufferPut(&buf, 0x200 | 'a'); + EXPECT_EQ(c, EOF); + EXPECT_EQ(buf.size, 4); + EXPECT_EQ(buf.pos, 8); + EXPECT_EQ(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; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.size, 4); + buf.pos = 8; + int c = cxBufferPut(&buf, 0x200 | 'a'); + EXPECT_EQ(c, 'a'); + EXPECT_EQ(buf.size, 9); + EXPECT_EQ(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"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + size_t written = cxBufferPutString(&buf, data); + EXPECT_EQ(written, 4); + EXPECT_EQ(buf.size, 8); + EXPECT_EQ(buf.pos, 8); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0); +} + +TEST_F(BufferWrite, PutStringWrapperDiscard) { + const char *data = "testing"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + size_t written = cxBufferPutString(&buf, data); + EXPECT_EQ(written, 4); + EXPECT_EQ(buf.size, 8); + EXPECT_EQ(buf.pos, 8); + EXPECT_EQ(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"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + size_t written = cxBufferPutString(&buf, data); + EXPECT_EQ(written, 7); + EXPECT_EQ(buf.size, 11); + EXPECT_EQ(buf.pos, 11); + EXPECT_GE(buf.capacity, 11); + EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0); +} + +TEST_F(BufferWrite, MultOverflow) { + const char *data = "testing"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + size_t written = cxBufferWrite(data, 8, SIZE_MAX / 4, &buf); + EXPECT_EQ(written, 0); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(buf.pos, 4); + EXPECT_EQ(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"; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + ASSERT_EQ(buf.size, 4); + size_t written = cxBufferWrite(data, 1, SIZE_MAX - 2, &buf); + EXPECT_EQ(written, 0); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(buf.pos, 4); + EXPECT_EQ(buf.size, 4); + EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0); +} + +TEST_F(BufferWrite, OnlyOverwrite) { + buf.flags |= CX_BUFFER_AUTO_EXTEND; + ASSERT_EQ(buf.capacity, 8); + memcpy(buf.space, "preptest", 8); + buf.pos = 3; + buf.size = 8; + size_t written = cxBufferWrite("XXX", 2, 2, &buf); + EXPECT_EQ(written, 2); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(buf.size, 8); + EXPECT_EQ(buf.pos, 7); + EXPECT_EQ(memcmp(buf.space, "preXXX\0t", 8), 0); +} + +TEST_F(BufferWrite, FlushAtCapacity) { + enableFlushing(); + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + size_t written = cxBufferWrite("foo", 1, 3, &buf); + EXPECT_EQ(written, 3); + ASSERT_EQ(buf.pos, 7); + ASSERT_EQ(buf.size, 7); + ASSERT_EQ(target.pos, 0); + ASSERT_EQ(target.size, 0); + written = cxBufferWrite("hello", 1, 5, &buf); + EXPECT_EQ(written, 5); + EXPECT_EQ(buf.pos, 0); + EXPECT_EQ(buf.size, 0); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(target.pos, 12); + ASSERT_EQ(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; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + size_t written = cxBufferWrite("foobar", 1, 6, &buf); + EXPECT_EQ(written, 6); + ASSERT_EQ(buf.pos, 10); + ASSERT_EQ(buf.size, 10); + ASSERT_GE(buf.capacity, 10); + ASSERT_LE(buf.capacity, 12); + ASSERT_EQ(target.pos, 0); + ASSERT_EQ(target.size, 0); + written = cxBufferWrite("hello", 1, 5, &buf); + EXPECT_EQ(written, 5); + EXPECT_EQ(buf.pos, 0); + EXPECT_EQ(buf.size, 0); + EXPECT_LE(buf.capacity, 12); + EXPECT_EQ(target.pos, 15); + ASSERT_EQ(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; + ASSERT_EQ(buf.capacity, 8); + ASSERT_EQ(buf.pos, 4); + size_t written = cxBufferWrite("foo", 1, 3, &buf); + EXPECT_EQ(written, 3); + ASSERT_EQ(buf.pos, 7); + ASSERT_EQ(buf.size, 7); + ASSERT_EQ(target.pos, 0); + ASSERT_EQ(target.size, 0); + written = cxBufferWrite("hello, world!", 1, 13, &buf); + // " world!" fits into this buffer, the remaining stuff is flushed out + EXPECT_EQ(written, 13); + EXPECT_EQ(buf.pos, 7); + EXPECT_EQ(buf.size, 7); + EXPECT_EQ(buf.capacity, 8); + EXPECT_EQ(memcmp(buf.space, " world!", 7), 0); + EXPECT_EQ(target.pos, 13); + ASSERT_EQ(target.size, 13); + EXPECT_EQ(target.capacity, 16); + EXPECT_EQ(memcmp(target.space, "prepfoohello,", 13), 0); +} + +class BufferSeek : public BufferFixture { +}; + +TEST_F(BufferSeek, SetZero) { + int result = cxBufferSeek(&buf, 0, SEEK_SET); + EXPECT_EQ(result, 0); + EXPECT_EQ(buf.pos, 0); +} + +TEST_F(BufferSeek, SetValid) { + int result = cxBufferSeek(&buf, 5, SEEK_SET); + EXPECT_EQ(result, 0); + EXPECT_EQ(buf.pos, 5); +} + +TEST_F(BufferSeek, SetInvalid) { + ASSERT_EQ(buf.pos, 3); + int result = cxBufferSeek(&buf, 6, SEEK_SET); + EXPECT_NE(result, 0); + EXPECT_EQ(buf.pos, 3); +} + +TEST_F(BufferSeek, CurZero) { + ASSERT_EQ(buf.pos, 3); + int result = cxBufferSeek(&buf, 0, SEEK_CUR); + EXPECT_EQ(result, 0); + EXPECT_EQ(buf.pos, 3); +} + +TEST_F(BufferSeek, CurValidPositive) { + ASSERT_EQ(buf.pos, 3); + int result = cxBufferSeek(&buf, 2, SEEK_CUR); + EXPECT_EQ(result, 0); + EXPECT_EQ(buf.pos, 5); +} + +TEST_F(BufferSeek, CurValidNegative) { + ASSERT_EQ(buf.pos, 3); + int result = cxBufferSeek(&buf, -3, SEEK_CUR); + EXPECT_EQ(result, 0); + EXPECT_EQ(buf.pos, 0); +} + +TEST_F(BufferSeek, CurInvalidPositive) { + ASSERT_EQ(buf.pos, 3); + int result = cxBufferSeek(&buf, 3, SEEK_CUR); + EXPECT_NE(result, 0); + EXPECT_EQ(buf.pos, 3); +} + +TEST_F(BufferSeek, CurInvalidNegative) { + ASSERT_EQ(buf.pos, 3); + int result = cxBufferSeek(&buf, -4, SEEK_CUR); + EXPECT_NE(result, 0); + EXPECT_EQ(buf.pos, 3); +} + +TEST_F(BufferSeek, EndZero) { + ASSERT_EQ(buf.size, 6); + int result = cxBufferSeek(&buf, 0, SEEK_END); + // the (past-the-)end position is always invalid + EXPECT_NE(result, 0); + EXPECT_EQ(buf.pos, 3); +} + +TEST_F(BufferSeek, EndValid) { + ASSERT_EQ(buf.size, 6); + int result = cxBufferSeek(&buf, -6, SEEK_END); + EXPECT_EQ(result, 0); + EXPECT_EQ(buf.pos, 0); +} + +TEST_F(BufferSeek, EndInvalid) { + ASSERT_EQ(buf.size, 6); + int result = cxBufferSeek(&buf, 1, SEEK_END); + EXPECT_NE(result, 0); + EXPECT_EQ(buf.pos, 3); +} + +TEST_F(BufferSeek, WhenceInvalid) { + ASSERT_EQ(buf.size, 6); + ASSERT_EQ(buf.pos, 3); + int result = cxBufferSeek(&buf, 2, 9000); + EXPECT_NE(result, 0); + EXPECT_EQ(buf.size, 6); + EXPECT_EQ(buf.pos, 3); +} + +class BufferEof : public BufferFixture { +}; + +TEST_F(BufferEof, Reached) { + buf.pos = buf.size; + EXPECT_TRUE(cxBufferEof(&buf)); + buf.pos = buf.size - 1; + ASSERT_FALSE(cxBufferEof(&buf)); + cxBufferPut(&buf, 'a'); + EXPECT_TRUE(cxBufferEof(&buf)); +} + +TEST_F(BufferEof, NotReached) { + buf.pos = buf.size - 1; + EXPECT_FALSE(cxBufferEof(&buf)); + buf.pos = 0; + cxBufferWrite("test", 1, 5, &buf); + EXPECT_FALSE(cxBufferEof(&buf)); +} + +class BufferRead : public ::testing::Test { +protected: + CxBuffer buf{}; + + void SetUp() override { + cxBufferInit(&buf, nullptr, 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'); + EXPECT_EQ(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); + ASSERT_EQ(read, 4); + EXPECT_EQ(memcmp(&target, "me d", 4), 0); + EXPECT_EQ(buf.pos, 6); +} + +TEST_F(BufferRead, ReadOutOfBounds) { + buf.pos = 6; + char target[4]; + auto read = cxBufferRead(&target, 1, 4, &buf); + ASSERT_EQ(read, 3); + EXPECT_EQ(memcmp(&target, "ata", 3), 0); + EXPECT_EQ(buf.pos, 9); +} + +TEST_F(BufferRead, ReadOutOfBoundsMultibyte) { + buf.pos = 6; + char target[4]; + target[2] = '\0'; + auto read = cxBufferRead(&target, 2, 2, &buf); + ASSERT_EQ(read, 1); + EXPECT_EQ(memcmp(&target, "at\0", 3), 0); + EXPECT_EQ(buf.pos, 8); +} + +TEST_F(BufferRead, ReadEof) { + buf.pos = 9; + char target[4]; + auto read = cxBufferRead(&target, 1, 1, &buf); + ASSERT_EQ(read, 0); + EXPECT_EQ(buf.pos, 9); +} diff -r bf817b825ed2 -r e081643aae2a tests/test_compare.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_compare.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,127 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2022 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/compare.h" + +#include + +template +static void test_compare( + int (*fnc)( + void const *, + void const * + ) +) { + auto m = std::numeric_limits::max() / 400; + T x, y; + + x = (std::is_signed_v ? -3 : 3) * m; + y = 5 * m; + EXPECT_LT(fnc(&x, &y), 0); + EXPECT_GT(fnc(&y, &x), 0); + + x = 120 * m; + y = 348 * m; + EXPECT_LT(fnc(&x, &y), 0); + EXPECT_GT(fnc(&y, &x), 0); + + if constexpr (std::is_signed_v) { + x = -120 * m; + y = -348 * m; + EXPECT_GT(fnc(&x, &y), 0); + EXPECT_LT(fnc(&y, &x), 0); + } + + x = y; + EXPECT_EQ(fnc(&x, &y), 0); + EXPECT_EQ(fnc(&y, &x), 0); +} + +TEST(Compare, Int) { + test_compare(cx_cmp_int); +} + +TEST(Compare, Longint) { + test_compare(cx_cmp_longint); +} + +TEST(Compare, Longlong) { + test_compare(cx_cmp_longlong); +} + +TEST(Compare, Int16) { + test_compare(cx_cmp_int16); +} + +TEST(Compare, Int32) { + test_compare(cx_cmp_int32); +} + +TEST(Compare, Int64) { + test_compare(cx_cmp_int64); +} + +TEST(Compare, Uint) { + test_compare(cx_cmp_uint); +} + +TEST(Compare, Ulongint) { + test_compare(cx_cmp_ulongint); +} + +TEST(Compare, Ulonglong) { + test_compare(cx_cmp_ulonglong); +} + +TEST(Compare, Uint16) { + test_compare(cx_cmp_uint16); +} + +TEST(Compare, Uint32) { + test_compare(cx_cmp_uint32); +} + +TEST(Compare, Uint64) { + test_compare(cx_cmp_uint64); +} + +TEST(Compare, Float) { + test_compare(cx_cmp_float); +} + +TEST(Compare, Double) { + test_compare(cx_cmp_double); +} + +TEST(Compare, IntPtr) { + test_compare(cx_cmp_intptr); +} + +TEST(Compare, UintPtr) { + test_compare(cx_cmp_uintptr); +} diff -r bf817b825ed2 -r e081643aae2a tests/test_hash_key.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_hash_key.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,87 @@ +/* + * 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/hash_key.h" + +#include + +TEST(cx_hash_key, functions) { + auto str = "my key"; + auto len = strlen(str); + + auto str_key = cx_hash_key_str(str); + auto bytes_key = cx_hash_key_bytes( + reinterpret_cast(str), len); + auto obj_key = cx_hash_key( + reinterpret_cast(str), len); + + EXPECT_EQ(str_key.hash, bytes_key.hash); + EXPECT_EQ(obj_key.hash, bytes_key.hash); + EXPECT_EQ(str_key.len, len); + EXPECT_EQ(bytes_key.len, len); + EXPECT_EQ(bytes_key.len, len); + EXPECT_EQ(str_key.data.cstr, str); + EXPECT_EQ(bytes_key.data.cbytes, reinterpret_cast(str)); + EXPECT_EQ(bytes_key.data.cobj, reinterpret_cast(str)); +} + +TEST(cx_hash_key, empty_string) { + auto str = ""; + + auto str_key = cx_hash_key_str(str); + auto bytes_key = cx_hash_key_bytes( + reinterpret_cast(str), 0); + auto obj_key = cx_hash_key( + reinterpret_cast(str), 0); + + EXPECT_EQ(bytes_key.hash, 4152238450u); + EXPECT_EQ(str_key.hash, 4152238450u); + EXPECT_EQ(obj_key.hash, 4152238450u); + EXPECT_EQ(str_key.len, 0); + EXPECT_EQ(bytes_key.len, 0); + EXPECT_EQ(bytes_key.len, 0); + EXPECT_EQ(str_key.data.cstr, str); + EXPECT_EQ(bytes_key.data.cbytes, reinterpret_cast(str)); + EXPECT_EQ(bytes_key.data.cobj, reinterpret_cast(str)); +} + +TEST(cx_hash_key, null_ptr) { + auto str_key = cx_hash_key_str(nullptr); + auto bytes_key = cx_hash_key_bytes(nullptr, 0); + auto obj_key = cx_hash_key(nullptr, 0); + + EXPECT_EQ(bytes_key.hash, 1574210520u); + EXPECT_EQ(str_key.hash, 1574210520u); + EXPECT_EQ(obj_key.hash, 1574210520u); + EXPECT_EQ(str_key.len, 0); + EXPECT_EQ(bytes_key.len, 0); + EXPECT_EQ(bytes_key.len, 0); + EXPECT_EQ(str_key.data.cstr, nullptr); + EXPECT_EQ(bytes_key.data.cbytes, nullptr); + EXPECT_EQ(bytes_key.data.cobj, nullptr); +} diff -r bf817b825ed2 -r e081643aae2a tests/test_list.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_list.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,1077 @@ +/* + * 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/linked_list.h" +#include "cx/array_list.h" +#include "cx/utils.h" +#include "cx/compare.h" +#include "util_allocator.h" + +#include +#include +#include +#include +#include + +struct node { + node *next = nullptr; + node *prev = nullptr; + int data = 0; +}; + +const ptrdiff_t loc_prev = offsetof(struct node, prev); +const ptrdiff_t loc_next = offsetof(struct node, next); +const ptrdiff_t loc_data = offsetof(struct node, data); + +struct node_test_data { + node *begin = nullptr; + + explicit node_test_data(node *begin) : begin(begin) { + auto n = begin; + while (n != nullptr) { + nodes.push_back(n); + n = n->next; + } + } + + node_test_data(node_test_data &) = delete; + + node_test_data(node_test_data &&) = default; + + ~node_test_data() { + for (auto &&n: nodes) delete n; + } + +private: + std::vector nodes; +}; + +static node_test_data create_nodes_test_data(size_t len) { + if (len == 0) return node_test_data{nullptr}; + auto begin = new node; + auto prev = begin; + for (size_t i = 1; i < len; i++) { + auto n = new node; + cx_linked_list_link(prev, n, loc_prev, loc_next); + prev = n; + } + return node_test_data{begin}; +} + +template +static node_test_data create_nodes_test_data( + InputIter begin, + InputIter end +) { + if (begin == end) return node_test_data{nullptr}; + node *first = new node; + first->data = *begin; + node *prev = first; + begin++; + for (; begin != end; begin++) { + auto n = new node; + n->data = *begin; + cx_linked_list_link(prev, n, loc_prev, loc_next); + prev = n; + } + return node_test_data{first}; +} + +static node_test_data create_nodes_test_data(std::initializer_list data) { + return create_nodes_test_data(data.begin(), data.end()); +} + +template +struct int_test_data { + std::array data; + + int_test_data() { + cx_for_n (i, N) data[i] = ::rand(); // NOLINT(cert-msc50-cpp) + } +}; + +TEST(LinkedList_LowLevel, link_unlink) { + node a, b, c; + + cx_linked_list_link(&a, &b, loc_prev, loc_next); + EXPECT_EQ(a.prev, nullptr); + EXPECT_EQ(a.next, &b); + EXPECT_EQ(b.prev, &a); + EXPECT_EQ(b.next, nullptr); + + cx_linked_list_unlink(&a, &b, loc_prev, loc_next); + EXPECT_EQ(a.prev, nullptr); + EXPECT_EQ(a.next, nullptr); + EXPECT_EQ(b.prev, nullptr); + EXPECT_EQ(b.next, nullptr); + + cx_linked_list_link(&b, &c, loc_prev, loc_next); + cx_linked_list_link(&a, &b, loc_prev, loc_next); + cx_linked_list_unlink(&b, &c, loc_prev, loc_next); + EXPECT_EQ(a.prev, nullptr); + EXPECT_EQ(a.next, &b); + EXPECT_EQ(b.prev, &a); + EXPECT_EQ(b.next, nullptr); + EXPECT_EQ(c.prev, nullptr); + EXPECT_EQ(c.next, nullptr); +} + +TEST(LinkedList_LowLevel, cx_linked_list_at) { + node a, b, c, d; + cx_linked_list_link(&a, &b, loc_prev, loc_next); + cx_linked_list_link(&b, &c, loc_prev, loc_next); + cx_linked_list_link(&c, &d, loc_prev, loc_next); + + EXPECT_EQ(cx_linked_list_at(&a, 0, loc_next, 0), &a); + EXPECT_EQ(cx_linked_list_at(&a, 0, loc_next, 1), &b); + EXPECT_EQ(cx_linked_list_at(&a, 0, loc_next, 2), &c); + EXPECT_EQ(cx_linked_list_at(&a, 0, loc_next, 3), &d); + EXPECT_EQ(cx_linked_list_at(&a, 0, loc_next, 4), nullptr); + + EXPECT_EQ(cx_linked_list_at(&b, 1, loc_prev, 0), &a); + EXPECT_EQ(cx_linked_list_at(&b, 1, loc_next, 1), &b); + EXPECT_EQ(cx_linked_list_at(&b, 1, loc_next, 2), &c); + EXPECT_EQ(cx_linked_list_at(&b, 1, loc_next, 3), &d); + EXPECT_EQ(cx_linked_list_at(&b, 1, loc_next, 4), nullptr); + + EXPECT_EQ(cx_linked_list_at(&d, 3, loc_prev, 0), &a); + EXPECT_EQ(cx_linked_list_at(&d, 3, loc_prev, 1), &b); +} + +TEST(LinkedList_LowLevel, cx_linked_list_find) { + auto testdata = create_nodes_test_data({2, 4, 6, 8}); + auto list = testdata.begin; + int s; + + s = 2; + EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 0); + s = 4; + EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 1); + s = 6; + EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 2); + s = 8; + EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 3); + s = 10; + EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 4); + s = -2; + EXPECT_EQ(cx_linked_list_find(list, loc_next, loc_data, cx_cmp_int, &s), 4); +} + +TEST(LinkedList_LowLevel, cx_linked_list_compare) { + auto ta = create_nodes_test_data({2, 4, 6, 8}); + auto tb = create_nodes_test_data({2, 4, 6}); + auto tc = create_nodes_test_data({2, 4, 6, 9}); + auto la = ta.begin, lb = tb.begin, lc = tc.begin; + + EXPECT_GT(cx_linked_list_compare(la, lb, loc_next, loc_data, cx_cmp_int), 0); + EXPECT_LT(cx_linked_list_compare(lb, la, loc_next, loc_data, cx_cmp_int), 0); + EXPECT_GT(cx_linked_list_compare(lc, la, loc_next, loc_data, cx_cmp_int), 0); + EXPECT_LT(cx_linked_list_compare(la, lc, loc_next, loc_data, cx_cmp_int), 0); + EXPECT_EQ(cx_linked_list_compare(la, la, loc_next, loc_data, cx_cmp_int), 0); +} + +TEST(LinkedList_LowLevel, cx_linked_list_add) { + // test with begin, end / prev, next + { + node nodes[4]; + void *begin = nullptr, *end = nullptr; + + cx_linked_list_add(&begin, &end, loc_prev, loc_next, &nodes[0]); + EXPECT_EQ(begin, &nodes[0]); + EXPECT_EQ(end, &nodes[0]); + EXPECT_EQ(nodes[0].prev, nullptr); + EXPECT_EQ(nodes[0].next, nullptr); + + cx_linked_list_add(&begin, &end, loc_prev, loc_next, &nodes[1]); + EXPECT_EQ(begin, &nodes[0]); + EXPECT_EQ(end, &nodes[1]); + EXPECT_EQ(nodes[0].next, &nodes[1]); + EXPECT_EQ(nodes[1].prev, &nodes[0]); + } + + // test with begin only / prev, next + { + node nodes[4]; + void *begin = nullptr; + + cx_linked_list_add(&begin, nullptr, loc_prev, loc_next, &nodes[0]); + EXPECT_EQ(begin, &nodes[0]); + cx_linked_list_add(&begin, nullptr, loc_prev, loc_next, &nodes[1]); + EXPECT_EQ(begin, &nodes[0]); + EXPECT_EQ(nodes[0].next, &nodes[1]); + EXPECT_EQ(nodes[1].prev, &nodes[0]); + + cx_linked_list_add(&begin, nullptr, loc_prev, loc_next, &nodes[2]); + EXPECT_EQ(nodes[1].next, &nodes[2]); + EXPECT_EQ(nodes[2].prev, &nodes[1]); + } + + // test with end only / prev, next + { + node nodes[4]; + void *end = nullptr; + + cx_linked_list_add(nullptr, &end, loc_prev, loc_next, &nodes[0]); + EXPECT_EQ(end, &nodes[0]); + cx_linked_list_add(nullptr, &end, loc_prev, loc_next, &nodes[1]); + EXPECT_EQ(end, &nodes[1]); + EXPECT_EQ(nodes[0].next, &nodes[1]); + EXPECT_EQ(nodes[1].prev, &nodes[0]); + + cx_linked_list_add(nullptr, &end, loc_prev, loc_next, &nodes[2]); + EXPECT_EQ(end, &nodes[2]); + EXPECT_EQ(nodes[1].next, &nodes[2]); + EXPECT_EQ(nodes[2].prev, &nodes[1]); + } + + // test with begin, end / next + { + node nodes[4]; + void *begin = nullptr, *end = nullptr; + + cx_linked_list_add(&begin, &end, -1, loc_next, &nodes[0]); + EXPECT_EQ(begin, &nodes[0]); + EXPECT_EQ(end, &nodes[0]); + cx_linked_list_add(&begin, &end, -1, loc_next, &nodes[1]); + EXPECT_EQ(end, &nodes[1]); + EXPECT_EQ(nodes[0].next, &nodes[1]); + EXPECT_EQ(nodes[1].prev, nullptr); + } +} + +TEST(LinkedList_LowLevel, cx_linked_list_prepend) { + // test with begin, end / prev, next + { + node nodes[4]; + void *begin = nullptr, *end = nullptr; + + cx_linked_list_prepend(&begin, &end, loc_prev, loc_next, &nodes[0]); + EXPECT_EQ(begin, &nodes[0]); + EXPECT_EQ(end, &nodes[0]); + EXPECT_EQ(nodes[0].prev, nullptr); + EXPECT_EQ(nodes[0].next, nullptr); + + cx_linked_list_prepend(&begin, &end, loc_prev, loc_next, &nodes[1]); + EXPECT_EQ(begin, &nodes[1]); + EXPECT_EQ(end, &nodes[0]); + EXPECT_EQ(nodes[1].next, &nodes[0]); + EXPECT_EQ(nodes[0].prev, &nodes[1]); + } + + // test with begin only / prev, next + { + node nodes[4]; + void *begin = nullptr; + + cx_linked_list_prepend(&begin, nullptr, loc_prev, loc_next, &nodes[0]); + EXPECT_EQ(begin, &nodes[0]); + cx_linked_list_prepend(&begin, nullptr, loc_prev, loc_next, &nodes[1]); + EXPECT_EQ(begin, &nodes[1]); + EXPECT_EQ(nodes[1].next, &nodes[0]); + EXPECT_EQ(nodes[0].prev, &nodes[1]); + + cx_linked_list_prepend(&begin, nullptr, loc_prev, loc_next, &nodes[2]); + EXPECT_EQ(begin, &nodes[2]); + EXPECT_EQ(nodes[2].next, &nodes[1]); + EXPECT_EQ(nodes[1].prev, &nodes[2]); + } + + // test with end only / prev, next + { + node nodes[4]; + void *end = nullptr; + + cx_linked_list_prepend(nullptr, &end, loc_prev, loc_next, &nodes[0]); + EXPECT_EQ(end, &nodes[0]); + cx_linked_list_prepend(nullptr, &end, loc_prev, loc_next, &nodes[1]); + EXPECT_EQ(end, &nodes[0]); + EXPECT_EQ(nodes[1].next, &nodes[0]); + EXPECT_EQ(nodes[0].prev, &nodes[1]); + + cx_linked_list_prepend(nullptr, &end, loc_prev, loc_next, &nodes[2]); + EXPECT_EQ(end, &nodes[0]); + EXPECT_EQ(nodes[2].next, &nodes[1]); + EXPECT_EQ(nodes[1].prev, &nodes[2]); + } + + // test with begin, end / next + { + node nodes[4]; + void *begin = nullptr, *end = nullptr; + + cx_linked_list_prepend(&begin, &end, -1, loc_next, &nodes[0]); + EXPECT_EQ(begin, &nodes[0]); + EXPECT_EQ(end, &nodes[0]); + cx_linked_list_prepend(&begin, &end, -1, loc_next, &nodes[1]); + cx_linked_list_prepend(&begin, &end, -1, loc_next, &nodes[2]); + EXPECT_EQ(begin, &nodes[2]); + EXPECT_EQ(end, &nodes[0]); + EXPECT_EQ(nodes[1].next, &nodes[0]); + EXPECT_EQ(nodes[2].next, &nodes[1]); + EXPECT_EQ(nodes[1].prev, nullptr); + EXPECT_EQ(nodes[0].prev, nullptr); + } +} + +TEST(LinkedList_LowLevel, cx_linked_list_insert) { + // insert mid list + { + node nodes[4]; + void *begin = &nodes[0], *end = &nodes[2]; + + cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); + cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); + + cx_linked_list_insert(&begin, &end, loc_prev, loc_next, &nodes[1], &nodes[3]); + EXPECT_EQ(begin, &nodes[0]); + EXPECT_EQ(end, &nodes[2]); + EXPECT_EQ(nodes[1].next, &nodes[3]); + EXPECT_EQ(nodes[2].prev, &nodes[3]); + EXPECT_EQ(nodes[3].prev, &nodes[1]); + EXPECT_EQ(nodes[3].next, &nodes[2]); + } + + // insert end + { + node nodes[4]; + void *begin = &nodes[0], *end = &nodes[2]; + + cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); + cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); + + cx_linked_list_insert(&begin, &end, loc_prev, loc_next, &nodes[2], &nodes[3]); + EXPECT_EQ(begin, &nodes[0]); + EXPECT_EQ(end, &nodes[3]); + EXPECT_EQ(nodes[2].next, &nodes[3]); + EXPECT_EQ(nodes[3].prev, &nodes[2]); + EXPECT_EQ(nodes[3].next, nullptr); + } + + // insert begin + { + node nodes[4]; + void *begin = &nodes[0], *end = &nodes[2]; + + cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); + cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); + + cx_linked_list_insert(&begin, &end, loc_prev, loc_next, nullptr, &nodes[3]); + EXPECT_EQ(begin, &nodes[3]); + EXPECT_EQ(end, &nodes[2]); + EXPECT_EQ(nodes[0].prev, &nodes[3]); + EXPECT_EQ(nodes[3].prev, nullptr); + EXPECT_EQ(nodes[3].next, &nodes[0]); + } +} + +TEST(LinkedList_LowLevel, cx_linked_list_insert_chain) { + // insert mid list + { + node nodes[5]; + void *begin = &nodes[0], *end = &nodes[2]; + + cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); + cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); + cx_linked_list_link(&nodes[3], &nodes[4], loc_prev, loc_next); + + cx_linked_list_insert_chain(&begin, &end, loc_prev, loc_next, &nodes[1], &nodes[3], nullptr); + EXPECT_EQ(begin, &nodes[0]); + EXPECT_EQ(end, &nodes[2]); + EXPECT_EQ(nodes[1].next, &nodes[3]); + EXPECT_EQ(nodes[2].prev, &nodes[4]); + EXPECT_EQ(nodes[3].prev, &nodes[1]); + EXPECT_EQ(nodes[4].next, &nodes[2]); + } + + // insert end + { + node nodes[5]; + void *begin = &nodes[0], *end = &nodes[2]; + + cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); + cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); + cx_linked_list_link(&nodes[3], &nodes[4], loc_prev, loc_next); + + cx_linked_list_insert_chain(&begin, &end, loc_prev, loc_next, &nodes[2], &nodes[3], nullptr); + EXPECT_EQ(begin, &nodes[0]); + EXPECT_EQ(end, &nodes[4]); + EXPECT_EQ(nodes[2].next, &nodes[3]); + EXPECT_EQ(nodes[3].prev, &nodes[2]); + EXPECT_EQ(nodes[4].next, nullptr); + } + + // insert begin + { + node nodes[5]; + void *begin = &nodes[0], *end = &nodes[2]; + + cx_linked_list_link(&nodes[0], &nodes[1], loc_prev, loc_next); + cx_linked_list_link(&nodes[1], &nodes[2], loc_prev, loc_next); + cx_linked_list_link(&nodes[3], &nodes[4], loc_prev, loc_next); + + cx_linked_list_insert_chain(&begin, &end, loc_prev, loc_next, nullptr, &nodes[3], nullptr); + EXPECT_EQ(begin, &nodes[3]); + EXPECT_EQ(end, &nodes[2]); + EXPECT_EQ(nodes[0].prev, &nodes[4]); + EXPECT_EQ(nodes[3].prev, nullptr); + EXPECT_EQ(nodes[4].next, &nodes[0]); + } +} + +TEST(LinkedList_LowLevel, cx_linked_list_first) { + auto testdata = create_nodes_test_data(3); + auto begin = testdata.begin; + EXPECT_EQ(cx_linked_list_first(begin, loc_prev), begin); + EXPECT_EQ(cx_linked_list_first(begin->next, loc_prev), begin); + EXPECT_EQ(cx_linked_list_first(begin->next->next, loc_prev), begin); +} + +TEST(LinkedList_LowLevel, cx_linked_list_last) { + auto testdata = create_nodes_test_data(3); + auto begin = testdata.begin; + auto end = begin->next->next; + EXPECT_EQ(cx_linked_list_last(begin, loc_next), end); + EXPECT_EQ(cx_linked_list_last(begin->next, loc_next), end); + EXPECT_EQ(cx_linked_list_last(begin->next->next, loc_next), end); +} + +TEST(LinkedList_LowLevel, cx_linked_list_prev) { + auto testdata = create_nodes_test_data(3); + auto begin = testdata.begin; + EXPECT_EQ(cx_linked_list_prev(begin, loc_next, begin), nullptr); + EXPECT_EQ(cx_linked_list_prev(begin, loc_next, begin->next), begin); + EXPECT_EQ(cx_linked_list_prev(begin, loc_next, begin->next->next), begin->next); +} + +TEST(LinkedList_LowLevel, cx_linked_list_remove) { + auto testdata = create_nodes_test_data({2, 4, 6}); + auto begin = reinterpret_cast(testdata.begin); + auto first = testdata.begin; + auto second = first->next; + auto third = second->next; + auto end = reinterpret_cast(third); + + cx_linked_list_remove(&begin, &end, loc_prev, loc_next, second); + EXPECT_EQ(begin, first); + EXPECT_EQ(end, third); + EXPECT_EQ(first->prev, nullptr); + EXPECT_EQ(first->next, third); + EXPECT_EQ(third->prev, first); + EXPECT_EQ(third->next, nullptr); + + cx_linked_list_remove(&begin, &end, loc_prev, loc_next, third); + EXPECT_EQ(begin, first); + EXPECT_EQ(end, first); + EXPECT_EQ(first->prev, nullptr); + EXPECT_EQ(first->next, nullptr); + + cx_linked_list_remove(&begin, &end, loc_prev, loc_next, first); + EXPECT_EQ(begin, nullptr); + EXPECT_EQ(end, nullptr); +} + +TEST(LinkedList_LowLevel, cx_linked_list_size) { + EXPECT_EQ(cx_linked_list_size(nullptr, loc_next), 0); + + { + auto testdata = create_nodes_test_data(5); + EXPECT_EQ(cx_linked_list_size(testdata.begin, loc_next), 5); + } + + { + auto testdata = create_nodes_test_data(13); + EXPECT_EQ(cx_linked_list_size(testdata.begin, loc_next), 13); + } +} + +TEST(LinkedList_LowLevel, cx_linked_list_sort) { + int_test_data<1500> testdata; + std::array sorted{}; + std::partial_sort_copy(testdata.data.begin(), testdata.data.end(), sorted.begin(), sorted.end()); + + auto scrambled = create_nodes_test_data(testdata.data.begin(), testdata.data.end()); + void *begin = scrambled.begin; + void *end = cx_linked_list_last(begin, loc_next); + + cx_linked_list_sort(&begin, &end, loc_prev, loc_next, loc_data, cx_cmp_int); + + node *check = reinterpret_cast(begin); + node *check_last = nullptr; + cx_for_n (i, sorted.size()) { + EXPECT_EQ(check->data, sorted[i]); + EXPECT_EQ(check->prev, check_last); + if (i < sorted.size() - 1) { + ASSERT_NE(check->next, nullptr); + } + check_last = check; + check = check->next; + } + EXPECT_EQ(check, nullptr); + EXPECT_EQ(end, check_last); +} + +TEST(LinkedList_LowLevel, cx_linked_list_reverse) { + auto testdata = create_nodes_test_data({2, 4, 6, 8}); + auto expected = create_nodes_test_data({8, 6, 4, 2}); + + auto begin = reinterpret_cast(testdata.begin); + auto end = cx_linked_list_last(begin, loc_next); + auto orig_begin = begin, orig_end = end; + + cx_linked_list_reverse(&begin, &end, loc_prev, loc_next); + EXPECT_EQ(end, orig_begin); + EXPECT_EQ(begin, orig_end); + EXPECT_EQ(cx_linked_list_compare(begin, expected.begin, loc_next, loc_data, cx_cmp_int), 0); +} + +class HighLevelTest : public ::testing::Test { + mutable std::unordered_set lists; +protected: + CxTestingAllocator testingAllocator; + + void TearDown() override { + for (auto &&l: lists) cxListDestroy(l); + EXPECT_TRUE(testingAllocator.verify()); + } + + static constexpr size_t testdata_len = 250; + int_test_data testdata; + + auto autofree(CxList *list) const -> CxList * { + if (list != nullptr) lists.insert(list); + return list; + } + + auto linkedListFromTestData() const -> CxList * { + auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int))); + cxListAddArray(list, testdata.data.data(), testdata_len); + return list; + } + + auto pointerLinkedListFromTestData() const -> CxList * { + auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *))); + cxListStorePointers(list); + // note: cannot use cxListAddArray() because we don't have a list of pointers + cx_for_n(i, testdata_len) cxListAdd(list, &testdata.data[i]); + return list; + } + + auto arrayListFromTestData() const -> CxList * { + auto list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), testdata_len)); + cxListAddArray(list, testdata.data.data(), testdata_len); + return list; + } + + void verifyCreate(CxList *list) const { + EXPECT_EQ(list->content_destructor_type, CX_DESTRUCTOR_NONE); + EXPECT_EQ(list->size, 0); + EXPECT_EQ(list->allocator, &testingAllocator); + EXPECT_EQ(list->cmpfunc, cx_cmp_int); + } + + void verifyAdd( + CxList *list, + bool as_pointer + ) { + auto len = testdata_len; + cx_for_n (i, len) EXPECT_EQ(cxListAdd(list, &testdata.data[i]), 0); + EXPECT_EQ(list->size, len); + EXPECT_GE(list->capacity, list->size); + cx_for_n (i, len) EXPECT_EQ(*(int *) cxListAt(list, i), testdata.data[i]); + cx_for_n (i, len) ++testdata.data[i]; + if (as_pointer) { + cx_for_n (i, len) EXPECT_EQ(*(int *) cxListAt(list, i), testdata.data[i]); + } else { + cx_for_n (i, len) EXPECT_EQ(*(int *) cxListAt(list, i), testdata.data[i] - 1); + } + } + + static void verifyInsert(CxList *list) { + int a = 5, b = 47, c = 13, d = 42; + + EXPECT_NE(cxListInsert(list, 1, &a), 0); + EXPECT_EQ(list->size, 0); + EXPECT_EQ(cxListInsert(list, 0, &a), 0); + EXPECT_EQ(list->size, 1); + EXPECT_EQ(cxListInsert(list, 0, &b), 0); + EXPECT_EQ(list->size, 2); + EXPECT_EQ(cxListInsert(list, 1, &c), 0); + EXPECT_EQ(list->size, 3); + EXPECT_EQ(cxListInsert(list, 3, &d), 0); + + ASSERT_EQ(list->size, 4); + EXPECT_GE(list->capacity, list->size); + + EXPECT_EQ(*(int *) cxListAt(list, 0), 47); + EXPECT_EQ(*(int *) cxListAt(list, 1), 13); + EXPECT_EQ(*(int *) cxListAt(list, 2), 5); + EXPECT_EQ(*(int *) cxListAt(list, 3), 42); + } + + static void verifyInsertArray( + CxList *list, + bool pointers = false + ) { + int a[5] = {5, 47, 11, 13, 42}; + int b[5] = {9, 18, 72, 50, 7}; + int *aptr[5]; + int *bptr[5]; + cx_for_n(i, 5) { + aptr[i] = &a[i]; + bptr[i] = &b[i]; + } + + size_t inserted; + + if (pointers) { + inserted = cxListInsertArray(list, 0, aptr, 5); + } else { + inserted = cxListInsertArray(list, 0, a, 5); + } + EXPECT_EQ(inserted, 5); + EXPECT_EQ(*(int *) cxListAt(list, 0), 5); + EXPECT_EQ(*(int *) cxListAt(list, 1), 47); + EXPECT_EQ(*(int *) cxListAt(list, 2), 11); + EXPECT_EQ(*(int *) cxListAt(list, 3), 13); + EXPECT_EQ(*(int *) cxListAt(list, 4), 42); + if (pointers) { + inserted = cxListInsertArray(list, 3, bptr, 5); + } else { + inserted = cxListInsertArray(list, 3, b, 5); + } + EXPECT_EQ(inserted, 5); + EXPECT_EQ(*(int *) cxListAt(list, 0), 5); + EXPECT_EQ(*(int *) cxListAt(list, 1), 47); + EXPECT_EQ(*(int *) cxListAt(list, 2), 11); + EXPECT_EQ(*(int *) cxListAt(list, 3), 9); + EXPECT_EQ(*(int *) cxListAt(list, 4), 18); + EXPECT_EQ(*(int *) cxListAt(list, 5), 72); + EXPECT_EQ(*(int *) cxListAt(list, 6), 50); + EXPECT_EQ(*(int *) cxListAt(list, 7), 7); + EXPECT_EQ(*(int *) cxListAt(list, 8), 13); + EXPECT_EQ(*(int *) cxListAt(list, 9), 42); + } + + void verifyRemove(CxList *list) const { + EXPECT_EQ(cxListRemove(list, 2), 0); + EXPECT_EQ(cxListRemove(list, 4), 0); + EXPECT_EQ(list->size, testdata_len - 2); + EXPECT_GE(list->capacity, list->size); + EXPECT_EQ(*(int *) cxListAt(list, 0), testdata.data[0]); + EXPECT_EQ(*(int *) cxListAt(list, 1), testdata.data[1]); + EXPECT_EQ(*(int *) cxListAt(list, 2), testdata.data[3]); + EXPECT_EQ(*(int *) cxListAt(list, 3), testdata.data[4]); + EXPECT_EQ(*(int *) cxListAt(list, 4), testdata.data[6]); + + EXPECT_EQ(cxListRemove(list, 0), 0); + EXPECT_EQ(list->size, testdata_len - 3); + EXPECT_GE(list->capacity, list->size); + EXPECT_EQ(*(int *) cxListAt(list, 0), testdata.data[1]); + EXPECT_EQ(*(int *) cxListAt(list, 1), testdata.data[3]); + + EXPECT_NE(cxListRemove(list, testdata_len), 0); + } + + void verifyAt(CxList *list) const { + auto len = testdata_len; + EXPECT_EQ(list->size, len); + cx_for_n (i, len) { + EXPECT_EQ(*(int *) cxListAt(list, i), testdata.data[i]); + } + EXPECT_EQ(cxListAt(list, list->size), nullptr); + } + + void verifyFind(CxList *list) const { + cx_for_n (attempt, 25) { + size_t exp = rand() % testdata_len; // NOLINT(cert-msc50-cpp) + int val = testdata.data[exp]; + // randomly picked number could occur earlier in list - find first position + cx_for_n (i, exp) { + if (testdata.data[i] == val) { + exp = i; + break; + } + } + EXPECT_EQ(cxListFind(list, &val), exp); + } + } + + void verifySort(CxList *list) const { + std::array expected{}; + std::partial_sort_copy(testdata.data.begin(), testdata.data.end(), expected.begin(), expected.end()); + cxListSort(list); + cx_for_n (i, testdata_len) ASSERT_EQ(*(int *) cxListAt(list, i), expected[i]); + } + + void verifyIterator(CxList *list) const { + int i = 0; + auto iter = cxListBeginMut(list); + cx_foreach(int*, x, iter) { + ASSERT_EQ(iter.index, (size_t) (i + 1) / 2); + ASSERT_EQ(*x, testdata.data[i]); + if (i % 2 == 1) cxIteratorFlagRemoval(iter); + i++; + } + auto len = testdata_len; + EXPECT_EQ(i, len); + ASSERT_EQ(list->size, len / 2); + cx_for_n(j, len / 2) ASSERT_EQ(*(int *) cxListAt(list, j), testdata.data[j * 2]); + } + + static void verifyInsertViaIterator(CxList *list) { + int newdata[] = {10, 20, 30, 40, 50}; + + auto iter = cxListMutIterator(list, 2); + EXPECT_TRUE(cxIteratorValid(iter)); + EXPECT_EQ(iter.index, 2); + EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 2); + cxListInsertAfter(&iter, &newdata[0]); + EXPECT_TRUE(cxIteratorValid(iter)); + EXPECT_EQ(iter.index, 2); + EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 2); + cxListInsertBefore(&iter, &newdata[1]); + EXPECT_TRUE(cxIteratorValid(iter)); + EXPECT_EQ(iter.index, 3); + EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 2); + + iter = cxListBeginMut(list); + cxListInsertBefore(&iter, &newdata[2]); + EXPECT_TRUE(cxIteratorValid(iter)); + EXPECT_EQ(iter.index, 1); + EXPECT_EQ(*(int *) cxIteratorCurrent(iter), 0); + iter = cxListMutIterator(list, list->size); + cxListInsertBefore(&iter, &newdata[3]); + EXPECT_FALSE(cxIteratorValid(iter)); + EXPECT_EQ(iter.index, 9); + iter = cxListMutIterator(list, list->size); + cxListInsertAfter(&iter, &newdata[4]); + EXPECT_FALSE(cxIteratorValid(iter)); + EXPECT_EQ(iter.index, 10); + + int expdata[] = {30, 0, 1, 20, 2, 10, 3, 4, 40, 50}; + cx_for_n (j, 10) EXPECT_EQ(*(int *) cxListAt(list, j), expdata[j]); + } + + void verifyReverse(CxList *list) const { + cxListReverse(list); + cx_for_n(i, testdata_len) { + ASSERT_EQ(*(int *) cxListAt(list, i), testdata.data[testdata_len - 1 - i]); + } + } + + static void verifyCompare( + CxList *left, + CxList *right + ) { + EXPECT_EQ(cxListCompare(left, right), 0); + int x = 42; + cxListAdd(left, &x); + ASSERT_GT(left->size, right->size); + EXPECT_GT(cxListCompare(left, right), 0); + EXPECT_LT(cxListCompare(right, left), 0); + cxListAdd(right, &x); + ASSERT_EQ(left->size, right->size); + EXPECT_EQ(cxListCompare(left, right), 0); + int a = 5, b = 10; + cxListInsert(left, 15, &a); + cxListInsert(right, 15, &b); + ASSERT_EQ(left->size, right->size); + EXPECT_LT(cxListCompare(left, right), 0); + EXPECT_GT(cxListCompare(right, left), 0); + *(int *) cxListAt(left, 15) = 10; + EXPECT_EQ(cxListCompare(left, right), 0); + } +}; + +class LinkedList : public HighLevelTest { +}; + +class PointerLinkedList : public HighLevelTest { +}; + +class ArrayList : public HighLevelTest { +}; + +TEST_F(PointerLinkedList, cxListStorePointers) { + auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, 47)); + EXPECT_FALSE(cxListIsStoringPointers(list)); + cxListStorePointers(list); + EXPECT_EQ(list->itemsize, sizeof(void *)); + EXPECT_NE(list->cl, nullptr); + EXPECT_NE(list->climpl, nullptr); + EXPECT_TRUE(cxListIsStoringPointers(list)); + cxListStoreObjects(list); + EXPECT_NE(list->cl, nullptr); + EXPECT_EQ(list->climpl, nullptr); + EXPECT_FALSE(cxListIsStoringPointers(list)); +} + +TEST_F(LinkedList, cxLinkedListCreate) { + CxList *list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int))); + ASSERT_NE(list, nullptr); + EXPECT_EQ(list->itemsize, sizeof(int)); + EXPECT_EQ(list->capacity, (size_t) -1); + verifyCreate(list); +} + +TEST_F(ArrayList, cxArrayListCreate) { + CxList *list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 8)); + ASSERT_NE(list, nullptr); + EXPECT_EQ(list->itemsize, sizeof(int)); + EXPECT_EQ(list->capacity, 8); + verifyCreate(list); +} + +TEST_F(LinkedList, cxListAdd) { + auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int))); + verifyAdd(list, false); +} + +TEST_F(PointerLinkedList, cxListAdd) { + auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *))); + cxListStorePointers(list); + verifyAdd(list, true); +} + +TEST_F(ArrayList, cxListAdd) { + auto list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 8)); + verifyAdd(list, false); +} + +TEST_F(LinkedList, cxListInsert) { + verifyInsert(autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int)))); +} + +TEST_F(PointerLinkedList, cxListInsert) { + auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *))); + cxListStorePointers(list); + verifyInsert(list); +} + +TEST_F(ArrayList, cxListInsert) { + verifyInsert(autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 2))); +} + +TEST_F(LinkedList, cxListInsertArray) { + verifyInsertArray(autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int)))); +} + +TEST_F(PointerLinkedList, cxListInsertArray) { + auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *))); + cxListStorePointers(list); + verifyInsertArray(list, true); +} + +TEST_F(ArrayList, cxListInsertArray) { + verifyInsertArray(autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 4))); +} + +TEST_F(LinkedList, cxListRemove) { + verifyRemove(linkedListFromTestData()); +} + +TEST_F(PointerLinkedList, cxListRemove) { + verifyRemove(pointerLinkedListFromTestData()); +} + +TEST_F(ArrayList, cxListRemove) { + verifyRemove(arrayListFromTestData()); +} + +TEST_F(LinkedList, cxListAt) { + verifyAt(linkedListFromTestData()); +} + +TEST_F(PointerLinkedList, cxListAt) { + verifyAt(pointerLinkedListFromTestData()); +} + +TEST_F(ArrayList, cxListAt) { + verifyAt(arrayListFromTestData()); +} + +TEST_F(LinkedList, cxListFind) { + verifyFind(linkedListFromTestData()); +} + +TEST_F(PointerLinkedList, cxListFind) { + verifyFind(pointerLinkedListFromTestData()); +} + +TEST_F(ArrayList, cxListFind) { + verifyFind(arrayListFromTestData()); +} + +TEST_F(LinkedList, cxListSort) { + verifySort(linkedListFromTestData()); +} + +TEST_F(PointerLinkedList, cxListSort) { + verifySort(pointerLinkedListFromTestData()); +} + +TEST_F(ArrayList, cxListSort) { + verifySort(arrayListFromTestData()); +} + +TEST_F(LinkedList, Iterator) { + verifyIterator(linkedListFromTestData()); +} + +TEST_F(PointerLinkedList, Iterator) { + verifyIterator(pointerLinkedListFromTestData()); +} + +TEST_F(ArrayList, Iterator) { + verifyIterator(arrayListFromTestData()); +} + +TEST_F(LinkedList, InsertViaIterator) { + int fivenums[] = {0, 1, 2, 3, 4, 5}; + CxList *list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int))); + cxListAddArray(list, fivenums, 5); + verifyInsertViaIterator(list); +} + +TEST_F(PointerLinkedList, InsertViaIterator) { + int fivenums[] = {0, 1, 2, 3, 4, 5}; + auto list = autofree(cxLinkedListCreate(&testingAllocator, cx_cmp_int, sizeof(int *))); + cxListStorePointers(list); + // note: cannot use cxListAddArray() because we don't have a list of pointers + cx_for_n(i, 5) cxListAdd(list, &fivenums[i]); + verifyInsertViaIterator(list); +} + +TEST_F(ArrayList, InsertViaIterator) { + int fivenums[] = {0, 1, 2, 3, 4, 5}; + CxList *list = autofree(cxArrayListCreate(&testingAllocator, cx_cmp_int, sizeof(int), 4)); + cxListAddArray(list, fivenums, 5); + verifyInsertViaIterator(list); +} + +TEST_F(LinkedList, cxListReverse) { + verifyReverse(linkedListFromTestData()); +} + +TEST_F(PointerLinkedList, cxListReverse) { + verifyReverse(pointerLinkedListFromTestData()); +} + +TEST_F(ArrayList, cxListReverse) { + verifyReverse(arrayListFromTestData()); +} + +TEST_F(LinkedList, cxListCompare) { + auto left = linkedListFromTestData(); + auto right = linkedListFromTestData(); + verifyCompare(left, right); +} + +TEST_F(LinkedList, cxListCompareWithPtrList) { + auto left = linkedListFromTestData(); + auto right = pointerLinkedListFromTestData(); + verifyCompare(left, right); +} + +TEST_F(LinkedList, cxListCompareWithArrayList) { + auto left = linkedListFromTestData(); + auto right = arrayListFromTestData(); + verifyCompare(left, right); +} + +TEST_F(PointerLinkedList, cxListCompare) { + auto left = pointerLinkedListFromTestData(); + auto right = pointerLinkedListFromTestData(); + verifyCompare(left, right); +} + +TEST_F(PointerLinkedList, cxListCompareWithNormalList) { + auto left = pointerLinkedListFromTestData(); + auto right = linkedListFromTestData(); + verifyCompare(left, right); +} + +TEST_F(PointerLinkedList, cxListCompareWithArrayList) { + auto left = pointerLinkedListFromTestData(); + auto right = arrayListFromTestData(); + verifyCompare(left, right); +} + +TEST_F(ArrayList, cxListCompare) { + auto left = arrayListFromTestData(); + auto right = arrayListFromTestData(); + verifyCompare(left, right); +} + +TEST_F(ArrayList, cxListCompareWithPtrList) { + auto left = arrayListFromTestData(); + auto right = pointerLinkedListFromTestData(); + verifyCompare(left, right); +} + +TEST_F(ArrayList, cxListCompareWithNormalList) { + auto left = arrayListFromTestData(); + auto right = linkedListFromTestData(); + verifyCompare(left, right); +} + +TEST_F(PointerLinkedList, NoDestructor) { + void *item = cxMalloc(&testingAllocator, sizeof(int)); + auto list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int *)); + cxListStorePointers(list); + cxListAdd(list, item); + ASSERT_FALSE(testingAllocator.verify()); + cxListDestroy(list); + EXPECT_FALSE(testingAllocator.verify()); + cxFree(&testingAllocator, item); + EXPECT_TRUE(testingAllocator.verify()); +} + +TEST_F(PointerLinkedList, SimpleDestructor) { + int item = 0; + auto list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int *)); + cxListStorePointers(list); + list->content_destructor_type = CX_DESTRUCTOR_SIMPLE; + list->simple_destructor = [](void *elem) { *(int *) elem = 42; }; + cxListAdd(list, &item); + cxListDestroy(list); + EXPECT_EQ(item, 42); +} + +TEST_F(PointerLinkedList, AdvancedDestructor) { + void *item = cxMalloc(&testingAllocator, sizeof(int)); + auto list = cxLinkedListCreate(cxDefaultAllocator, cx_cmp_int, sizeof(int *)); + cxListStorePointers(list); + list->content_destructor_type = CX_DESTRUCTOR_ADVANCED; + list->advanced_destructor.data = &testingAllocator; + list->advanced_destructor.func = (cx_destructor_func2) cxFree; + cxListAdd(list, item); + ASSERT_FALSE(testingAllocator.verify()); + cxListDestroy(list); + EXPECT_TRUE(testingAllocator.verify()); +} diff -r bf817b825ed2 -r e081643aae2a tests/test_map.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_map.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,272 @@ +/* + * 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/hash_map.h" +#include "cx/utils.h" +#include "util_allocator.h" + +#include +#include +#include + +struct map_operation { + enum { + put, rm + } op; + char const *key; + char const *value; +}; + +auto generate_map_operations() -> std::vector { + return { + {map_operation::put, "key 1", "test"}, + {map_operation::put, "key 2", "blub"}, + {map_operation::put, "key 3", "hallo"}, + {map_operation::put, "key 2", "foobar"}, + {map_operation::put, "key 4", "value 4"}, + {map_operation::put, "key 5", "value 5"}, + {map_operation::put, "key 6", "value 6"}, + {map_operation::rm, "key 4", nullptr}, + {map_operation::put, "key 7", "value 7"}, + {map_operation::put, "key 8", "value 8"}, + {map_operation::rm, "does not exist", nullptr}, + {map_operation::put, "key 9", "value 9"}, + {map_operation::put, "key 6", "other value"}, + {map_operation::put, "key 7", "something else"}, + {map_operation::rm, "key 8", nullptr}, + {map_operation::rm, "key 2", nullptr}, + {map_operation::put, "key 8", "new value"}, + }; +} + +static void verify_map_contents( + CxMap *map, + std::unordered_map const &refmap +) { + // verify key iterator + { + auto keyiter = cxMapIteratorKeys(map); + std::unordered_set keys; + cx_foreach(CxHashKey*, elem, keyiter) { + keys.insert(std::string(elem->data.cstr, elem->len)); + } + EXPECT_EQ(keyiter.index, map->size); + ASSERT_EQ(keys.size(), map->size); + for (auto &&k: keys) { + EXPECT_NE(refmap.find(k), refmap.end()); + } + } + + // verify value iterator + { + auto valiter = cxMapIteratorValues(map); + std::unordered_set values; // we use that the values in our test data are unique strings + cx_foreach(char const*, elem, valiter) { + values.insert(std::string(elem)); + } + EXPECT_EQ(valiter.index, map->size); + ASSERT_EQ(values.size(), map->size); + for (auto &&v: values) { + EXPECT_NE(std::find_if(refmap.begin(), refmap.end(), + [v](auto const &e) { return e.second == v; }), refmap.end()); + } + } + + // verify pair iterator + { + auto pairiter = cxMapIterator(map); + std::unordered_map pairs; + cx_foreach(CxMapEntry*, entry, pairiter) { + pairs[std::string(entry->key->data.cstr, entry->key->len)] = std::string((char *) entry->value); + } + EXPECT_EQ(pairiter.index, map->size); + ASSERT_EQ(pairs.size(), refmap.size()); + for (auto &&p: pairs) { + ASSERT_EQ(p.second, refmap.at(p.first)); + } + } +} + +TEST(CxHashMap, Create) { + CxTestingAllocator allocator; + auto map = cxHashMapCreate(&allocator, 0); + auto hmap = reinterpret_cast(map); + EXPECT_GT(hmap->bucket_count, 0); + cx_for_n(i, hmap->bucket_count) { + EXPECT_EQ(hmap->buckets[i], nullptr); + } + EXPECT_EQ(map->size, 0); + EXPECT_EQ(map->allocator, &allocator); + + cxMapDestroy(map); + EXPECT_TRUE(allocator.verify()); +} + +TEST(CxHashMap, BasicOperations) { + // create the map + CxTestingAllocator allocator; + auto map = cxHashMapCreate(&allocator, 8); + + // create a reference map + std::unordered_map refmap; + + // generate operations + auto ops = generate_map_operations(); + + // verify iterators for empty map + verify_map_contents(map, refmap); + + // execute operations and verify results + for (auto &&op: ops) { + CxHashKey key = cx_hash_key_str(op.key); + key.hash = 0; // force the hash map to compute the hash + if (op.op == map_operation::put) { + // execute a put operation and verify that the exact value can be read back + refmap[std::string(op.key)] = std::string(op.value); + int result = cxMapPut(map, key, (void *) op.value); + EXPECT_EQ(result, 0); + auto added = cxMapGet(map, key); + EXPECT_EQ(memcmp(op.value, added, strlen(op.value)), 0); + } else { + // execute a remove and verify that the removed element was returned (or nullptr) + auto found = refmap.find(op.key); + auto removed = cxMapRemove(map, key); + if (found == refmap.end()) { + EXPECT_EQ(removed, nullptr); + } else { + EXPECT_EQ(std::string((char *) removed), found->second); + refmap.erase(found); + } + } + // compare the current map state with the reference map + verify_map_contents(map, refmap); + } + + // destroy the map and verify the memory (de)allocations + cxMapDestroy(map); + EXPECT_TRUE(allocator.verify()); +} + +TEST(CxHashMap, RemoveViaIterator) { + CxTestingAllocator allocator; + auto map = cxHashMapCreate(&allocator, 4); + + cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1"); + cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2"); + cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3"); + cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4"); + cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5"); + cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6"); + + auto iter = cxMapMutIterator(map); + cx_foreach(CxMapEntry*, entry, iter) { + if (entry->key->data.cstr[4] % 2 == 1) cxIteratorFlagRemoval(iter); + } + EXPECT_EQ(map->size, 3); + EXPECT_EQ(iter.index, map->size); + + EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 1")), nullptr); + EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 2")), nullptr); + EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 3")), nullptr); + EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 4")), nullptr); + EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 5")), nullptr); + EXPECT_NE(cxMapGet(map, cx_hash_key_str("key 6")), nullptr); + + cxMapDestroy(map); + EXPECT_TRUE(allocator.verify()); +} + +TEST(CxHashMap, RehashNotRequired) { + CxTestingAllocator allocator; + auto map = cxHashMapCreate(&allocator, 8); + + cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1"); + cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2"); + cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3"); + cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4"); + cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5"); + cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6"); + + // 6/8 does not exceed 0.75, therefore the function should not rehash + int result = cxMapRehash(map); + EXPECT_EQ(result, 0); + EXPECT_EQ(reinterpret_cast(map)->bucket_count, 8); + + cxMapDestroy(map); + EXPECT_TRUE(allocator.verify()); +} + +TEST(CxHashMap, Rehash) { + CxTestingAllocator allocator; + auto map = cxHashMapCreate(&allocator, 8); + + cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1"); + cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2"); + cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3"); + cxMapPut(map, cx_hash_key_str("key 4"), (void *) "val 4"); + cxMapPut(map, cx_hash_key_str("key 5"), (void *) "val 5"); + cxMapPut(map, cx_hash_key_str("key 6"), (void *) "val 6"); + cxMapPut(map, cx_hash_key_str("key 7"), (void *) "val 7"); + + int result = cxMapRehash(map); + EXPECT_EQ(result, 0); + EXPECT_EQ(reinterpret_cast(map)->bucket_count, 17); + EXPECT_EQ(map->size, 7); + + EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 1")), "val 1"), 0); + EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 2")), "val 2"), 0); + EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 3")), "val 3"), 0); + EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 4")), "val 4"), 0); + EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 5")), "val 5"), 0); + EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 6")), "val 6"), 0); + EXPECT_EQ(strcmp((char *) cxMapGet(map, cx_hash_key_str("key 7")), "val 7"), 0); + + cxMapDestroy(map); + EXPECT_TRUE(allocator.verify()); +} + +TEST(CxHashMap, Clear) { + CxTestingAllocator allocator; + auto map = cxHashMapCreate(&allocator, 0); + + cxMapPut(map, cx_hash_key_str("key 1"), (void *) "val 1"); + cxMapPut(map, cx_hash_key_str("key 2"), (void *) "val 2"); + cxMapPut(map, cx_hash_key_str("key 3"), (void *) "val 3"); + + EXPECT_EQ(map->size, 3); + + cxMapClear(map); + + EXPECT_EQ(map->size, 0); + EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 1")), nullptr); + EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 2")), nullptr); + EXPECT_EQ(cxMapGet(map, cx_hash_key_str("key 3")), nullptr); + + cxMapDestroy(map); + EXPECT_TRUE(allocator.verify()); +} \ No newline at end of file diff -r bf817b825ed2 -r e081643aae2a tests/test_printf.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_printf.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,248 @@ +/* + * 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/printf.h" +#include "cx/buffer.h" + +#include +#include "util_allocator.h" + +class PrintfFixture : public ::testing::Test { +protected: + std::string buf; + CxTestingAllocator alloc; + + void TearDown() override { + buf.clear(); + ASSERT_TRUE(alloc.verify()); + } + + static size_t write_func( + void const *src, + size_t esize, + size_t ecount, + void *target + ) { + auto str = reinterpret_cast(src); + auto buf = reinterpret_cast(target); + EXPECT_EQ(esize, 1); + EXPECT_EQ(strlen(str), ecount); + *buf = str; + return ecount; + } +}; + + +TEST_F(PrintfFixture, BPrintf) { + CxBuffer buf; + cxBufferInit(&buf, nullptr, 64, &alloc, 0); + + auto r = cx_bprintf(&buf, "This %s aged %u years in a %2XSK.", "Test", 10, 0xca); + EXPECT_EQ(r, 34); + EXPECT_EQ(buf.size, 34); + buf.space[r] = '\0'; + EXPECT_STREQ(buf.space, "This Test aged 10 years in a CASK."); + + cxBufferDestroy(&buf); +} + +TEST_F(PrintfFixture, FPrintf) { + auto h = "Hello"; + size_t r; + + r = cx_fprintf(&buf, PrintfFixture::write_func, "teststring"); + EXPECT_EQ(r, 10); + EXPECT_EQ(buf, "teststring"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "[%10s]", h); + EXPECT_EQ(r, 12); + EXPECT_EQ(buf, "[ Hello]"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "[%-10s]", h); + EXPECT_EQ(r, 12); + EXPECT_EQ(buf, "[Hello ]"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "[%*s]", 10, h); + EXPECT_EQ(r, 12); + EXPECT_EQ(buf, "[ Hello]"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "[%-10.*s]", 4, h); + EXPECT_EQ(r, 12); + EXPECT_EQ(buf, "[Hell ]"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "[%-*.*s]", 10, 4, h); + EXPECT_EQ(r, 12); + EXPECT_EQ(buf, "[Hell ]"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "%c", 'A'); + EXPECT_EQ(r, 1); + EXPECT_EQ(buf, "A"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "%i %d %.6i %i %.0i %+i %i", 1, 2, 3, 0, 0, 4, -4); + EXPECT_EQ(r, 19); + EXPECT_EQ(buf, "1 2 000003 0 +4 -4"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "%x %x %X %#x", 5, 10, 10, 6); + EXPECT_EQ(r, 9); + EXPECT_EQ(buf, "5 a A 0x6"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "%o %#o %#o", 10, 10, 4); + EXPECT_EQ(r, 9); + EXPECT_EQ(buf, "12 012 04"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "%05.2f %.2f %5.2f", 1.5, 1.5, 1.5); + EXPECT_EQ(r, 16); + EXPECT_EQ(buf, "01.50 1.50 1.50"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "'%*c'", 5, 'x'); + EXPECT_EQ(r, 7); + EXPECT_EQ(buf, "' x'"); + + r = cx_fprintf(&buf, PrintfFixture::write_func, "'%*c'", -5, 'x'); + EXPECT_EQ(r, 7); + EXPECT_EQ(buf, "'x '"); +} + +TEST_F(PrintfFixture, BPrintfLargeString) { + CxBuffer buf; + cxBufferInit(&buf, nullptr, 64, &alloc, CX_BUFFER_AUTO_EXTEND); + + auto aaa = std::string(512, 'a'); + auto bbb = std::string(512, 'b'); + + auto r = cx_bprintf(&buf, "After %s comes %s.", aaa.data(), bbb.data()); + EXPECT_EQ(r, 1038); + EXPECT_EQ(buf.size, 1038); + cxBufferPut(&buf, 0); + EXPECT_EQ(buf.space, std::string("After ") + aaa + " comes " + bbb + "."); + + cxBufferDestroy(&buf); +} + +TEST_F(PrintfFixture, BPrintfNoCap) { + CxBuffer buf; + char space[20]; + memset(space, 'a', 20); + cxBufferInit(&buf, space, 16, &alloc, 0); + + auto r = cx_bprintf(&buf, "Hello %s with more than %d chars.", "string", 16); + EXPECT_EQ(r, 16); + EXPECT_EQ(buf.size, 16); + EXPECT_EQ(0, memcmp(space, "Hello string witaaaa", 20)); + + cxBufferDestroy(&buf); +} + +TEST_F(PrintfFixture, SPrintf) { + auto h = "Hello"; + + std::vector fl; + cxmutstr r; + + r = cx_asprintf_a(&alloc, "teststring"); + EXPECT_EQ(r.length, 10); + EXPECT_STREQ(r.ptr, "teststring"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "[%10s]", h); + EXPECT_EQ(r.length, 12); + EXPECT_STREQ(r.ptr, "[ Hello]"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "[%-10s]", h); + EXPECT_EQ(r.length, 12); + EXPECT_STREQ(r.ptr, "[Hello ]"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "[%*s]", 10, h); + EXPECT_EQ(r.length, 12); + EXPECT_STREQ(r.ptr, "[ Hello]"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "[%-10.*s]", 4, h); + EXPECT_EQ(r.length, 12); + EXPECT_STREQ(r.ptr, "[Hell ]"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "[%-*.*s]", 10, 4, h); + EXPECT_EQ(r.length, 12); + EXPECT_STREQ(r.ptr, "[Hell ]"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "%c", 'A'); + EXPECT_EQ(r.length, 1); + EXPECT_STREQ(r.ptr, "A"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "%i %d %.6i %i %.0i %+i %i", 1, 2, 3, 0, 0, 4, -4); + EXPECT_EQ(r.length, 19); + EXPECT_STREQ(r.ptr, "1 2 000003 0 +4 -4"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "%x %x %X %#x", 5, 10, 10, 6); + EXPECT_EQ(r.length, 9); + EXPECT_STREQ(r.ptr, "5 a A 0x6"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "%o %#o %#o", 10, 10, 4); + EXPECT_EQ(r.length, 9); + EXPECT_STREQ(r.ptr, "12 012 04"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "%05.2f %.2f %5.2f", 1.5, 1.5, 1.5); + EXPECT_EQ(r.length, 16); + EXPECT_STREQ(r.ptr, "01.50 1.50 1.50"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "'%*c'", 5, 'x'); + EXPECT_EQ(r.length, 7); + EXPECT_STREQ(r.ptr, "' x'"); + fl.push_back(r.ptr); + + r = cx_asprintf_a(&alloc, "'%*c'", -5, 'x'); + EXPECT_EQ(r.length, 7); + EXPECT_STREQ(r.ptr, "'x '"); + fl.push_back(r.ptr); + + for (auto c: fl) { + auto s = cx_mutstrn(c, 0); + cx_strfree_a(&alloc, &s); + } +} + +TEST_F(PrintfFixture, SPrintfLargeString) { + auto aaa = std::string(512, 'a'); + auto bbb = std::string(512, 'b'); + + auto r = cx_asprintf_a(&alloc, "After %s comes %s.", aaa.data(), bbb.data()); + EXPECT_EQ(r.length, 1038); + EXPECT_EQ(r.ptr, std::string("After ") + aaa + " comes " + bbb + "."); + EXPECT_EQ(r.ptr[1038], '\0'); + + cx_strfree_a(&alloc, &r); +} \ No newline at end of file diff -r bf817b825ed2 -r e081643aae2a tests/test_string.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_string.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,865 @@ +/* + * 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/string.h" +#include "util_allocator.h" + +#include + +#define EXPECT_ZERO_TERMINATED(str) EXPECT_EQ((str).ptr[(str).length], '\0') + +TEST(String, construct) { + cxstring s1 = cx_str("1234"); + cxstring s2 = cx_strn("abcd", 2); + cxmutstr s3 = cx_mutstr((char *) "1234"); + cxmutstr s4 = cx_mutstrn((char *) "abcd", 2); + + EXPECT_EQ(s1.length, 4); + EXPECT_EQ(s2.length, 2); + EXPECT_EQ(s3.length, 4); + EXPECT_EQ(s4.length, 2); +} + +TEST(String, strfree) { + CxTestingAllocator alloc; + auto test = (char *) cxMalloc(&alloc, 16); + cxmutstr str = cx_mutstrn(test, 16); + ASSERT_EQ(str.ptr, test); + EXPECT_EQ(str.length, 16); + cx_strfree_a(&alloc, &str); + EXPECT_EQ(str.ptr, nullptr); + EXPECT_EQ(str.length, 0); + EXPECT_TRUE(alloc.verify()); +} + +TEST(String, strdup) { + cxstring str = CX_STR("test"); + cxmutstr dup = cx_strdup(str); + ASSERT_EQ(dup.length, str.length); + EXPECT_STREQ(dup.ptr, str.ptr); + EXPECT_ZERO_TERMINATED(dup); + cx_strfree(&dup); + + str.length = 2; + dup = cx_strdup(str); + ASSERT_EQ(dup.length, str.length); + EXPECT_STREQ(dup.ptr, "te"); + EXPECT_ZERO_TERMINATED(dup); + cx_strfree(&dup); +} + +TEST(String, strlen) { + cxstring s1 = CX_STR("1234"); + cxstring s2 = CX_STR(".:.:."); + cxstring s3 = CX_STR("X"); + + size_t len0 = cx_strlen(0); + size_t len1 = cx_strlen(1, s1); + size_t len2 = cx_strlen(2, s1, s2); + size_t len3 = cx_strlen(3, s1, s2, s3); + + EXPECT_EQ(len0, 0); + EXPECT_EQ(len1, 4); + EXPECT_EQ(len2, 9); + EXPECT_EQ(len3, 10); +} + +TEST(String, strsubs) { + cxstring str = CX_STR("A test string"); + + cxstring sub = cx_strsubs(str, 0); + EXPECT_EQ(cx_strcmp(sub, str), 0); + + sub = cx_strsubs(str, 2); + EXPECT_EQ(cx_strcmp(sub, cx_str("test string")), 0); + + sub = cx_strsubs(str, 7); + EXPECT_EQ(cx_strcmp(sub, cx_str("string")), 0); + + sub = cx_strsubs(str, 15); + EXPECT_EQ(cx_strcmp(sub, cx_str("")), 0); + + sub = cx_strsubsl(str, 2, 4); + EXPECT_EQ(cx_strcmp(sub, cx_str("test")), 0); + + sub = cx_strsubsl(str, 7, 3); + EXPECT_EQ(cx_strcmp(sub, cx_str("str")), 0); + + sub = cx_strsubsl(str, 7, 20); + EXPECT_EQ(cx_strcmp(sub, cx_str("string")), 0); + + // just for coverage, call the _m variant + auto m = cx_strsubs_m(cx_mutstrn(nullptr, 0), 0); + EXPECT_EQ(cx_strcmp(cx_strcast(m), cx_str("")), 0); +} + +TEST(String, strchr) { + cxstring str = CX_STR("I will find you - and I will kill you"); + + cxstring notfound = cx_strchr(str, 'x'); + EXPECT_EQ(notfound.length, 0); + + cxstring result = cx_strchr(str, 'w'); + EXPECT_EQ(result.length, 35); + EXPECT_STREQ(result.ptr, "will find you - and I will kill you"); + + // just for coverage, call the _m variant + auto m = cx_strchr_m(cx_mutstrn(nullptr, 0), 'a'); + EXPECT_EQ(cx_strcmp(cx_strcast(m), cx_str("")), 0); +} + +TEST(String, strrchr) { + cxstring str = CX_STR("I will find you - and I will kill you"); + + cxstring notfound = cx_strrchr(str, 'x'); + EXPECT_EQ(notfound.length, 0); + + cxstring result = cx_strrchr(str, 'w'); + EXPECT_EQ(result.length, 13); + EXPECT_STREQ(result.ptr, "will kill you"); + + // just for coverage, call the _m variant + auto m = cx_strrchr_m(cx_mutstrn(nullptr, 0), 'a'); + EXPECT_EQ(cx_strcmp(cx_strcast(m), cx_str("")), 0); +} + +TEST(String, strstr) { + cxstring str = CX_STR("find the match in this string"); + cxstring longstr = CX_STR( + "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl" + "mnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx" + "yzabcdeababababnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghij" + "klmnopqrstuvwxyzaababababababababrstuvwxyzabcdefghijklmnopqrstuv" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "wxyz1234567890"); + cxstring longstrpattern = CX_STR( + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + ); + cxstring longstrresult = CX_STR( + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "abababababababababababababababababababababababababababababababab" + "wxyz1234567890" + ); + + cxstring notfound = cx_strstr(str, cx_str("no match")); + EXPECT_EQ(notfound.length, 0); + + cxstring result = cx_strstr(str, cx_str("match")); + EXPECT_EQ(result.length, 20); + EXPECT_STREQ(result.ptr, "match in this string"); + + result = cx_strstr(str, cx_str("")); + EXPECT_EQ(result.length, str.length); + EXPECT_STREQ(result.ptr, str.ptr); + + result = cx_strstr(longstr, longstrpattern); + EXPECT_EQ(result.length, longstrresult.length); + EXPECT_STREQ(result.ptr, longstrresult.ptr); + + // just for coverage, call the _m variant + auto mstr = cx_strdup(longstr); + auto m = cx_strstr_m(mstr, longstrpattern); + EXPECT_EQ(m.length, longstrresult.length); + EXPECT_STREQ(m.ptr, longstrresult.ptr); + cx_strfree(&mstr); +} + +TEST(String, strcmp) { + cxstring str = CX_STR("compare this"); + + EXPECT_EQ(cx_strcmp(cx_str(""), cx_str("")), 0); + EXPECT_GT(cx_strcmp(str, cx_str("")), 0); + EXPECT_EQ(cx_strcmp(str, cx_str("compare this")), 0); + EXPECT_NE(cx_strcmp(str, cx_str("Compare This")), 0); + EXPECT_LT(cx_strcmp(str, cx_str("compare tool")), 0); + EXPECT_GT(cx_strcmp(str, cx_str("compare shit")), 0); + EXPECT_LT(cx_strcmp(str, cx_str("compare this not")), 0); + EXPECT_GT(cx_strcmp(str, cx_str("compare")), 0); +} + +TEST(String, strcasecmp) { + cxstring str = CX_STR("compare this"); + + EXPECT_EQ(cx_strcasecmp(cx_str(""), cx_str("")), 0); + EXPECT_GT(cx_strcasecmp(str, cx_str("")), 0); + EXPECT_EQ(cx_strcasecmp(str, cx_str("compare this")), 0); + EXPECT_EQ(cx_strcasecmp(str, cx_str("Compare This")), 0); + EXPECT_LT(cx_strcasecmp(str, cx_str("compare tool")), 0); + EXPECT_GT(cx_strcasecmp(str, cx_str("compare shit")), 0); + EXPECT_LT(cx_strcasecmp(str, cx_str("compare this not")), 0); + EXPECT_GT(cx_strcasecmp(str, cx_str("compare")), 0); +} + +TEST(String, strcat) { + cxstring s1 = CX_STR("12"); + cxstring s2 = CX_STR("34"); + cxstring s3 = CX_STR("56"); + cxstring sn = {nullptr, 0}; + + CxTestingAllocator alloc; + + cxmutstr t1 = cx_strcat_a(&alloc, 2, s1, s2); + EXPECT_EQ(cx_strcmp(cx_strcast(t1), cx_str("1234")), 0); + EXPECT_ZERO_TERMINATED(t1); + cx_strfree_a(&alloc, &t1); + + cxmutstr t2 = cx_strcat_a(&alloc, 3, s1, s2, s3); + EXPECT_EQ(cx_strcmp(cx_strcast(t2), cx_str("123456")), 0); + EXPECT_ZERO_TERMINATED(t2); + cx_strfree_a(&alloc, &t2); + + cxmutstr t3 = cx_strcat_a(&alloc, 6, s1, sn, s2, sn, s3, sn); + EXPECT_EQ(cx_strcmp(cx_strcast(t3), cx_str("123456")), 0); + EXPECT_ZERO_TERMINATED(t3); + cx_strfree_a(&alloc, &t3); + + cxmutstr t4 = cx_strcat_a(&alloc, 2, sn, sn); + EXPECT_EQ(cx_strcmp(cx_strcast(t4), cx_str("")), 0); + EXPECT_ZERO_TERMINATED(t4); + cx_strfree_a(&alloc, &t4); + + EXPECT_TRUE(alloc.verify()); + + // use the macro + cxmutstr t5 = cx_strcat(3, s3, s1, s2); + EXPECT_EQ(cx_strcmp(cx_strcast(t5), cx_str("561234")), 0); + EXPECT_ZERO_TERMINATED(t5); + cx_strfree(&t5); +} + +TEST(String, strsplit) { + + cxstring test = cx_str("this,is,a,csv,string"); + size_t capa = 8; + cxstring list[8]; + size_t n; + + // special case: empty string + n = cx_strsplit(test, cx_str(""), capa, list); + ASSERT_EQ(n, 1); + EXPECT_EQ(cx_strcmp(list[0], test), 0); + + // no delimiter occurrence + n = cx_strsplit(test, cx_str("z"), capa, list); + ASSERT_EQ(n, 1); + EXPECT_EQ(cx_strcmp(list[0], test), 0); + + // partially matching delimiter + n = cx_strsplit(test, cx_str("is,not"), capa, list); + ASSERT_EQ(n, 1); + EXPECT_EQ(cx_strcmp(list[0], test), 0); + + // matching single-char delimiter + n = cx_strsplit(test, cx_str(","), capa, list); + ASSERT_EQ(n, 5); + EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0); + EXPECT_EQ(cx_strcmp(list[2], cx_str("a")), 0); + EXPECT_EQ(cx_strcmp(list[3], cx_str("csv")), 0); + EXPECT_EQ(cx_strcmp(list[4], cx_str("string")), 0); + + // matching multi-char delimiter + n = cx_strsplit(test, cx_str("is"), capa, list); + ASSERT_EQ(n, 3); + EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str(",")), 0); + EXPECT_EQ(cx_strcmp(list[2], cx_str(",a,csv,string")), 0); + + // bounded list using single-char delimiter + n = cx_strsplit(test, cx_str(","), 3, list); + ASSERT_EQ(n, 3); + EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0); + EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0); + + // bounded list using multi-char delimiter + n = cx_strsplit(test, cx_str("is"), 2, list); + ASSERT_EQ(n, 2); + EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0); + + // start with delimiter + n = cx_strsplit(test, cx_str("this"), capa, list); + ASSERT_EQ(n, 2); + EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0); + + // end with delimiter + n = cx_strsplit(test, cx_str("string"), capa, list); + ASSERT_EQ(n, 2); + EXPECT_EQ(cx_strcmp(list[0], cx_str("this,is,a,csv,")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); + + + // end with delimiter exceed bound + n = cx_strsplit(cx_str("a,b,c,"), cx_str(","), 3, list); + ASSERT_EQ(n, 3); + EXPECT_EQ(cx_strcmp(list[0], cx_str("a")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("b")), 0); + EXPECT_EQ(cx_strcmp(list[2], cx_str("c,")), 0); + + // exact match + n = cx_strsplit(test, cx_str("this,is,a,csv,string"), capa, list); + ASSERT_EQ(n, 2); + EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); + + // string to be split is only substring + n = cx_strsplit(test, cx_str("this,is,a,csv,string,with,extension"), capa, list); + ASSERT_EQ(n, 1); + EXPECT_EQ(cx_strcmp(list[0], test), 0); + + // subsequent encounter of delimiter (the string between is empty) + n = cx_strsplit(test, cx_str("is,"), capa, list); + ASSERT_EQ(n, 3); + EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); + EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0); + + // call the _m variant just for coverage + auto mtest = cx_strdup(test); + cxmutstr mlist[4]; + n = cx_strsplit_m(mtest, cx_str("is,"), 4, mlist); + ASSERT_EQ(n, 3); + EXPECT_EQ(cx_strcmp(cx_strcast(mlist[0]), cx_str("th")), 0); + EXPECT_EQ(cx_strcmp(cx_strcast(mlist[1]), cx_str("")), 0); + EXPECT_EQ(cx_strcmp(cx_strcast(mlist[2]), cx_str("a,csv,string")), 0); + cx_strfree(&mtest); +} + +TEST(String, strsplit_a) { + CxTestingAllocator alloc; + + cxstring test = cx_str("this,is,a,csv,string"); + size_t capa = 8; + cxstring *list; + size_t n; + + // special case: empty string + n = cx_strsplit_a(&alloc, test, cx_str(""), capa, &list); + ASSERT_EQ(n, 1); + EXPECT_EQ(cx_strcmp(list[0], test), 0); + cxFree(&alloc, list); + + // no delimiter occurrence + n = cx_strsplit_a(&alloc, test, cx_str("z"), capa, &list); + ASSERT_EQ(n, 1); + EXPECT_EQ(cx_strcmp(list[0], test), 0); + cxFree(&alloc, list); + + // partially matching delimiter + n = cx_strsplit_a(&alloc, test, cx_str("is,not"), capa, &list); + ASSERT_EQ(n, 1); + EXPECT_EQ(cx_strcmp(list[0], test), 0); + cxFree(&alloc, list); + + // matching single-char delimiter + n = cx_strsplit_a(&alloc, test, cx_str(","), capa, &list); + ASSERT_EQ(n, 5); + EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0); + EXPECT_EQ(cx_strcmp(list[2], cx_str("a")), 0); + EXPECT_EQ(cx_strcmp(list[3], cx_str("csv")), 0); + EXPECT_EQ(cx_strcmp(list[4], cx_str("string")), 0); + cxFree(&alloc, list); + + // matching multi-char delimiter + n = cx_strsplit_a(&alloc, test, cx_str("is"), capa, &list); + ASSERT_EQ(n, 3); + EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str(",")), 0); + EXPECT_EQ(cx_strcmp(list[2], cx_str(",a,csv,string")), 0); + cxFree(&alloc, list); + + // bounded list using single-char delimiter + n = cx_strsplit_a(&alloc, test, cx_str(","), 3, &list); + ASSERT_EQ(n, 3); + EXPECT_EQ(cx_strcmp(list[0], cx_str("this")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("is")), 0); + EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0); + cxFree(&alloc, list); + + // bounded list using multi-char delimiter + n = cx_strsplit_a(&alloc, test, cx_str("is"), 2, &list); + ASSERT_EQ(n, 2); + EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0); + cxFree(&alloc, list); + + // start with delimiter + n = cx_strsplit_a(&alloc, test, cx_str("this"), capa, &list); + ASSERT_EQ(n, 2); + EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str(",is,a,csv,string")), 0); + cxFree(&alloc, list); + + // end with delimiter + n = cx_strsplit_a(&alloc, test, cx_str("string"), capa, &list); + ASSERT_EQ(n, 2); + EXPECT_EQ(cx_strcmp(list[0], cx_str("this,is,a,csv,")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); + cxFree(&alloc, list); + + // end with delimiter exceed bound + n = cx_strsplit_a(&alloc, cx_str("a,b,c,"), cx_str(","), 3, &list); + ASSERT_EQ(n, 3); + EXPECT_EQ(cx_strcmp(list[0], cx_str("a")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("b")), 0); + EXPECT_EQ(cx_strcmp(list[2], cx_str("c,")), 0); + cxFree(&alloc, list); + + // exact match + n = cx_strsplit_a(&alloc, test, cx_str("this,is,a,csv,string"), capa, &list); + ASSERT_EQ(n, 2); + EXPECT_EQ(cx_strcmp(list[0], cx_str("")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); + cxFree(&alloc, list); + + // string to be split is only substring + n = cx_strsplit_a(&alloc, test, cx_str("this,is,a,csv,string,with,extension"), capa, &list); + ASSERT_EQ(n, 1); + EXPECT_EQ(cx_strcmp(list[0], test), 0); + cxFree(&alloc, list); + + // subsequent encounter of delimiter (the string between is empty) + n = cx_strsplit_a(&alloc, test, cx_str("is,"), capa, &list); + ASSERT_EQ(n, 3); + EXPECT_EQ(cx_strcmp(list[0], cx_str("th")), 0); + EXPECT_EQ(cx_strcmp(list[1], cx_str("")), 0); + EXPECT_EQ(cx_strcmp(list[2], cx_str("a,csv,string")), 0); + cxFree(&alloc, list); + + // call the _m variant just for coverage + auto mtest = cx_strdup(test); + cxmutstr *mlist; + n = cx_strsplit_ma(&alloc, mtest, cx_str("is,"), 4, &mlist); + ASSERT_EQ(n, 3); + EXPECT_EQ(cx_strcmp(cx_strcast(mlist[0]), cx_str("th")), 0); + EXPECT_EQ(cx_strcmp(cx_strcast(mlist[1]), cx_str("")), 0); + EXPECT_EQ(cx_strcmp(cx_strcast(mlist[2]), cx_str("a,csv,string")), 0); + cxFree(&alloc, mlist); + cx_strfree(&mtest); + + EXPECT_TRUE(alloc.verify()); +} + +TEST(String, strtrim) { + cxstring t1 = cx_strtrim(cx_str(" ein test \t ")); + cxstring t2 = cx_strtrim(cx_str("abc")); + cxstring t3 = cx_strtrim(cx_str(" 123")); + cxstring t4 = cx_strtrim(cx_str("xyz ")); + cxstring t5 = cx_strtrim(cx_str(" ")); + cxstring empty = cx_strtrim(cx_str("")); + + EXPECT_EQ(cx_strcmp(t1, cx_str("ein test")), 0); + EXPECT_EQ(cx_strcmp(t2, cx_str("abc")), 0); + EXPECT_EQ(cx_strcmp(t3, cx_str("123")), 0); + EXPECT_EQ(cx_strcmp(t4, cx_str("xyz")), 0); + EXPECT_EQ(cx_strcmp(t5, cx_str("")), 0); + EXPECT_EQ(cx_strcmp(empty, cx_str("")), 0); + + // call the _m variant just for coverage + cxmutstr m1 = cx_strtrim_m(cx_mutstr((char *) " ein test \t ")); + EXPECT_EQ(cx_strcmp(cx_strcast(m1), cx_str("ein test")), 0); +} + +TEST(String, strprefix) { + cxstring str = CX_STR("test my prefix and my suffix"); + cxstring empty = CX_STR(""); + EXPECT_FALSE(cx_strprefix(empty, cx_str("pref"))); + EXPECT_TRUE(cx_strprefix(str, empty)); + EXPECT_TRUE(cx_strprefix(empty, empty)); + EXPECT_TRUE(cx_strprefix(str, cx_str("test "))); + EXPECT_FALSE(cx_strprefix(str, cx_str("8-) fsck "))); +} + +TEST(String, strsuffix) { + cxstring str = CX_STR("test my prefix and my suffix"); + cxstring empty = CX_STR(""); + EXPECT_FALSE(cx_strsuffix(empty, cx_str("suf"))); + EXPECT_TRUE(cx_strsuffix(str, empty)); + EXPECT_TRUE(cx_strsuffix(empty, empty)); + EXPECT_TRUE(cx_strsuffix(str, cx_str("fix"))); + EXPECT_FALSE(cx_strsuffix(str, cx_str("fox"))); +} + +TEST(String, strcaseprefix) { + cxstring str = CX_STR("test my prefix and my suffix"); + cxstring empty = CX_STR(""); + EXPECT_FALSE(cx_strcaseprefix(empty, cx_str("pREf"))); + EXPECT_TRUE(cx_strcaseprefix(str, empty)); + EXPECT_TRUE(cx_strcaseprefix(empty, empty)); + EXPECT_TRUE(cx_strcaseprefix(str, cx_str("TEST "))); + EXPECT_FALSE(cx_strcaseprefix(str, cx_str("8-) fsck "))); +} + +TEST(String, strcasesuffix) { + cxstring str = CX_STR("test my prefix and my suffix"); + cxstring empty = CX_STR(""); + EXPECT_FALSE(cx_strcasesuffix(empty, cx_str("sUf"))); + EXPECT_TRUE(cx_strcasesuffix(str, empty)); + EXPECT_TRUE(cx_strcasesuffix(empty, empty)); + EXPECT_TRUE(cx_strcasesuffix(str, cx_str("FIX"))); + EXPECT_FALSE(cx_strcasesuffix(str, cx_str("fox"))); +} + +TEST(String, strreplace) { + CxTestingAllocator alloc; + cxstring str = CX_STR("test ababab string aba"); + cxstring longstr = CX_STR( + "xyaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacd"); + cxstring notrail = CX_STR("test abab"); + cxstring empty = CX_STR(""); + cxstring astr = CX_STR("aaaaaaaaaa"); + cxstring csstr = CX_STR("test AB ab TEST xyz"); + + cxmutstr repl = cx_strreplace(str, cx_str("abab"), cx_str("muchlonger")); + auto expected = "test muchlongerab string aba"; + + cxmutstr repln = cx_strreplacen(str, cx_str("ab"), cx_str("c"), 2); + auto expectedn = "test ccab string aba"; + + cxmutstr longrepl = cx_strreplace(longstr, cx_str("a"), cx_str("z")); + auto longexpect = "xyzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzcd"; + + cxmutstr replnotrail = cx_strreplace(notrail, cx_str("ab"), cx_str("z")); + auto notrailexpect = "test zz"; + + cxmutstr repleq = cx_strreplace(str, str, cx_str("hello")); + auto eqexpect = "hello"; + + cxmutstr replempty1 = cx_strreplace(empty, cx_str("ab"), cx_str("c")); // expect: empty + cxmutstr replempty2 = cx_strreplace(str, cx_str("abab"), empty); + auto emptyexpect2 = "test ab string aba"; + + cxmutstr replpre = cx_strreplace(str, cx_str("test "), cx_str("TEST ")); + auto preexpected = "TEST ababab string aba"; + + cxmutstr replan1 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 1); + auto an1expected = "xaaaaaaaaa"; + + cxmutstr replan4 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 4); + auto an4expected = "xxxxaaaaaa"; + + cxmutstr replan9 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 9); + auto an9expected = "xxxxxxxxxa"; + + cxmutstr replan10 = cx_strreplacen(astr, cx_str("a"), cx_str("x"), 10); + auto an10expected = "xxxxxxxxxx"; + + cxmutstr repl1_a = cx_strreplace_a(&alloc, csstr, cx_str("AB"), cx_str("*")); + auto expeced1_a = "test * ab TEST xyz"; + + cxmutstr repl2_a = cx_strreplace_a(&alloc, csstr, cx_str("test"), cx_str("TEST")); + auto expected2_a = "TEST AB ab TEST xyz"; + + + EXPECT_NE(repl.ptr, str.ptr); + EXPECT_ZERO_TERMINATED(repl); + EXPECT_STREQ(repl.ptr, expected); + EXPECT_ZERO_TERMINATED(repln); + EXPECT_STREQ(repln.ptr, expectedn); + EXPECT_ZERO_TERMINATED(longrepl); + EXPECT_STREQ(longrepl.ptr, longexpect); + EXPECT_ZERO_TERMINATED(replnotrail); + EXPECT_STREQ(replnotrail.ptr, notrailexpect); + EXPECT_ZERO_TERMINATED(repleq); + EXPECT_STREQ(repleq.ptr, eqexpect); + EXPECT_ZERO_TERMINATED(replempty1); + EXPECT_STREQ(replempty1.ptr, ""); + EXPECT_ZERO_TERMINATED(replempty2); + EXPECT_STREQ(replempty2.ptr, emptyexpect2); + EXPECT_ZERO_TERMINATED(replpre); + EXPECT_STREQ(replpre.ptr, preexpected); + EXPECT_ZERO_TERMINATED(replan1); + EXPECT_STREQ(replan1.ptr, an1expected); + EXPECT_ZERO_TERMINATED(replan4); + EXPECT_STREQ(replan4.ptr, an4expected); + EXPECT_ZERO_TERMINATED(replan9); + EXPECT_STREQ(replan9.ptr, an9expected); + EXPECT_ZERO_TERMINATED(replan10); + EXPECT_STREQ(replan10.ptr, an10expected); + EXPECT_ZERO_TERMINATED(repl1_a); + EXPECT_STREQ(repl1_a.ptr, expeced1_a); + EXPECT_ZERO_TERMINATED(repl2_a); + EXPECT_STREQ(repl2_a.ptr, expected2_a); + + cx_strfree(&repl); + cx_strfree(&repln); + cx_strfree(&longrepl); + cx_strfree(&replnotrail); + cx_strfree(&repleq); + cx_strfree(&replempty1); + cx_strfree(&replempty2); + cx_strfree(&replpre); + cx_strfree(&replan1); + cx_strfree(&replan4); + cx_strfree(&replan9); + cx_strfree(&replan10); + + cx_strfree_a(&alloc, &repl1_a); + cx_strfree_a(&alloc, &repl2_a); + EXPECT_TRUE(alloc.verify()); +} + +TEST(String, strupper) { + cxmutstr str = cx_strdup(cx_str("thIs 1s @ Te$t")); + cx_strupper(str); + EXPECT_STREQ(str.ptr, "THIS 1S @ TE$T"); + cx_strfree(&str); +} + +TEST(String, strlower) { + cxmutstr str = cx_strdup(cx_str("thIs 1s @ Te$t")); + cx_strlower(str); + EXPECT_STREQ(str.ptr, "this 1s @ te$t"); + cx_strfree(&str); +} + +TEST(String, strtok) { + cxstring str = cx_str("a,comma,separated,string"); + cxstring delim = cx_str(","); + CxStrtokCtx ctx = cx_strtok(str, delim, 3); + EXPECT_EQ(ctx.str.ptr, str.ptr); + EXPECT_EQ(ctx.str.length, str.length); + EXPECT_EQ(ctx.delim.ptr, delim.ptr); + EXPECT_EQ(ctx.delim.length, delim.length); + EXPECT_EQ(ctx.limit, 3); + EXPECT_EQ(ctx.found, 0); + EXPECT_EQ(ctx.pos, 0); + EXPECT_EQ(ctx.next_pos, 0); + EXPECT_EQ(ctx.delim_more, nullptr); + EXPECT_EQ(ctx.delim_more_count, 0); +} + +TEST(String, strtok_m) { + cxmutstr str = cx_strdup(cx_str("a,comma,separated,string")); + cxstring delim = cx_str(","); + CxStrtokCtx ctx = cx_strtok_m(str, delim, 3); + EXPECT_EQ(ctx.str.ptr, str.ptr); + EXPECT_EQ(ctx.str.length, str.length); + EXPECT_EQ(ctx.delim.ptr, delim.ptr); + EXPECT_EQ(ctx.delim.length, delim.length); + EXPECT_EQ(ctx.limit, 3); + EXPECT_EQ(ctx.found, 0); + EXPECT_EQ(ctx.pos, 0); + EXPECT_EQ(ctx.next_pos, 0); + EXPECT_EQ(ctx.delim_more, nullptr); + EXPECT_EQ(ctx.delim_more_count, 0); + cx_strfree(&str); +} + +TEST(String, strtok_delim) { + cxstring str = cx_str("an,arbitrarily|separated;string"); + cxstring delim = cx_str(","); + cxstring delim_more[2] = {CX_STR("|"), CX_STR(";")}; + CxStrtokCtx ctx = cx_strtok(str, delim, 3); + cx_strtok_delim(&ctx, delim_more, 2); + EXPECT_EQ(ctx.str.ptr, str.ptr); + EXPECT_EQ(ctx.str.length, str.length); + EXPECT_EQ(ctx.delim.ptr, delim.ptr); + EXPECT_EQ(ctx.delim.length, delim.length); + EXPECT_EQ(ctx.limit, 3); + EXPECT_EQ(ctx.found, 0); + EXPECT_EQ(ctx.pos, 0); + EXPECT_EQ(ctx.next_pos, 0); + EXPECT_EQ(ctx.delim_more, delim_more); + EXPECT_EQ(ctx.delim_more_count, 2); +} + +TEST(String, strtok_next_easy) { + cxstring str = cx_str("a,comma,separated,string"); + cxstring delim = cx_str(","); + CxStrtokCtx ctx = cx_strtok(str, delim, 3); + bool ret; + cxstring tok; + + ret = cx_strtok_next(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(tok, cx_str("a")), 0); + EXPECT_EQ(ctx.pos, 0); + EXPECT_EQ(ctx.next_pos, 2); + EXPECT_EQ(ctx.delim_pos, 1); + EXPECT_EQ(ctx.found, 1); + + ret = cx_strtok_next(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(tok, cx_str("comma")), 0); + EXPECT_EQ(ctx.pos, 2); + EXPECT_EQ(ctx.next_pos, 8); + EXPECT_EQ(ctx.delim_pos, 7); + EXPECT_EQ(ctx.found, 2); + + ret = cx_strtok_next(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(tok, cx_str("separated")), 0); + EXPECT_EQ(ctx.pos, 8); + EXPECT_EQ(ctx.next_pos, 18); + EXPECT_EQ(ctx.delim_pos, 17); + EXPECT_EQ(ctx.found, 3); + + ret = cx_strtok_next(&ctx, &tok); + ASSERT_FALSE(ret); + EXPECT_EQ(ctx.pos, 8); + EXPECT_EQ(ctx.next_pos, 18); + EXPECT_EQ(ctx.delim_pos, 17); + EXPECT_EQ(ctx.found, 3); +} + +TEST(String, strtok_next_unlimited) { + cxstring str = cx_str("some;-;otherwise;-;separated;-;string;-;"); + cxstring delim = cx_str(";-;"); + CxStrtokCtx ctx = cx_strtok(str, delim, SIZE_MAX); + bool ret; + cxstring tok; + + ret = cx_strtok_next(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(tok, cx_str("some")), 0); + EXPECT_EQ(ctx.pos, 0); + EXPECT_EQ(ctx.next_pos, 7); + EXPECT_EQ(ctx.delim_pos, 4); + EXPECT_EQ(ctx.found, 1); + + ret = cx_strtok_next(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(tok, cx_str("otherwise")), 0); + EXPECT_EQ(ctx.pos, 7); + EXPECT_EQ(ctx.next_pos, 19); + EXPECT_EQ(ctx.delim_pos, 16); + EXPECT_EQ(ctx.found, 2); + + ret = cx_strtok_next(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(tok, cx_str("separated")), 0); + EXPECT_EQ(ctx.pos, 19); + EXPECT_EQ(ctx.next_pos, 31); + EXPECT_EQ(ctx.delim_pos, 28); + EXPECT_EQ(ctx.found, 3); + + ret = cx_strtok_next(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(tok, cx_str("string")), 0); + EXPECT_EQ(ctx.pos, 31); + EXPECT_EQ(ctx.next_pos, 40); + EXPECT_EQ(ctx.delim_pos, 37); + EXPECT_EQ(ctx.found, 4); + + ret = cx_strtok_next(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(tok, cx_str("")), 0); + EXPECT_EQ(ctx.pos, 40); + EXPECT_EQ(ctx.next_pos, 40); + EXPECT_EQ(ctx.delim_pos, 40); + EXPECT_EQ(ctx.found, 5); + + ret = cx_strtok_next(&ctx, &tok); + ASSERT_FALSE(ret); + EXPECT_EQ(ctx.pos, 40); + EXPECT_EQ(ctx.delim_pos, 40); + EXPECT_EQ(ctx.found, 5); +} + +TEST(String, strtok_next_advanced) { + cxmutstr str = cx_strdup(cx_str("an,arbitrarily;||separated;string")); + cxstring delim = cx_str(","); + cxstring delim_more[2] = {CX_STR("||"), CX_STR(";")}; + CxStrtokCtx ctx = cx_strtok_m(str, delim, 10); + cx_strtok_delim(&ctx, delim_more, 2); + bool ret; + cxmutstr tok; + + ret = cx_strtok_next_m(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(cx_strcast(tok), cx_str("an")), 0); + EXPECT_EQ(ctx.pos, 0); + EXPECT_EQ(ctx.next_pos, 3); + EXPECT_EQ(ctx.delim_pos, 2); + EXPECT_EQ(ctx.found, 1); + cx_strupper(tok); + + ret = cx_strtok_next_m(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(cx_strcast(tok), cx_str("arbitrarily")), 0); + EXPECT_EQ(ctx.pos, 3); + EXPECT_EQ(ctx.next_pos, 15); + EXPECT_EQ(ctx.delim_pos, 14); + EXPECT_EQ(ctx.found, 2); + cx_strupper(tok); + + ret = cx_strtok_next_m(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(cx_strcast(tok), cx_str("")), 0); + EXPECT_EQ(ctx.pos, 15); + EXPECT_EQ(ctx.next_pos, 17); + EXPECT_EQ(ctx.delim_pos, 15); + EXPECT_EQ(ctx.found, 3); + cx_strupper(tok); + + ret = cx_strtok_next_m(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(cx_strcast(tok), cx_str("separated")), 0); + EXPECT_EQ(ctx.pos, 17); + EXPECT_EQ(ctx.next_pos, 27); + EXPECT_EQ(ctx.delim_pos, 26); + EXPECT_EQ(ctx.found, 4); + cx_strupper(tok); + + ret = cx_strtok_next_m(&ctx, &tok); + ASSERT_TRUE(ret); + EXPECT_EQ(cx_strcmp(cx_strcast(tok), cx_str("string")), 0); + EXPECT_EQ(ctx.pos, 27); + EXPECT_EQ(ctx.next_pos, 33); + EXPECT_EQ(ctx.delim_pos, 33); + EXPECT_EQ(ctx.found, 5); + cx_strupper(tok); + + ret = cx_strtok_next_m(&ctx, &tok); + ASSERT_FALSE(ret); + EXPECT_EQ(ctx.pos, 27); + EXPECT_EQ(ctx.next_pos, 33); + EXPECT_EQ(ctx.delim_pos, 33); + EXPECT_EQ(ctx.found, 5); + + EXPECT_EQ(cx_strcmp(cx_strcast(str), cx_str("AN,ARBITRARILY;||SEPARATED;STRING")), 0); + + cx_strfree(&str); +} diff -r bf817b825ed2 -r e081643aae2a tests/test_tree.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_tree.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,122 @@ +/* + * 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/tree.h" +#include + +struct TestNode { + TestNode *parent = nullptr; + TestNode *prev = nullptr; + TestNode *next = nullptr; + + TestNode *children_begin = nullptr; + TestNode *children_end = nullptr; +}; + +TEST(Tree, cx_tree_add_sibling) { + // prepare test tree + TestNode root, a; + root.children_begin = &a; + root.children_end = &a; + a.parent = &root; + + // new test nodes + TestNode b, c; + + // test + cx_tree_add_sibling(&a, offsetof(TestNode, prev), offsetof(TestNode, next), offsetof(TestNode, parent), &b); + EXPECT_EQ(b.parent, &root); + EXPECT_EQ(b.prev, &a); + EXPECT_EQ(b.next, nullptr); + EXPECT_EQ(a.next, &b); + + cx_tree_add_sibling(&a, -1, offsetof(TestNode, next), -1, &c); + EXPECT_EQ(c.parent, nullptr); + EXPECT_EQ(c.prev, nullptr); + EXPECT_EQ(c.next, nullptr); + EXPECT_EQ(b.next, &c); +} + +TEST(Tree, cx_tree_add_child) { + TestNode root, a, b, c, a1; + + cx_tree_add_child( + (void **) &root.children_begin, + (void **) &root.children_end, + offsetof(TestNode, prev), + offsetof(TestNode, next), + &a, + offsetof(TestNode, parent), + &root); + EXPECT_EQ(root.children_begin, &a); + EXPECT_EQ(root.children_end, &a); + EXPECT_EQ(a.parent, &root); + EXPECT_EQ(a.prev, nullptr); + EXPECT_EQ(a.next, nullptr); + + cx_tree_add_child( + (void **) &root.children_begin, + (void **) &root.children_end, + offsetof(TestNode, prev), + offsetof(TestNode, next), + &b, + offsetof(TestNode, parent), + &root); + EXPECT_EQ(root.children_begin, &a); + EXPECT_EQ(root.children_begin->next, &b); + EXPECT_EQ(root.children_end, &b); + EXPECT_EQ(b.parent, &root); + EXPECT_EQ(b.prev, &a); + + cx_tree_add_child( + (void **) &root.children_begin, + nullptr, + -1, + offsetof(TestNode, next), + &c, + -1, + &root); + EXPECT_EQ(root.children_end, &b); // children_end unchanged + EXPECT_EQ(b.next, &c); + EXPECT_EQ(c.prev, nullptr); + EXPECT_EQ(c.next, nullptr); + EXPECT_EQ(c.parent, nullptr); + + cx_tree_add_child( + (void **) &a.children_begin, + (void **) &a.children_end, + offsetof(TestNode, prev), + offsetof(TestNode, next), + &a1, + offsetof(TestNode, parent), + &a); + EXPECT_EQ(a.children_begin, &a1); + EXPECT_EQ(a1.parent, &a); + EXPECT_EQ(root.children_begin, &a); + EXPECT_EQ(root.children_begin->children_begin, &a1); +} diff -r bf817b825ed2 -r e081643aae2a tests/test_utils.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/test_utils.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,155 @@ +/* + * 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/utils.h" + +#include + +TEST(Utils, ForN) { + unsigned j; + j = 0; + cx_for_n(i, 50) { + EXPECT_EQ(i, j); + j++; + } +} + +TEST(Utils, szmul) { + size_t r; + int e; + e = cx_szmul(5, 7, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(35, r); + + size_t s = SIZE_MAX & ~3; + + e = cx_szmul(s / 4, 2, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(s / 2, r); + e = cx_szmul(2, s / 4, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(s / 2, r); + + e = cx_szmul(s / 4, 4, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(s, r); + + e = cx_szmul(4, s / 4, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(s, r); + + e = cx_szmul(s / 4, 5, &r); + EXPECT_NE(0, e); + + e = cx_szmul(5, s / 4, &r); + EXPECT_NE(0, e); + + e = cx_szmul(SIZE_MAX - 4, 0, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(0, r); + + e = cx_szmul(0, SIZE_MAX - 1, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(0, r); + + e = cx_szmul(SIZE_MAX, 0, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(0, r); + + e = cx_szmul(0, SIZE_MAX, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(0, r); + + e = cx_szmul(0, 0, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(0, r); +} + +#ifdef CX_SZMUL_BUILTIN + +// also test the custom implementation +struct Utils_szmul_impl : ::testing::Test { +#undef CX_SZMUL_BUILTIN + +#include "../src/utils.c" + +#define CX_SZMUL_BUILTIN +}; + +TEST_F(Utils_szmul_impl, Test) { + size_t r; + int e; + e = cx_szmul_impl(5, 7, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(35, r); + + size_t s = SIZE_MAX & ~3; + + e = cx_szmul_impl(s / 4, 2, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(s / 2, r); + e = cx_szmul_impl(2, s / 4, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(s / 2, r); + + e = cx_szmul_impl(s / 4, 4, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(s, r); + + e = cx_szmul_impl(4, s / 4, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(s, r); + + e = cx_szmul_impl(s / 4, 5, &r); + EXPECT_NE(0, e); + + e = cx_szmul_impl(5, s / 4, &r); + EXPECT_NE(0, e); + + e = cx_szmul_impl(SIZE_MAX - 4, 0, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(0, r); + + e = cx_szmul_impl(0, SIZE_MAX - 1, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(0, r); + + e = cx_szmul_impl(SIZE_MAX, 0, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(0, r); + + e = cx_szmul_impl(0, SIZE_MAX, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(0, r); + + e = cx_szmul_impl(0, 0, &r); + EXPECT_EQ(0, e); + EXPECT_EQ(0, r); +} + +#endif // CX_SZMUL_BUILTIN diff -r bf817b825ed2 -r e081643aae2a tests/util_allocator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/util_allocator.cpp Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,167 @@ +/* + * 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 "util_allocator.h" + +void *cx_malloc_testing(void *d, size_t n) { + auto data = reinterpret_cast(d); + void *ptr = malloc(n); + data->alloc_total++; + if (ptr == nullptr) { + data->alloc_failed++; + } else { + data->tracked.insert(ptr); + } + return ptr; +} + +void *cx_realloc_testing(void *d, void *mem, size_t n) { + auto data = reinterpret_cast(d); + void *ptr = realloc(mem, n); + if (ptr == mem) { + return ptr; + } else { + data->alloc_total++; + if (ptr == nullptr) { + data->alloc_failed++; + } else { + data->free_total++; + if (data->tracked.erase(mem) == 0) { + data->free_failed++; + } + data->tracked.insert(ptr); + } + return ptr; + } +} + +void *cx_calloc_testing(void *d, size_t nelem, size_t n) { + auto data = reinterpret_cast(d); + void *ptr = calloc(nelem, n); + data->alloc_total++; + if (ptr == nullptr) { + data->alloc_failed++; + } else { + data->tracked.insert(ptr); + } + return ptr; +} + +void cx_free_testing(void *d, void *mem) { + auto data = reinterpret_cast(d); + data->free_total++; + if (data->tracked.erase(mem) == 0) { + data->free_failed++; + // do not even attempt to free mem, because it is likely to segfault + } else { + free(mem); + } +} + +cx_allocator_class cx_testing_allocator_class = { + cx_malloc_testing, + cx_realloc_testing, + cx_calloc_testing, + cx_free_testing +}; + +CxTestingAllocator::CxTestingAllocator() : CxAllocator() { + cl = &cx_testing_allocator_class; + data = this; +} + +bool CxTestingAllocator::used() const { + return alloc_total > 0; +} + +bool CxTestingAllocator::verify() const { + return tracked.empty() && alloc_failed == 0 && free_failed == 0 && alloc_total == free_total; +} + +// SELF-TEST + +#include + +TEST(TestingAllocator, ExpectFree) { + CxTestingAllocator allocator; + + ASSERT_TRUE(allocator.verify()); + EXPECT_FALSE(allocator.used()); + auto ptr = cxMalloc(&allocator, 16); + EXPECT_TRUE(allocator.used()); + ASSERT_NE(ptr, nullptr); + EXPECT_FALSE(allocator.verify()); + + cxFree(&allocator, ptr); + EXPECT_TRUE(allocator.verify()); +} + +TEST(TestingAllocator, DetectDoubleFree) { + CxTestingAllocator allocator; + + ASSERT_TRUE(allocator.verify()); + auto ptr = cxMalloc(&allocator, 16); + ASSERT_NE(ptr, nullptr); + + cxFree(&allocator, ptr); + EXPECT_TRUE(allocator.verify()); + ASSERT_NO_FATAL_FAILURE(cxFree(&allocator, ptr)); + EXPECT_FALSE(allocator.verify()); +} + +TEST(TestingAllocator, FreeUntracked) { + CxTestingAllocator allocator; + + auto ptr = malloc(16); + ASSERT_TRUE(allocator.verify()); + ASSERT_NO_FATAL_FAILURE(cxFree(&allocator, ptr)); + EXPECT_FALSE(allocator.verify()); + ASSERT_NO_FATAL_FAILURE(free(ptr)); +} + +TEST(TestingAllocator, FullLifecycleWithRealloc) { + CxTestingAllocator allocator; + ASSERT_TRUE(allocator.verify()); + auto ptr = cxMalloc(&allocator, 16); + ASSERT_NE(ptr, nullptr); + EXPECT_EQ(allocator.tracked.size(), 1); + ptr = cxRealloc(&allocator, ptr, 256); + ASSERT_NE(ptr, nullptr); + EXPECT_EQ(allocator.tracked.size(), 1); + cxFree(&allocator, ptr); + EXPECT_TRUE(allocator.verify()); +} + +TEST(TestingAllocator, CallocInitializes) { + CxTestingAllocator allocator; + const char zeros[16] = {0}; + auto ptr = cxCalloc(&allocator, 16, 1); + EXPECT_EQ(memcmp(ptr, zeros, 16), 0); + cxFree(&allocator, ptr); + EXPECT_TRUE(allocator.verify()); +} diff -r bf817b825ed2 -r e081643aae2a tests/util_allocator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/util_allocator.h Tue Feb 07 21:55:37 2023 +0100 @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef UCX_UTIL_ALLOCATOR_H +#define UCX_UTIL_ALLOCATOR_H + +#include "cx/allocator.h" + +#include + +struct CxTestingAllocator : public CxAllocator { + /** + * Total number of all allocations (malloc, calloc, realloc). + * A realloc() does only count when the memory has to be moved. + */ + unsigned alloc_total = 0; + /** + * Number of failed allocations (malloc, calloc, realloc). + */ + unsigned alloc_failed = 0; + /** + * Total number of freed pointers. + * A reallocation also counts as a free when the memory has to be moved. + */ + unsigned free_total = 0; + /** + * Number of failed free invocations. + * A free() is considered failed, if it has not been performed on tracked memory. + */ + unsigned free_failed = 0; + /** + * The set of tracked memory blocks. + */ + std::set tracked; + + /** + * Constructs a new testing allocator. + */ + CxTestingAllocator(); + + /** + * Verifies that this allocator has been used. + * + * @return true if any allocation was attempted using this allocator + */ + [[nodiscard]] bool used() const; + + /** + * Verifies that all allocated memory blocks are freed and no free occurred twice. + * + * @return true iff all tracked allocations / deallocations were valid + */ + [[nodiscard]] bool verify() const; +}; + +#endif // UCX_UTIL_ALLOCATOR_H