From f12ae36f97a912d0d2ffc93e45fee60c6990ebb6 Mon Sep 17 00:00:00 2001 From: SpacingBat3 Date: Sat, 23 May 2026 01:43:33 +0200 Subject: [PATCH 1/6] feat(test): Initial implementation --- CMakeLists.txt | 7 + test/CMakeLists.txt | 61 ++++++++ test/include/TestRunner.h | 3 + test/include/TestRunner/fun.h | 4 + test/include/TestRunner/prefix.h | 34 +++++ test/include/TestRunner/res.h | 7 + test/lib/CMakeLists.txt | 2 + test/lib/cppvec/CMakeLists.txt | 3 + test/lib/cppvec/include/RefLib.h | 26 ++++ test/lib/cppvec/src/lib.cpp | 41 ++++++ test/vec/api/prefix.h | 1 + test/vec/api/should_concat_arrays.c | 59 ++++++++ test/vec/api/should_convert_arrays.c | 46 ++++++ test/vec/api/should_push_many.c | 30 ++++ test/vec/api/should_push_one.c | 29 ++++ test/vec/perf/prefix.h | 1 + .../perf/should_push_not_slower_than_cpp.c | 137 ++++++++++++++++++ 17 files changed, 491 insertions(+) create mode 100644 test/CMakeLists.txt create mode 100644 test/include/TestRunner.h create mode 100644 test/include/TestRunner/fun.h create mode 100644 test/include/TestRunner/prefix.h create mode 100644 test/include/TestRunner/res.h create mode 100644 test/lib/CMakeLists.txt create mode 100644 test/lib/cppvec/CMakeLists.txt create mode 100644 test/lib/cppvec/include/RefLib.h create mode 100644 test/lib/cppvec/src/lib.cpp create mode 100644 test/vec/api/prefix.h create mode 100644 test/vec/api/should_concat_arrays.c create mode 100644 test/vec/api/should_convert_arrays.c create mode 100644 test/vec/api/should_push_many.c create mode 100644 test/vec/api/should_push_one.c create mode 100644 test/vec/perf/prefix.h create mode 100644 test/vec/perf/should_push_not_slower_than_cpp.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 26beb96..bd07444 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,11 @@ project(YAVL VERSION 0.1.0 LANGUAGES C ) +set(YAVL_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}") + include(GNUInstallDirs) include(CMakePackageConfigHelpers) +include(CTest) find_package(Doxygen OPTIONAL_COMPONENTS doxygen) @@ -142,6 +145,10 @@ foreach(header "${${PROJECT_NAME}_GEN_HEADERS}") ) endforeach() +# Tests +# ===== +add_subdirectory("test/") + # Arch PKGBUILD # ============= configure_file( diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..fd2c880 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,61 @@ +cmake_minimum_required(VERSION 4.2) +set(OLD_PROJECT_NAME "${PROJECT_NAME}") +project("${OLD_PROJECT_NAME}Tests" C CXX) + +# Collect tests +file(GLOB_RECURSE "${OLD_PROJECT_NAME}_TESTS" + LIST_DIRECTORIES false + CONFIGURE_DEPENDS + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + **/*.c +) + +list(JOIN "${OLD_PROJECT_NAME}_TESTS" " " testrunner_str) +create_test_sourcelist( + "${OLD_PROJECT_NAME}_TEST_SRC" + TestRunner.c + ${${OLD_PROJECT_NAME}_TESTS} +) + +# Additional libraries +add_subdirectory("lib/") + +add_executable("${OLD_PROJECT_NAME}TestRunner" TestRunner.c ${${OLD_PROJECT_NAME}_TEST_SRC}) +add_executable("${OLD_PROJECT_NAME}::TestRunner" ALIAS "${OLD_PROJECT_NAME}TestRunner") +add_executable("${PROJECT_NAME}::Runner" ALIAS "${OLD_PROJECT_NAME}TestRunner") +target_link_libraries("${OLD_PROJECT_NAME}TestRunner" PRIVATE "${OLD_PROJECT_NAME}" "${PROJECT_NAME}RefLib") +target_include_directories("${OLD_PROJECT_NAME}TestRunner" PRIVATE "include/") +set_target_properties("${OLD_PROJECT_NAME}TestRunner" PROPERTIES + CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test") + +foreach(test IN LISTS "${OLD_PROJECT_NAME}_TESTS") + get_filename_component(TName "${test}" NAME_WE) + get_filename_component(TPath "${test}" DIRECTORY) + string(REPLACE "/" "->" TNamespace "${TPath}") + string(REPLACE "/" ";" TLabels "${TPath}") + string(REGEX MATCH "^should(_not)?" TKind "${TName}") + add_test( + NAME "${TNamespace}.${TName}" + COMMAND "${OLD_PROJECT_NAME}TestRunner" "${TPath}/${TName}" + ) + if(TKind STREQUAL "should_not") + set_property( + TEST + "${TNamespace}.${TName}" + PROPERTY + WILL_FAIL true + ) + endif() + set_property( + TEST + "${TNamespace}.${TName}" + PROPERTY + SKIP_RETURN_CODE 2 + ) + set_property( + TEST + "${TNamespace}.${TName}" + PROPERTY + LABELS ${TLabels} + ) +endforeach() diff --git a/test/include/TestRunner.h b/test/include/TestRunner.h new file mode 100644 index 0000000..76e9cca --- /dev/null +++ b/test/include/TestRunner.h @@ -0,0 +1,3 @@ +#include "TestRunner/prefix.h" +#include "TestRunner/fun.h" +#include "TestRunner/res.h" diff --git a/test/include/TestRunner/fun.h b/test/include/TestRunner/fun.h new file mode 100644 index 0000000..5ae38f5 --- /dev/null +++ b/test/include/TestRunner/fun.h @@ -0,0 +1,4 @@ +// Macros +#define is(S) (code = code || !(S)) +#define test_if(S) if((code = code || !(S))) +#define countof(A) (sizeof(A)/sizeof(A[0])) diff --git a/test/include/TestRunner/prefix.h b/test/include/TestRunner/prefix.h new file mode 100644 index 0000000..fb6eb89 --- /dev/null +++ b/test/include/TestRunner/prefix.h @@ -0,0 +1,34 @@ +#ifdef PREFIX + +// Macro backups +#pragma push_macro("vec_api") +#pragma push_macro("vec_perf") + +// Map names to numbers +#define vec_api 1L +#define vec_perf 2L + +// Compare macro to macro by name +#if PREFIX == vec_api +#define PRIVATE_DEP_should(T,K) vec_api##K##T +#elif PREFIX == vec_perf +#define PRIVATE_DEP_should(T,K) vec_perf##K##T +#else /* PREFIX == vec_api */ +#error Unhandled PREFIX case +#endif /* PREFIX == vec_api */ + +// Macro restore +#undef vec_perf +#undef vec_api +#pragma pop_macro("vec_perf") +#pragma pop_macro("vec_api") + +// Static macros +#define it_should(T) int PRIVATE_DEP_should(T,_should_)(int argc,char** argv) +#define it_should_not(T) int PRIVATE_DEP_should(T,_should_not_)(int argc,char** argv) +#define _tostr(...) #__VA_ARGS__ +#define tostr(...) _tostr(__VA_ARGS__) +#define todo(...) { _Pragma(_tostr(message("Unimplemented test case: " #__VA_ARGS__))) return 2; } +#define it_todo(T) int PRIVATE_DEP_should(T,_should_)(int argc,char** argv) todo +#undef PREFIX +#endif diff --git a/test/include/TestRunner/res.h b/test/include/TestRunner/res.h new file mode 100644 index 0000000..144a210 --- /dev/null +++ b/test/include/TestRunner/res.h @@ -0,0 +1,7 @@ +#pragma once +#ifndef TESTRUNNER_H +#define TESTRUNNER_H +// Shared resources across tests +static char A[] = {1,2,3,4}; +static char B[] = {5,6,7,8,9,10}; +#endif diff --git a/test/lib/CMakeLists.txt b/test/lib/CMakeLists.txt new file mode 100644 index 0000000..8e09372 --- /dev/null +++ b/test/lib/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory("cppvec") +#add_subdirectory("bench") #TODO! diff --git a/test/lib/cppvec/CMakeLists.txt b/test/lib/cppvec/CMakeLists.txt new file mode 100644 index 0000000..d7778c2 --- /dev/null +++ b/test/lib/cppvec/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library("${PROJECT_NAME}RefLib" STATIC src/lib.cpp) +add_library("${PROJECT_NAME}::RefLib" ALIAS "${PROJECT_NAME}RefLib") +target_include_directories("${PROJECT_NAME}RefLib" PUBLIC "include/") diff --git a/test/lib/cppvec/include/RefLib.h b/test/lib/cppvec/include/RefLib.h new file mode 100644 index 0000000..926b1e1 --- /dev/null +++ b/test/lib/cppvec/include/RefLib.h @@ -0,0 +1,26 @@ +#ifdef __cplusplus +#include +extern "C" { +#define cpp_vector(T,...) std::vector +#else +#define cpp_vector(T,...) struct vector_##T +/// Type definition +cpp_vector(char); +#endif +#include +#include +// C++ API maps +cpp_vector(char) *const cpp_vec_new(); +void cpp_vec_free(cpp_vector(char) *const vec); +void cpp_vec_push(cpp_vector(char) *const vec, const char data); +char cpp_vec_pop(cpp_vector(char) *const vec, const char data); +char* cpp_vec_ref(cpp_vector(char) *const vec, size_t idx); +size_t cpp_vec_get_len(cpp_vector(char) *const vec); +size_t cpp_vec_get_reservd(cpp_vector(char) *const vec); +size_t cpp_vec_get_reservd_max(cpp_vector(char) *const vec); +void cpp_vec_set_reservd(cpp_vector(char) *const vec, size_t new_reservd); +char cpp_vec_get_el(cpp_vector(char) *const vec, size_t idx); +char cpp_vec_set_el(cpp_vector(char) *const vec, size_t idx, char v); +#ifdef __cplusplus +} +#endif diff --git a/test/lib/cppvec/src/lib.cpp b/test/lib/cppvec/src/lib.cpp new file mode 100644 index 0000000..7c24831 --- /dev/null +++ b/test/lib/cppvec/src/lib.cpp @@ -0,0 +1,41 @@ +#include + +// Bindings for C++ std::vec to C. + +extern "C" { + cpp_vector(char) *const cpp_vec_new() { + return new cpp_vector(char); + } + void cpp_vec_free(cpp_vector(char) *const vec) { + delete vec; + } + void cpp_vec_push(cpp_vector(char) *const vec, const char data) { + vec->push_back(data); + } + char cpp_vec_pop(cpp_vector(char) *const vec, const char data) { + char last = vec->back(); + vec->pop_back(); + return last; + } + char* cpp_vec_ref(cpp_vector(char) *const vec, size_t idx) { + return vec->data()+idx; + } + size_t cpp_vec_get_len(cpp_vector(char) *const vec) { + return vec->size(); + } + size_t cpp_vec_get_reservd(cpp_vector(char) *const vec) { + return vec->capacity(); + } + size_t cpp_vec_get_reservd_max(cpp_vector(char) *const vec) { + return vec->max_size(); + } + void cpp_vec_set_reservd(cpp_vector(char) *const vec, size_t new_reservd) { + vec->reserve(new_reservd); + } + char cpp_vec_get_el(cpp_vector(char) *const vec, size_t idx) { + return (*vec)[idx]; + } + char cpp_vec_set_el(cpp_vector(char) *const vec, size_t idx, char v) { + return (*vec)[idx] = v; + } +} diff --git a/test/vec/api/prefix.h b/test/vec/api/prefix.h new file mode 100644 index 0000000..f0e0c1f --- /dev/null +++ b/test/vec/api/prefix.h @@ -0,0 +1 @@ +#define PREFIX vec_api diff --git a/test/vec/api/should_concat_arrays.c b/test/vec/api/should_concat_arrays.c new file mode 100644 index 0000000..51890b3 --- /dev/null +++ b/test/vec/api/should_concat_arrays.c @@ -0,0 +1,59 @@ +#include +#include +#include + +#include "prefix.h" +#include +#include + +it_should(concat_arrays) { + yavl_vec_t vec = YAVL_VEC_T_ALLOCATOR; + struct Array { + const size_t len; + char *const A; + } arrs[] = { + (struct Array){countof(A),&A[0]}, + (struct Array){countof(B),&B[0]} + }; + unsigned char arrInd[][countof(arrs)] = { + {0,1}, + {1,0} + }; + size_t total = 0; + for(size_t i=0; i +#include +#include + +it_should(convert_arrays) { + char *const heaparr = calloc(5,sizeof(char)); + int code=0; + if(heaparr==NULL) return 2; // OOM is not our problem + for(char i=0;i<4;++i) heaparr[i]=i; + yavl_vec_t vec = YAVL_VEC_T_ALLOCATOR; + printf("0. Prepare...\n"); + test_if(yavl_vec_fromarray(&vec, heaparr, sizeof(char), 4) == YAVL_VEC_RES_OK) + return code; + printf("1. Arr -> Vec\n"); + // from that point, YAVL took pointer ownership + test_if(yavl_vec_scale(&vec, 10) == YAVL_VEC_RES_OK) + goto exit; + printf("2. Vec[5] -> Vec[10]\n"); + test_if(yavl_vec_push(&vec, &B[0], sizeof(B)/sizeof(B[0])) == YAVL_VEC_RES_OK) + goto exit; + printf("3. Vec[4-10] = B\n"); + test_if(vec.len == vec.reservd + && vec.reservd == sizeof(B)/sizeof(B[0])+4) + goto exit; + printf("4. ok(Vec.len,Vec.reservd)\n"); + // check data + size_t i=0; + for(;i<4;++i) { + char j; + yavl_vec_get(&vec, i, &j); + test_if(heaparr[i]==j) printf("Bug: read: %i!=%i\n",heaparr[i],j); + printf(" * %2i | %2i\n",i,j); + test_if(j==i) goto exit; + } + for(;i<10;++i) { + char j; + yavl_vec_get(&vec, i, &j); + printf(" * %2i | %2i\n",B[i-4],j); + test_if(j==B[i-4]) break; + } + printf("5. Data check\n"); + exit: yavl_vec_free(&vec); + return code; +} diff --git a/test/vec/api/should_push_many.c b/test/vec/api/should_push_many.c new file mode 100644 index 0000000..7addd6a --- /dev/null +++ b/test/vec/api/should_push_many.c @@ -0,0 +1,30 @@ +#include +#include +#include + +#include "prefix.h" +#include + +it_should(push_many) { + yavl_vec_t vec = YAVL_VEC_T_ALLOCATOR; + char testcase = -1; + int code = 0; + + test_if(yavl_vec_init(&vec, sizeof(A[0]), countof(A)) == YAVL_VEC_RES_OK) + return code; + test_if(yavl_vec_push(&vec, A, countof(A)) == YAVL_VEC_RES_OK) + goto exit; + test_if(vec.len == vec.reservd && vec.reservd == countof(A)) + goto exit; + for(size_t i = 0; i < countof(A); ++i) { + char tescase = -1; + test_if(yavl_vec_get(&vec, i, &testcase) == YAVL_VEC_RES_OK) + break; + test_if(testcase == A[i]) { + break; + } + } + + exit: is(yavl_vec_free(&vec) == YAVL_VEC_RES_OK); + return code; +} diff --git a/test/vec/api/should_push_one.c b/test/vec/api/should_push_one.c new file mode 100644 index 0000000..a1838e1 --- /dev/null +++ b/test/vec/api/should_push_one.c @@ -0,0 +1,29 @@ +#include +#include +#include + +#include "prefix.h" +#include + +it_should(push_one) { + yavl_vec_t vec = YAVL_VEC_T_ALLOCATOR; + char testcase = -1; + int code = 0; + + printf("1. Init\n"); + test_if(yavl_vec_init(&vec, sizeof(A[0]), 1) == YAVL_VEC_RES_OK) + return code; + printf("2. Push\n"); + test_if(yavl_vec_push(&vec, &A[0], 1) == YAVL_VEC_RES_OK) + goto exit; + printf("3. Get\n"); + test_if(yavl_vec_get(&vec, 0, &testcase) == YAVL_VEC_RES_OK) + goto exit; + printf("4. Compares\n"); + test_if(testcase == A[0]) printf(" - tetcase (%i/%i)\n",testcase,A[0]); + test_if(vec.len == vec.reservd) printf(" - len (%zu/%zu)\n",vec.len,vec.reservd); + is(vec.reservd == 1); + + exit: is(yavl_vec_free(&vec) == YAVL_VEC_RES_OK); + return code; +} diff --git a/test/vec/perf/prefix.h b/test/vec/perf/prefix.h new file mode 100644 index 0000000..ac52b15 --- /dev/null +++ b/test/vec/perf/prefix.h @@ -0,0 +1 @@ +#define PREFIX vec_perf diff --git a/test/vec/perf/should_push_not_slower_than_cpp.c b/test/vec/perf/should_push_not_slower_than_cpp.c new file mode 100644 index 0000000..60923ba --- /dev/null +++ b/test/vec/perf/should_push_not_slower_than_cpp.c @@ -0,0 +1,137 @@ +#include "YAVL/vec.h" +#include "prefix.h" +#include +#include + +#include +#include +#include + +it_should(push_not_slower_than_cpp) { + struct timespec t[2] = {}; + double time[4] = {0.,0.,0.}; + + // RNG + timespec_get(&t[0], CLOCK_REALTIME); + srand(t[0].tv_nsec); + + // Prepare data + printf("Preparing CPP...\n"); + cpp_vector(char) *cpp = cpp_vec_new(); + const size_t default_reservd=cpp_vec_get_reservd(cpp); + + printf("Preparing YAVL...\n"); + yavl_vec_t yavl = YAVL_VEC_T_ALLOCATOR; + cpp_vec_free(cpp); + + printf("Preparing data array...\n"); + size_t tdata_len = 100L*1024L*1024L; + char *const restrict tdata = calloc(tdata_len,sizeof(tdata[0])); + if(tdata == NULL) return 0; + for(size_t i=0; il) { + l=l<<1; + void *new = realloc(res,l*sizeof(char)); + if(new) res=new; + } + res[i]=tdata[i]; + } + { + void *new = realloc(res,(l=i)*sizeof(char)); + if(new) res=new; + } + timespec_get(&t[1], TIME_UTC); + free(res); + t[1].tv_nsec-=t[0].tv_nsec; + t[1].tv_sec-=t[0].tv_sec; + time[3] += ((double)t[1].tv_nsec/1e9)+(t[1].tv_sec); + } + + #if COMPARE_RESIZE + printf(" * Test CPP resize\n"); { + size_t c=0; + cpp = cpp_vec_new(); + size_t reserv_old=0; + for(size_t i=0; itime[0]; +} From 1d73e4b468577f6740ba7d7ca2590b99a42dd1d8 Mon Sep 17 00:00:00 2001 From: SpacingBat3 Date: Tue, 26 May 2026 09:57:03 +0200 Subject: [PATCH 2/6] fix(test): Update `printf` specifiers I previously was using `int` as the iterator, but switched towards `size_t`. --- test/vec/api/should_convert_arrays.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/vec/api/should_convert_arrays.c b/test/vec/api/should_convert_arrays.c index 2fac9ca..9dfc467 100644 --- a/test/vec/api/should_convert_arrays.c +++ b/test/vec/api/should_convert_arrays.c @@ -31,7 +31,7 @@ it_should(convert_arrays) { char j; yavl_vec_get(&vec, i, &j); test_if(heaparr[i]==j) printf("Bug: read: %i!=%i\n",heaparr[i],j); - printf(" * %2i | %2i\n",i,j); + printf(" * %2zu | %2i\n",i,j); test_if(j==i) goto exit; } for(;i<10;++i) { From b44d4dcb28de95b2ad23c28115ef30ed8e9cc4e3 Mon Sep 17 00:00:00 2001 From: SpacingBat3 Date: Sat, 13 Jun 2026 16:49:42 +0200 Subject: [PATCH 3/6] feat(test): Multi-thread perf tests --- .../perf/should_push_not_slower_than_cpp.c | 170 ++++++++++-------- 1 file changed, 94 insertions(+), 76 deletions(-) diff --git a/test/vec/perf/should_push_not_slower_than_cpp.c b/test/vec/perf/should_push_not_slower_than_cpp.c index 60923ba..7c97221 100644 --- a/test/vec/perf/should_push_not_slower_than_cpp.c +++ b/test/vec/perf/should_push_not_slower_than_cpp.c @@ -6,84 +6,77 @@ #include #include #include +#include -it_should(push_not_slower_than_cpp) { - struct timespec t[2] = {}; - double time[4] = {0.,0.,0.}; - - // RNG - timespec_get(&t[0], CLOCK_REALTIME); - srand(t[0].tv_nsec); +static size_t default_reservd = 0; - // Prepare data - printf("Preparing CPP...\n"); - cpp_vector(char) *cpp = cpp_vec_new(); - const size_t default_reservd=cpp_vec_get_reservd(cpp); - - printf("Preparing YAVL...\n"); - yavl_vec_t yavl = YAVL_VEC_T_ALLOCATOR; - cpp_vec_free(cpp); +typedef struct { + double res_time; + const char *const restrict test_data; + size_t test_data_len; +} bench_t; - printf("Preparing data array...\n"); - size_t tdata_len = 100L*1024L*1024L; - char *const restrict tdata = calloc(tdata_len,sizeof(tdata[0])); - if(tdata == NULL) return 0; - for(size_t i=0; ires_time=0; - printf("Benchmarking CPP...\n"); - for(unsigned char b=1;b<=20;++b) { + for(unsigned char b=0;b<=20;++b) { cpp = cpp_vec_new(); timespec_get(&t[0], TIME_UTC); - for(size_t i=0; itest_data_len; ++i) + cpp_vec_push(cpp,data->test_data[i]); timespec_get(&t[1], TIME_UTC); cpp_vec_free(cpp); t[1].tv_nsec-=t[0].tv_nsec; t[1].tv_sec-=t[0].tv_sec; - time[0] += (((double)t[1].tv_nsec)/1e9)+(t[1].tv_sec); - } - printf("Benchmarking YAVL...\n"); - for(unsigned char b=1;b<=20;++b) { - yavl_vec_init(&yavl,sizeof(char),default_reservd); - timespec_get(&t[0], TIME_UTC); - for(size_t i=0; ires_time += (((double)t[1].tv_nsec)/1e9)+(t[1].tv_sec); } - #if COMPARE_MULTIPUSH - printf("Benchmarking YAVL (pushing entire array)...\n"); - for(unsigned char b=1;b<=20;++b) { + return thrd_success; +} + +static int bench_yavl(void* userdata) { + bench_t *const data = userdata; + struct timespec t[2] = {}; + yavl_vec_t yavl = YAVL_VEC_T_ALLOCATOR; + data->res_time=0; + + for(unsigned char b=0;b<=20;++b) { yavl_vec_init(&yavl,sizeof(char),default_reservd); timespec_get(&t[0], TIME_UTC); - yavl_vec_push(&yavl, tdata, tdata_len); + for(size_t i=0; itest_data_len; ++i) + yavl_vec_push(&yavl, data->test_data+i, 1); timespec_get(&t[1], TIME_UTC); yavl_vec_free(&yavl); t[1].tv_nsec-=t[0].tv_nsec; t[1].tv_sec-=t[0].tv_sec; - time[2] += ((double)t[1].tv_nsec/1e9)+(t[1].tv_sec); + if(b) + data->res_time += ((double)t[1].tv_nsec/1e9)+(t[1].tv_sec); } - #endif + return thrd_success; +} + +static int bench_inline(void* userdata) { + bench_t *const data = userdata; + struct timespec t[2] = {}; + data->res_time=0; - printf("Benchmarking inline C array...\n"); - for(unsigned char b=1;b<=20;++b) { + for(unsigned char b=0;b<=20;++b) { char* res = malloc(1*sizeof(char)); size_t l = 1; timespec_get(&t[0], TIME_UTC); size_t i=0; - for(; itest_data_len; ++i) { if(i>l) { l=l<<1; void *new = realloc(res,l*sizeof(char)); if(new) res=new; } - res[i]=tdata[i]; + res[i]=data->test_data[i]; } { void *new = realloc(res,(l=i)*sizeof(char)); @@ -93,45 +86,70 @@ it_should(push_not_slower_than_cpp) { free(res); t[1].tv_nsec-=t[0].tv_nsec; t[1].tv_sec-=t[0].tv_sec; - time[3] += ((double)t[1].tv_nsec/1e9)+(t[1].tv_sec); + if(b) + data->res_time += ((double)t[1].tv_nsec/1e9)+(t[1].tv_sec); } + return thrd_success; +} - #if COMPARE_RESIZE - printf(" * Test CPP resize\n"); { - size_t c=0; - cpp = cpp_vec_new(); - size_t reserv_old=0; - for(size_t i=0; i0;--i) { + char j = rand() % (i + 1); + char temp = ind[i]; + ind[i] = ind[j]; + ind[j] = temp; + } + + printf("Running benchmarks in their own threads...\n"); + // Random thread creation, ordered thread collection + for(size_t i=0;itime[0]; + return bdata[0].res_time>bdata[1].res_time; } From 52693f85d6dabf1ff50eb8fcc6a334a77ac7a077 Mon Sep 17 00:00:00 2001 From: SpacingBat3 Date: Sun, 14 Jun 2026 11:40:49 +0200 Subject: [PATCH 4/6] fix(tests): Single-threaded perf variant Implement generic STDC_NO_THREADS version for tests, so tests may execute regardless of target platform and compiler limitations. --- .../perf/should_push_not_slower_than_cpp.c | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/test/vec/perf/should_push_not_slower_than_cpp.c b/test/vec/perf/should_push_not_slower_than_cpp.c index 7c97221..027f343 100644 --- a/test/vec/perf/should_push_not_slower_than_cpp.c +++ b/test/vec/perf/should_push_not_slower_than_cpp.c @@ -6,7 +6,9 @@ #include #include #include +#ifndef __STDC_NO_THREADS__ #include +#endif static size_t default_reservd = 0; @@ -35,8 +37,11 @@ static int bench_cpp(void* userdata) { if(b) data->res_time += (((double)t[1].tv_nsec)/1e9)+(t[1].tv_sec); } - + #ifndef __STDC_NO_THREADS__ return thrd_success; + #else + return 0; + #endif } static int bench_yavl(void* userdata) { @@ -57,7 +62,11 @@ static int bench_yavl(void* userdata) { if(b) data->res_time += ((double)t[1].tv_nsec/1e9)+(t[1].tv_sec); } + #ifndef __STDC_NO_THREADS__ return thrd_success; + #else + return 0; + #endif } static int bench_inline(void* userdata) { @@ -89,7 +98,11 @@ static int bench_inline(void* userdata) { if(b) data->res_time += ((double)t[1].tv_nsec/1e9)+(t[1].tv_sec); } + #ifndef __STDC_NO_THREADS__ return thrd_success; + #else + return 0; + #endif } it_should(push_not_slower_than_cpp) { @@ -116,8 +129,12 @@ it_should(push_not_slower_than_cpp) { {.test_data=tdata, .test_data_len=tdata_len}, {.test_data=tdata, .test_data_len=tdata_len}, }; + #ifndef __STDC_NO_THREADS__ thrd_t thr[3]; thrd_start_t funs[3] = { bench_yavl,bench_cpp,bench_inline }; + #else + typeof(&bench_yavl) funs[3] = { bench_yavl,bench_cpp,bench_inline }; + #endif char *strs[] = {"YAVL","CPP","inline"}; char ind[3]={0,1,2}; @@ -128,6 +145,7 @@ it_should(push_not_slower_than_cpp) { ind[j] = temp; } + #ifndef __STDC_NO_THREADS__ printf("Running benchmarks in their own threads...\n"); // Random thread creation, ordered thread collection for(size_t i=0;i Date: Sun, 14 Jun 2026 22:52:09 +0200 Subject: [PATCH 5/6] fix(tests): More generic multi-thread This uses a combination of CMake and macros to detect and provide more generic code. --- test/CMakeLists.txt | 43 ++++++++++++++- test/compiler/posix_threads.c | 15 +++++ test/compiler/stdc_threads.c | 15 +++++ .../perf/should_push_not_slower_than_cpp.c | 55 +++++++++++-------- 4 files changed, 105 insertions(+), 23 deletions(-) create mode 100644 test/compiler/posix_threads.c create mode 100644 test/compiler/stdc_threads.c diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index fd2c880..bbc6614 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,7 +7,7 @@ file(GLOB_RECURSE "${OLD_PROJECT_NAME}_TESTS" LIST_DIRECTORIES false CONFIGURE_DEPENDS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" - **/*.c + vec/**/*.c ) list(JOIN "${OLD_PROJECT_NAME}_TESTS" " " testrunner_str) @@ -20,6 +20,7 @@ create_test_sourcelist( # Additional libraries add_subdirectory("lib/") +# Test runner add_executable("${OLD_PROJECT_NAME}TestRunner" TestRunner.c ${${OLD_PROJECT_NAME}_TEST_SRC}) add_executable("${OLD_PROJECT_NAME}::TestRunner" ALIAS "${OLD_PROJECT_NAME}TestRunner") add_executable("${PROJECT_NAME}::Runner" ALIAS "${OLD_PROJECT_NAME}TestRunner") @@ -28,6 +29,46 @@ target_include_directories("${OLD_PROJECT_NAME}TestRunner" PRIVATE "include/") set_target_properties("${OLD_PROJECT_NAME}TestRunner" PROPERTIES CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/test") +# Check multithreading +if(NOT DEFINED TRY_STDC_THREADS) + message(CHECK_START "Test if is compiling") + try_compile(TRY_STDC_THREADS + SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/compiler/stdc_threads.c" + ) + if(TRY_STDC_THREADS) + message(CHECK_PASS "Success") + else() + message(CHECK_FAIL "Failure") + endif() +endif() + +if(TRY_STDC_THREADS) + set(THREAD_IMPL "C") + set(THREAD_IMPL_ENUM 1) +else(TRY_STDC_THREADS) + #POSIX + set(THREADS_PREFER_PTHREAD_FLAG TRUE) + find_package(Threads) + if(CMAKE_USE_PTHREADS_INIT AND NOT DEFINED TRY_POSIX_THREADS) + try_compile(TRY_POSIX_THREADS + SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/compiler/stdc_threads.c" + LINK_LIBRARIES Threads::Threads + ) + endif(CMAKE_USE_PTHREADS_INIT AND NOT DEFINED TRY_POSIX_THREADS) + if(TRY_POSIX_THREADS) + set(THREAD_IMPL "POSIX") + set(THREAD_IMPL_ENUM 2) + target_link_libraries("${OLD_PROJECT_NAME}TestRunner" PRIVATE Threads::Threads) + else(TRY_POSIX_THREADS) + set(THREAD_IMPL "") + set(THREAD_IMPL_ENUM 0) + endif(TRY_POSIX_THREADS) +endif(TRY_STDC_THREADS) + +message(STATUS "Threads implementation: ${THREAD_IMPL}") +target_compile_definitions("${OLD_PROJECT_NAME}TestRunner" + PRIVATE "DETECTED_THREAD_IMPL=${THREAD_IMPL_ENUM}") + foreach(test IN LISTS "${OLD_PROJECT_NAME}_TESTS") get_filename_component(TName "${test}" NAME_WE) get_filename_component(TPath "${test}" DIRECTORY) diff --git a/test/compiler/posix_threads.c b/test/compiler/posix_threads.c new file mode 100644 index 0000000..b7978c4 --- /dev/null +++ b/test/compiler/posix_threads.c @@ -0,0 +1,15 @@ +#include +#include + +void* thr_fun(void* pass) { + return pass; +} + +int main() { + pthread_t thr; + pthread_create(&thr, NULL, thr_fun, (void*) 0xADD); + void* var=NULL; + pthread_join(thr, &var); + assert(var==0xADD); + return 0; +} \ No newline at end of file diff --git a/test/compiler/stdc_threads.c b/test/compiler/stdc_threads.c new file mode 100644 index 0000000..ba08d80 --- /dev/null +++ b/test/compiler/stdc_threads.c @@ -0,0 +1,15 @@ +#include +#include + +int thr_fun(void*_) { + return 0xADD; +} + +int main() { + thrd_t thr; + thrd_create(&thr, thr_fun, NULL); + int var; + thrd_join(thr, &var); + assert(var==0xADD); + return 0; +} \ No newline at end of file diff --git a/test/vec/perf/should_push_not_slower_than_cpp.c b/test/vec/perf/should_push_not_slower_than_cpp.c index 027f343..fb0d46f 100644 --- a/test/vec/perf/should_push_not_slower_than_cpp.c +++ b/test/vec/perf/should_push_not_slower_than_cpp.c @@ -6,8 +6,20 @@ #include #include #include -#ifndef __STDC_NO_THREADS__ + +#if !defined(__STDC_NO_THREADS__) && DETECTED_THREAD_IMPL == 1 // C #include +#define THREADS_INCLUDED 1 +#define THREAD_RET int +#define THREAD_RET_OK thrd_success +#elif DETECTED_THREAD_IMPL == 2 // POSIX +#include +#define PTHREAD_INCLUDED 1 +#define THREAD_RET void* +#define THREAD_RET_OK NULL +#else +#define THREAD_RET int +#define THREAD_RET_OK 0 #endif static size_t default_reservd = 0; @@ -18,7 +30,7 @@ typedef struct { size_t test_data_len; } bench_t; -static int bench_cpp(void* userdata) { +static THREAD_RET bench_cpp(void* userdata) { bench_t *const data = userdata; struct timespec t[2] = {}; cpp_vector(char) *cpp = NULL; @@ -37,14 +49,10 @@ static int bench_cpp(void* userdata) { if(b) data->res_time += (((double)t[1].tv_nsec)/1e9)+(t[1].tv_sec); } - #ifndef __STDC_NO_THREADS__ - return thrd_success; - #else - return 0; - #endif + return THREAD_RET_OK; } -static int bench_yavl(void* userdata) { +static THREAD_RET bench_yavl(void* userdata) { bench_t *const data = userdata; struct timespec t[2] = {}; yavl_vec_t yavl = YAVL_VEC_T_ALLOCATOR; @@ -62,14 +70,10 @@ static int bench_yavl(void* userdata) { if(b) data->res_time += ((double)t[1].tv_nsec/1e9)+(t[1].tv_sec); } - #ifndef __STDC_NO_THREADS__ - return thrd_success; - #else - return 0; - #endif + return THREAD_RET_OK; } -static int bench_inline(void* userdata) { +static THREAD_RET bench_inline(void* userdata) { bench_t *const data = userdata; struct timespec t[2] = {}; data->res_time=0; @@ -98,11 +102,7 @@ static int bench_inline(void* userdata) { if(b) data->res_time += ((double)t[1].tv_nsec/1e9)+(t[1].tv_sec); } - #ifndef __STDC_NO_THREADS__ - return thrd_success; - #else - return 0; - #endif + return THREAD_RET_OK; } it_should(push_not_slower_than_cpp) { @@ -129,9 +129,12 @@ it_should(push_not_slower_than_cpp) { {.test_data=tdata, .test_data_len=tdata_len}, {.test_data=tdata, .test_data_len=tdata_len}, }; - #ifndef __STDC_NO_THREADS__ + #if THREADS_INCLUDED thrd_t thr[3]; thrd_start_t funs[3] = { bench_yavl,bench_cpp,bench_inline }; + #elif PTHREAD_INCLUDED + pthread_t thr[3]; + typeof(&bench_yavl) funs[3] = { bench_yavl,bench_cpp,bench_inline }; #else typeof(&bench_yavl) funs[3] = { bench_yavl,bench_cpp,bench_inline }; #endif @@ -145,19 +148,27 @@ it_should(push_not_slower_than_cpp) { ind[j] = temp; } - #ifndef __STDC_NO_THREADS__ + #if THREADS_INCLUDED || PTHREAD_INCLUDED printf("Running benchmarks in their own threads...\n"); // Random thread creation, ordered thread collection for(size_t i=0;i Date: Sun, 14 Jun 2026 22:52:21 +0200 Subject: [PATCH 6/6] fix(tests): Unused var --- test/vec/api/should_push_many.c | 1 - 1 file changed, 1 deletion(-) diff --git a/test/vec/api/should_push_many.c b/test/vec/api/should_push_many.c index 7addd6a..ccc04cf 100644 --- a/test/vec/api/should_push_many.c +++ b/test/vec/api/should_push_many.c @@ -17,7 +17,6 @@ it_should(push_many) { test_if(vec.len == vec.reservd && vec.reservd == countof(A)) goto exit; for(size_t i = 0; i < countof(A); ++i) { - char tescase = -1; test_if(yavl_vec_get(&vec, i, &testcase) == YAVL_VEC_RES_OK) break; test_if(testcase == A[i]) {