Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -142,6 +145,10 @@ foreach(header "${${PROJECT_NAME}_GEN_HEADERS}")
)
endforeach()

# Tests
# =====
add_subdirectory("test/")

# Arch PKGBUILD
# =============
configure_file(
Expand Down
102 changes: 102 additions & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
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}"
vec/**/*.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/")

# 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")
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")

# Check multithreading
if(NOT DEFINED TRY_STDC_THREADS)
message(CHECK_START "Test if <threads.h> 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 "<none>")
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)
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()
15 changes: 15 additions & 0 deletions test/compiler/posix_threads.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <pthread.h>
#include <assert.h>

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;
}
15 changes: 15 additions & 0 deletions test/compiler/stdc_threads.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include <threads.h>
#include <assert.h>

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;
}
3 changes: 3 additions & 0 deletions test/include/TestRunner.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#include "TestRunner/prefix.h"
#include "TestRunner/fun.h"
#include "TestRunner/res.h"
4 changes: 4 additions & 0 deletions test/include/TestRunner/fun.h
Original file line number Diff line number Diff line change
@@ -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]))
34 changes: 34 additions & 0 deletions test/include/TestRunner/prefix.h
Original file line number Diff line number Diff line change
@@ -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
7 changes: 7 additions & 0 deletions test/include/TestRunner/res.h
Original file line number Diff line number Diff line change
@@ -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
2 changes: 2 additions & 0 deletions test/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_subdirectory("cppvec")
#add_subdirectory("bench") #TODO!
3 changes: 3 additions & 0 deletions test/lib/cppvec/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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/")
26 changes: 26 additions & 0 deletions test/lib/cppvec/include/RefLib.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#ifdef __cplusplus
#include <vector>
extern "C" {
#define cpp_vector(T,...) std::vector<T __VA_OPT__(,) __VA_ARGS__>
#else
#define cpp_vector(T,...) struct vector_##T
/// Type definition
cpp_vector(char);
#endif
#include <stddef.h>
#include <stdbool.h>
// 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
41 changes: 41 additions & 0 deletions test/lib/cppvec/src/lib.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#include <RefLib.h>

// 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;
}
}
1 change: 1 addition & 0 deletions test/vec/api/prefix.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#define PREFIX vec_api
59 changes: 59 additions & 0 deletions test/vec/api/should_concat_arrays.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <stdbool.h>
#include <stddef.h>
#include <YAVL/vec.h>

#include "prefix.h"
#include <TestRunner.h>
#include <stdio.h>

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<countof(arrs); ++i) total+=arrs[i].len;

int code = 0;
for(size_t i=0; i<countof(arrInd); ++i) {
yavl_vec_init(
/*vec=*/&vec,
/*data_align=*/sizeof(char),
/*data_reserv=*/total
);
printf("0. Init\n");
for(size_t j=0; j<countof(arrInd[i]);++j) {
test_if(yavl_vec_push(&vec, arrs[arrInd[i][j]].A, arrs[arrInd[i][j]].len)
== YAVL_VEC_RES_OK)
goto exit;
}
printf("1. Push arrays\n");
for(size_t j=0,ci=0; j<countof(arrInd[i]);++j) for(size_t c=0; c<arrs[arrInd[i][j]].len;++c,++ci) {
char testcase = -1;
test_if(yavl_vec_get(&vec,ci,&testcase) == YAVL_VEC_RES_OK)
goto exit;
printf("%zu: %i | %i (%u)\n",ci,testcase,arrs[arrInd[i][j]].A[c],arrInd[i][j]);
//test_if(testcase == arrs[arrInd[i][j]].A[c]) {
// raise(SIGSTOP);
// goto exit;
//}
}
printf("2. Validate data\n");
test_if(vec.len == vec.reservd && vec.reservd == total)
break;
printf("3. Validate reservd\n");
test_if(yavl_vec_free(&vec) == YAVL_VEC_RES_OK)
break;
printf("4. After free\n");
}
if(false) exit: is(yavl_vec_free(&vec) == YAVL_VEC_RES_OK);
return code;
}
46 changes: 46 additions & 0 deletions test/vec/api/should_convert_arrays.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "YAVL/vec.h"
#include "prefix.h"
#include <TestRunner.h>
#include <stdlib.h>
#include <stdio.h>

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(" * %2zu | %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;
}
Loading