Add C++ tests

This commit is contained in:
jun 2025-02-23 23:45:31 +01:00
parent 4c35d180e8
commit c1e125dfcb
13 changed files with 433 additions and 254 deletions

View File

@ -2,9 +2,9 @@ cmake_minimum_required(VERSION 3.12)
set(PROJECT_NAME libguf) set(PROJECT_NAME libguf)
project(${PROJECT_NAME}) project(${PROJECT_NAME})
set(SOURCES src/guf_str.c) # set(SOURCES src/guf_str.c)
add_library(${PROJECT_NAME} STATIC ${SOURCES}) # add_library(${PROJECT_NAME} STATIC ${SOURCES})
# target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}}/lib/guf) # target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}}/lib/guf)
# target_include_directories(${PROJECT_NAME} PRIVATE src) # target_include_directories(${PROJECT_NAME} PRIVATE src)
@ -12,46 +12,60 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_C_EXTENSIONS OFF)
set(CMAKE_C_STANDARD 17) set(CMAKE_C_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 20)
if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/bin)
endif () endif ()
add_executable(libguf_test ${SOURCES} src/guf_test.c src/guf_test_dict_impl.c) add_executable(libguf_example src/test/guf_example.c src/test/guf_dict_impl.c)
target_include_directories(libguf_test PRIVATE src) target_include_directories(libguf_example PRIVATE src src/test)
add_executable(libguf_test src/test/guf_test.cpp src/test/guf_dict_impl.c src/test/guf_dbuf_impl.c src/test/guf_rand_impl.c)
target_include_directories(libguf_test PRIVATE src src/test)
if (NOT DEFINED MSVC)
set(WARNING_FLAGS_C -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wconversion -Wno-sign-conversion -Wdouble-promotion -Wno-unused-function)
set(WARNING_FLAGS_CXX -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wshadow -Wundef -Wstrict-overflow=5 -Wsign-promo -Wcast-align -Wcast-qual -Woverloaded-virtual -Wredundant-decls -Wctor-dtor-privacy -Wdisabled-optimization -Wformat=2 -Winit-self -Wswitch-default -Wno-unused-function)
set(DBG_FLAGS -fsanitize=undefined,address -g3 -glldb -Og)
else ()
set(WARNING_FLAGS_C /W4)
set(WARNING_FLAGS_CXX /W4)
set(DBG_FLAGS /fsanitize=address)
endif ()
if (NOT DEFINED CMAKE_DEBUG_POSTFIX)
set(CMAKE_DEBUG_POSTFIX _dbg)
endif ()
set_target_properties(libguf_example libguf_test PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_available)
if (ipo_available AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
message(STATUS "LTO enabled")
set_target_properties(libguf_example libguf_test PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
else()
message(STATUS "LTO disabled")
endif()
if (TARGET libguf_test) if (TARGET libguf_test)
message("-- Configure libguf_test...") message(STATUS "Configure libguf_test...")
if (NOT DEFINED MSVC) target_compile_options(libguf_test PRIVATE ${WARNING_FLAGS_CXX} $<$<CONFIG:Debug>: ${DBG_FLAGS}>)
set(WARNING_FLAGS_C -Wall -Wextra -Wpedantic -Wvla -Wshadow -Wundef -Wmisleading-indentation -Wnull-dereference -Wswitch-default -Wconversion -Wno-sign-conversion -Wdouble-promotion -Wno-unused-function) target_link_options(libguf_test PRIVATE ${WARNING_FLAGS_CXX} $<$<CONFIG:Debug>: ${DBG_FLAGS}> )
else ()
set(WARNING_FLAGS_CXX /W4)
endif ()
include(CheckIPOSupported)
check_ipo_supported(RESULT ipo_available)
if (ipo_available AND (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo"))
message(STATUS "LTO enabled")
set_target_properties(${PROJECT_NAME} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
else()
message(STATUS "LTO disabled")
endif()
if (NOT DEFINED CMAKE_DEBUG_POSTFIX)
set(CMAKE_DEBUG_POSTFIX _dbg)
endif ()
set_target_properties(libguf_test PROPERTIES DEBUG_POSTFIX ${CMAKE_DEBUG_POSTFIX})
if (NOT DEFINED MSVC)
set(DBG_FLAGS -fsanitize=undefined,address -g3 -glldb -Og)
else ()
set(DBG_FLAGS /fsanitize=address)
endif()
target_compile_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}>)
target_link_options(libguf_test PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}> )
message(STATUS "Configured libguf_test") message(STATUS "Configured libguf_test")
endif() endif()
if (TARGET libguf_example)
message(STATUS "Configure libguf_example...")
target_compile_options(libguf_example PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}>)
target_link_options(libguf_example PRIVATE ${WARNING_FLAGS_C} $<$<CONFIG:Debug>: ${DBG_FLAGS}> )
message(STATUS "Configured libguf_example")
endif()

View File

@ -1,190 +0,0 @@
from functools import partial
from testgen import gen_test_struct, gen_res_str
DEFAULT_N = 4
test_funs = list()
def test_push(is_str = False, n = DEFAULT_N):
buf = list()
for i in range(n):
if is_str:
buf.append("element at index " + str(i))
else:
buf.append(i)
name = ""
if is_str:
name = "str"
return gen_test_struct(f"tst_push{name}", f"guf_dbuf_test: push {name}", gen_res_str(buf))
test_funs.append(partial(test_push, False))
test_funs.append(partial(test_push, True))
def test_insert_empty_front():
buf = list()
buf.insert(0, 3141)
return gen_test_struct("tst_insert_empty_front", "guf_dbuf_test: insert empty front", gen_res_str(buf))
test_funs.append(test_insert_empty_front)
def test_insert_empty_back():
buf = list()
buf.insert(1, 3141)
return gen_test_struct("tst_insert_empty_back", "guf_dbuf_test: insert empty back", gen_res_str(buf))
test_funs.append(test_insert_empty_back)
def test_insert(is_str = False, n = DEFAULT_N):
buf = list()
for i in range(n):
if i % 7 == 0:
idx = len(buf)
elif i % 2 == 0:
idx = 1
else:
assert(len(buf) > 0)
idx = len(buf) - 1
if is_str:
buf.insert(idx, f"element at index {idx}")
else:
buf.insert(idx, i)
if is_str:
start = "pi" * 64
end = "euler" * 64
else:
start = 3141
end = 2718
buf.insert(0, start)
buf.insert(len(buf), end)
buf.insert(1, start * 2)
buf.insert(len(buf) - 1, end * 2)
name = "int"
if is_str:
name = "str"
return gen_test_struct(f"tst_insert{name}", f"guf_dbuf_test: insert {name}", gen_res_str(buf))
test_funs.append(partial(test_insert, False))
test_funs.append(partial(test_insert, True))
def test_erase(is_str = False, n = DEFAULT_N):
buf = list()
for i in range(n):
if is_str:
buf.append("element at index " + str(i))
else:
buf.append(i)
for i, elem in enumerate(buf):
if i % 2 == 0:
del elem
name = "int"
if is_str:
name = "str"
return gen_test_struct(f"tst_erase{name}", f"guf_dbuf_test: erase {name}", gen_res_str(buf))
test_funs.append(partial(test_erase, False))
test_funs.append(partial(test_erase, True))
def test_erase_all(is_str = False, n = DEFAULT_N):
buf = list()
for i in range(n):
if is_str:
buf.append("element at index " + str(i))
else:
buf.append(i)
for i, elem in enumerate(buf):
del elem
name = "int"
if is_str:
name = "str"
return gen_test_struct(f"tst_remove{name}", f"guf_dbuf_test: erase {name} all", gen_res_str(buf))
test_funs.append(partial(test_erase_all, False))
test_funs.append(partial(test_erase_all, True))
def test_pop(is_str = False, n = DEFAULT_N):
buf = list()
for i in range(n):
if is_str:
buf.append("element at index " + str(i))
else:
buf.append(i)
new_buf = list()
for i in range(len(buf)):
new_buf.append(buf.pop())
new_buf.append(len(buf))
name = "int"
if is_str:
name = "str"
return gen_test_struct(f"tst_pop{name}", f"guf_dbuf_test: pop {name}", gen_res_str(buf))
test_funs.append(partial(test_pop, False))
test_funs.append(partial(test_pop, True))
def test_front_back(is_str = False, n = DEFAULT_N):
buf = list()
new_buf = list()
for i in range(n):
if is_str:
buf.append("element at index " + str(i))
else:
buf.append(i)
if i % 2:
new_buf.append(buf[0]) # front
else:
new_buf.append(buf[-1]) # back
if is_str:
new_buf[0] = "first elem"
new_buf[-1] = "last elem"
else:
new_buf[0] = 12345
new_buf[-1] = 54321
new_buf.append(len(new_buf))
name = "int"
if is_str:
name = "str"
return gen_test_struct(f"tst_front_back{name}", f"guf_dbuf_test: front() back() {name}", gen_res_str(new_buf))
test_funs.append(partial(test_front_back, False))
test_funs.append(partial(test_front_back, True))
def test_at(is_str = False, n = DEFAULT_N):
buf = list()
for i in range(n):
if is_str:
buf.append("element at index " + str(i))
else:
buf.append(i)
new_buf = list()
for elem in reversed(buf):
new_buf.append(elem * 2)
name = "int"
if is_str:
name = "str"
return gen_test_struct(f"tst_at{name}", f"guf_dbuf_test: at() {name}", gen_res_str(new_buf))
test_funs.append(partial(test_at, False))
test_funs.append(partial(test_at, True))
def all_tests():
return test_funs

View File

@ -156,6 +156,7 @@ GUF_DICT_FN_KEYWORDS ptrdiff_t GUF_CAT(GUF_DICT_NAME, _iter_to_idx)(const GUF_DI
GUF_DICT_FN_KEYWORDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _find_val_if)(GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) begin, GUF_CAT(GUF_DICT_NAME, _iter) end, bool (*predicate)(const GUF_DICT_VAL_T *)); GUF_DICT_FN_KEYWORDS GUF_CAT(GUF_DICT_NAME, _iter) GUF_CAT(GUF_DICT_NAME, _find_val_if)(GUF_DICT_NAME *ht, GUF_CAT(GUF_DICT_NAME, _iter) begin, GUF_CAT(GUF_DICT_NAME, _iter) end, bool (*predicate)(const GUF_DICT_VAL_T *));
#endif #endif
// #define GUF_DICT_IMPL /* DEBUGGGGGGGGG */ // #define GUF_DICT_IMPL /* DEBUGGGGGGGGG */
#if defined(GUF_DICT_IMPL) || defined(GUF_DICT_IMPL_STATIC) #if defined(GUF_DICT_IMPL) || defined(GUF_DICT_IMPL_STATIC)

View File

@ -1,6 +1,8 @@
#ifndef GUF_RAND_H #ifndef GUF_RAND_H
#define GUF_RAND_H #define GUF_RAND_H
#include "guf_common.h"
#if defined(GUF_IMPL_STATIC) || defined(GUF_STATIC) #if defined(GUF_IMPL_STATIC) || defined(GUF_STATIC)
#define GUF_FN_KEYWORDS static #define GUF_FN_KEYWORDS static
#else #else

28
src/test/guf_dbuf_impl.c Normal file
View File

@ -0,0 +1,28 @@
#include "guf_dbuf_impl.h"
#define GUF_CNT_NAME dbuf_int
#define GUF_T int
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_IMPL
#include "guf_dbuf.h"
#define GUF_CNT_NAME dbuf_float
#define GUF_T float
#define GUF_T_IS_INTEGRAL_TYPE
#define GUF_IMPL
#include "guf_dbuf.h"
#define GUF_T guf_cstr_heap
#define GUF_CNT_NAME dbuf_heap_cstr
#define GUF_T_COPY guf_cstr_heap_copy
#define GUF_T_MOVE guf_cstr_heap_move
#define GUF_T_FREE guf_cstr_heap_free
#define GUF_T_EQ guf_cstr_heap_eq
#define GUF_IMPL
#include "guf_dbuf.h"
#define GUF_T guf_cstr_const
#define GUF_CNT_NAME dbuf_const_cstr
#define GUF_T_EQ guf_cstr_const_eq
#define GUF_IMPL
#include "guf_dbuf.h"

29
src/test/guf_dbuf_impl.h Normal file
View File

@ -0,0 +1,29 @@
#ifndef GUF_DBUF_IMPL_H
#define GUF_DBUF_IMPL_H
#include "guf_cstr.h"
#define GUF_CNT_NAME dbuf_int
#define GUF_T int
#define GUF_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_CNT_NAME dbuf_float
#define GUF_T float
#define GUF_T_IS_INTEGRAL_TYPE
#include "guf_dbuf.h"
#define GUF_T guf_cstr_heap
#define GUF_CNT_NAME dbuf_heap_cstr
#define GUF_T_COPY guf_cstr_heap_copy
#define GUF_T_MOVE guf_cstr_heap_move
#define GUF_T_FREE guf_cstr_heap_free
#define GUF_T_EQ guf_cstr_heap_eq
#include "guf_dbuf.h"
#define GUF_T guf_cstr_const
#define GUF_CNT_NAME dbuf_const_cstr
#define GUF_T_EQ guf_cstr_const_eq
#include "guf_dbuf.h"
#endif

View File

@ -1,4 +1,4 @@
#include "guf_test_dict_impl.h" #include "guf_dict_impl.h"
#define GUF_DICT_KEY_T guf_cstr_const #define GUF_DICT_KEY_T guf_cstr_const
#define GUF_DICT_KEY_T_EQ guf_cstr_const_eq #define GUF_DICT_KEY_T_EQ guf_cstr_const_eq

View File

@ -4,6 +4,7 @@
#include <time.h> #include <time.h>
#include "guf_init.h" /* Must be included once */ #include "guf_init.h" /* Must be included once */
#include "guf_alloc_libc.h" #include "guf_alloc_libc.h"
#include "guf_cstr.h" #include "guf_cstr.h"
@ -46,7 +47,7 @@
#define GUF_IMPL_STATIC #define GUF_IMPL_STATIC
#include "guf_rand.h" #include "guf_rand.h"
#include "guf_test_dict_impl.h" #include "guf_dict_impl.h"
int main(void) int main(void)
{ {

2
src/test/guf_rand_impl.c Normal file
View File

@ -0,0 +1,2 @@
#define GUF_IMPL
#include "guf_rand.h"

321
src/test/guf_test.cpp Normal file
View File

@ -0,0 +1,321 @@
#include <vector>
#include <unordered_map>
#include <string>
#include <cstdio>
#include <iostream>
#include <chrono>
#include <iomanip>
extern "C"
{
#include "guf_init.h"
#include "guf_alloc_libc.h"
#include "guf_dbuf_impl.h"
#include "guf_dict_impl.h"
#include "guf_rand.h"
}
struct Test
{
const std::string name {};
std::chrono::duration<float, std::milli> runtime_ms {0};
bool passed {false}, done {false};
size_t num_failed_checks {0}, num_passed_checks {0};
Test(std::string& name) : name{name} {}
virtual ~Test() = default;
bool operator==(const Test &other) const {return name == other.name;}
size_t total_checks() const {return num_passed_checks + num_failed_checks;}
virtual bool run() = 0;
friend std::ostream& operator<<(std::ostream &os, const Test& tst)
{
std::ios_base::fmtflags os_flags = os.flags();
std::streamsize os_precision = os.precision();
os << tst.name << ": " << (tst.passed ? "PASS" : "FAIL");
os << std::fixed << std::setprecision(2);
os << " (" << tst.num_passed_checks << "/" << tst.total_checks() << ") in " << tst.runtime_ms.count() << " ms";
os.precision(os_precision);
os.flags(os_flags);
return os;
}
};
template<>
struct std::hash<std::unique_ptr<Test>> {
std::size_t operator()(const std::unique_ptr<Test>& test)
{
if (!test.get()) {
return 0;
}
return std::hash<std::string>()(test.get()->name);
}
};
#define GUF_TEST_CHECK(COND, MSG, TEST_OBJ_PTR) do { \
if (!(COND)) { \
std::cerr << this->name << ": FAILED CHECK " << MSG << " (line " << __LINE__ << " file " << __FILE__ << ")\n"; \
TEST_OBJ_PTR->num_failed_checks++; \
} else { \
TEST_OBJ_PTR->num_passed_checks++; \
} \
} while (0);
struct DbufIntTest : public Test
{
DbufIntTest(std::string name) : Test(name) {};
private:
std::vector<int> dbuf_to_vec(dbuf_int *dbuf)
{
std::vector<int> vec;
GUF_CNT_FOREACH(dbuf, dbuf_int, it) {
vec.push_back(*it.ptr);
}
return vec;
}
void test_push(dbuf_int *dbuf, int n)
{
std::vector<int> vec = dbuf_to_vec(dbuf);
GUF_TEST_CHECK(std::ssize(vec) == dbuf->size, "test_push(): initial size", this);
for (int i = 0; i < n; ++i) {
dbuf_int_push_val(dbuf, i);
vec.push_back(i);
GUF_TEST_CHECK(*dbuf_int_back(dbuf) == vec.back(), "test_push(): equal while pushing", this);
}
ptrdiff_t i = 0;
GUF_CNT_FOREACH(dbuf, dbuf_int, it) {
GUF_TEST_CHECK(*it.ptr == vec.at(i++), "test_push(): equal forward iteration", this);
}
GUF_TEST_CHECK(i == dbuf->size, "test_push(): size after iterating", this);
i = dbuf->size - 1;
GUF_CNT_FOREACH_REVERSE(dbuf, dbuf_int, rit) {
GUF_TEST_CHECK(*rit.ptr == vec.at(i--), "test_push(): equal reverse iteration", this);
}
GUF_TEST_CHECK(i == -1, "test_push(): size after reverse iteration", this);
}
void test_insert_remove(int n)
{
dbuf_int dbuf = {};
dbuf_int_init(&dbuf, 0, &guf_allocator_libc);
std::vector<int> vec = dbuf_to_vec(&dbuf);
guf_err err = GUF_ERR_NONE;
dbuf_int_try_erase(&dbuf, 0, &err);
GUF_TEST_CHECK(err == GUF_ERR_IDX_RANGE, "test_remove(): erase empty test 1", this);
err = GUF_ERR_NONE;
dbuf_int_try_erase(&dbuf, 12, &err);
GUF_TEST_CHECK(err == GUF_ERR_IDX_RANGE, "test_remove(): erase empty test 2", this);
err = GUF_ERR_NONE;
dbuf_int_try_front(&dbuf, &err);
GUF_TEST_CHECK(err == GUF_ERR_IDX_RANGE, "test_remove(): erase empty test 3", this);
err = GUF_ERR_NONE;
dbuf_int_try_back(&dbuf, &err);
GUF_TEST_CHECK(err == GUF_ERR_IDX_RANGE, "test_remove(): erase empty back 4", this);
err = GUF_ERR_NONE;
dbuf_int_try_at(&dbuf, 0, &err);
GUF_TEST_CHECK(err == GUF_ERR_IDX_RANGE, "test_remove(): erase empty back 5", this);
for (int i = 0; i < n; ++i) {
dbuf_int_insert_val(&dbuf, i, i);
dbuf_int_insert_val(&dbuf, i * 2, 0);
dbuf_int_insert_val(&dbuf, i * 4, dbuf.size);
vec.insert(vec.begin() + i, i);
vec.insert(vec.begin(), i * 2);
vec.insert(vec.end(), i * 4);
}
GUF_TEST_CHECK(std::ssize(vec) == dbuf.size, "test_remove(): size after insert", this);
// Iterate
dbuf_int_iter it_dbuf = dbuf_int_begin(&dbuf);
std::vector<int>::const_iterator it_vec = vec.begin();
while (!dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec != vec.end()) {
GUF_TEST_CHECK(*it_dbuf.ptr == *it_vec, "test_remove(): iterate equal", this);
it_dbuf = dbuf_int_iter_next(&dbuf, it_dbuf, 1);
std::advance(it_vec, 1);
}
GUF_TEST_CHECK(dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec == vec.end(), "test_remove(): iterate equal 2", this);
// Step iterate.
it_dbuf = dbuf_int_begin(&dbuf);
it_vec = vec.begin();
while (!dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec != vec.end()) {
GUF_TEST_CHECK(*it_dbuf.ptr == *it_vec, "test_remove(): iterate step equal", this);
it_dbuf = dbuf_int_iter_next(&dbuf, it_dbuf, 7);
if (dbuf_int_iter_is_end(&dbuf, it_dbuf)) {
it_vec = vec.end();
} else {
std::advance(it_vec, 7);
}
}
GUF_TEST_CHECK(dbuf_int_iter_is_end(&dbuf, it_dbuf) && it_vec == vec.end(), "test_remove(): iterat step equal 2", this);
// Reverse iterate.
dbuf_int_iter rit_dbuf = dbuf_int_rbegin(&dbuf);
std::vector<int>::const_reverse_iterator rit_vec = vec.crbegin();
while (!dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec != vec.crend()) {
GUF_TEST_CHECK(*rit_dbuf.ptr == *rit_vec, "test_remove(): reverse iterate equal", this);
rit_dbuf = dbuf_int_iter_next(&dbuf, rit_dbuf, 1);
std::advance(rit_vec, 1);
}
GUF_TEST_CHECK(dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec == vec.rend(), "test_remove(): reverse iterate equal 2", this);
// Reverse iterate step.
rit_dbuf = dbuf_int_rbegin(&dbuf);
rit_vec = vec.crbegin();
while (!dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec != vec.crend()) {
GUF_TEST_CHECK(*rit_dbuf.ptr == *rit_vec, "test_remove(): reverse iterate step equal", this);
rit_dbuf = dbuf_int_iter_next(&dbuf, rit_dbuf, 4);
if (dbuf_int_iter_is_end(&dbuf, rit_dbuf)) {
rit_vec = vec.rend();
} else {
std::advance(rit_vec, 4);
}
}
GUF_TEST_CHECK(dbuf_int_iter_is_end(&dbuf, rit_dbuf) && rit_vec == vec.rend(), "test_remove(): reverse iterate step equal 2", this);
GUF_TEST_CHECK(dbuf.size == std::ssize(vec), "test_remove(): size equal", this);
for (ptrdiff_t i = 0; i < dbuf.size; i += 8) {
dbuf_int_erase(&dbuf, i);
dbuf_int_erase(&dbuf, 0);
dbuf_int_pop(&dbuf);
vec.erase(vec.begin() + i);
vec.erase(vec.begin() + 0);
vec.pop_back();
}
GUF_TEST_CHECK(dbuf.size == std::ssize(vec), "test_remove(): size equal after remove", this);
for (ptrdiff_t i = 0; i < dbuf.size; i += 8) {
GUF_TEST_CHECK(*dbuf_int_at(&dbuf, i) == vec.at(i), "test_remove(): equal after remove", this);
}
const ptrdiff_t size = dbuf.size;
for (ptrdiff_t i = 0; i < size; ++i) {
int a = dbuf_int_pop_cpy(&dbuf, GUF_CPY_VALUE);
int b = vec.back();
GUF_TEST_CHECK(a == b, "test_remove(): equal pop", this);
vec.pop_back();
}
GUF_TEST_CHECK(dbuf.size == 0 && vec.size() == 0, "test_remove(): equal after removing all", this);
dbuf_int_free(&dbuf, NULL);
GUF_TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && !dbuf.data, "test_remove(): state after free", this);
}
public:
bool run() override
{
if (done) {
return passed;
}
auto t0 = std::chrono::high_resolution_clock::now();
dbuf_int dbuf {};
dbuf_int_init(&dbuf, 0, &guf_allocator_libc);
test_push(&dbuf, 256);
test_push(&dbuf, 128);
test_push(&dbuf, 17);
GUF_TEST_CHECK(dbuf.size == (256 + 128 + 17), "run(): size after push", this);
dbuf_int_free(&dbuf, NULL);
GUF_TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && dbuf.data == NULL, "run(): size after free", this);
dbuf_int_init(&dbuf, 24, &guf_allocator_libc);
GUF_TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 24 && dbuf.data, "run(): capacity after init with capacity", this);
test_push(&dbuf, 365);
test_push(&dbuf, 4);
test_push(&dbuf, 25);
test_push(&dbuf, 64);
GUF_TEST_CHECK(dbuf.size == (365 + 4 + 25 + 64), "run(): size after push", this);
dbuf_int_free(&dbuf, NULL);
GUF_TEST_CHECK(dbuf.size == 0 && dbuf.capacity == 0 && dbuf.data == NULL, "run(): size after free", this);
test_insert_remove(4);
test_insert_remove(5);
test_insert_remove(6);
test_insert_remove(7);
test_insert_remove(8);
test_insert_remove(9);
test_insert_remove(10);
test_insert_remove(11);
test_insert_remove(16);
test_insert_remove(17);
test_insert_remove(31);
test_insert_remove(32);
test_insert_remove(33);
test_insert_remove(64);
test_insert_remove(128);
test_insert_remove(400);
test_insert_remove(401);
test_insert_remove(512);
test_insert_remove(513);
test_insert_remove(601);
test_insert_remove(2048);
test_insert_remove(2049);
const auto delta_t = std::chrono::high_resolution_clock::now() - t0;
runtime_ms = std::chrono::duration_cast<decltype(runtime_ms)>(delta_t);
done = true;
passed = (num_failed_checks == 0);
return passed;
}
};
// TODO: use std::unordered_set maybe
std::unordered_map<std::string, std::unique_ptr<Test>> g_tests {};
void init_dbuf_tests()
{
if (std::unique_ptr<Test> test = std::make_unique<DbufIntTest>("DbufIntTest"); test.get() != NULL) {
g_tests.insert({test.get()->name, std::move(test)});
} else {
throw std::runtime_error("test is NULL");
}
}
int main()
{
init_dbuf_tests();
bool all_passed = true;
for (auto& [name, test] : g_tests) {
if (Test *tst = test.get(); test.get() != NULL) {
tst->run();
std::cout << "- " << *tst << "\n";
all_passed = all_passed && tst->passed;
} else {
throw std::runtime_error("tst is NULL");
}
}
return all_passed ? EXIT_SUCCESS : EXIT_FAILURE;
}

View File

@ -1,29 +0,0 @@
import textwrap
import dbuf_tests
def gen_test_struct(test_fn_name: str, name: str, expected_output: str) -> str:
return textwrap.dedent(f"""
(guf_test) {{
.test_fn = {test_fn_name}, .name = "{name}",
.expected_output = "{expected_output}",
.output = NULL,
.passed = false,
.runtime_ms = 0,
}}""")
def gen_res_str(buf):
res = ""
for elem in buf:
res += str(elem) + ","
res = res[:-1]
return res
if __name__ == "__main__":
test_array_definition = "static const guf_test dbuf_tests[] = {"
for test_fn in dbuf_tests.all_tests():
test_array_definition += textwrap.indent(test_fn() + ",", " ")
test_array_definition += "\n};"
print(test_array_definition)