add new feature for testing

This commit is contained in:
falsycat 2023-11-26 14:14:45 +09:00
parent 0bb51750ef
commit cdc22d158f
15 changed files with 373 additions and 5 deletions

View File

@ -16,9 +16,6 @@ include(tool/meta.cmake)
# ---- thirdparty import
add_subdirectory(thirdparty EXCLUDE_FROM_ALL)
# ---- generation
include(tool/meta.cmake)
# ---- common config
add_library(nf7config INTERFACE)
target_include_directories(nf7config
@ -42,6 +39,9 @@ target_compile_options(nf7config INTERFACE
>
)
# ---- test library
add_subdirectory(test EXCLUDE_FROM_ALL)
# ---- util library
add_subdirectory(util)

View File

@ -1,6 +1,7 @@
# ---- basic modules
set(MODS
sdl2
test
)
# ---- core library

14
core/test/CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
add_library(nf7core_test)
target_sources(nf7core_test
PRIVATE
mod.c
run.h
PUBLIC
mod.h
)
target_link_libraries(nf7core_test
PRIVATE
nf7if
nf7util
nf7test
)

51
core/test/mod.c Normal file
View File

@ -0,0 +1,51 @@
// No copyright
#include "core/test/mod.h"
#include <assert.h>
#include "nf7.h"
#include "util/log.h"
#include "util/malloc.h"
#include "core/test/run.h"
static void del_(struct nf7_mod*);
struct nf7_mod* nf7_core_test_new(struct nf7* nf7) {
assert(nullptr != nf7);
struct nf7_core_test* this = nf7_util_malloc_new(nf7->malloc, sizeof(*this));
if (nullptr == this) {
nf7_util_log_error("failed to allocate instance");
return nullptr;
}
*this = (struct nf7_core_test) {
.meta = &nf7_core_test,
.nf7 = nf7,
.malloc = nf7->malloc,
.uv = nf7->uv,
};
if (!run_trigger_setup_(this)) {
nf7_util_log_error("failed to setup runner");
return nullptr;
}
return (struct nf7_mod*) this;
}
static void del_(struct nf7_mod* mod) {
struct nf7_core_test* this = (struct nf7_core_test*) mod;
nf7_util_malloc_del(this->malloc, this);
}
const struct nf7_mod_meta nf7_core_test = {
.name = (const uint8_t*) "nf7core_test",
.desc = (const uint8_t*) "executes tests after the initialization",
.ver = 0,
.delete = del_,
};

22
core/test/mod.h Normal file
View File

@ -0,0 +1,22 @@
// No copyright
#pragma once
#include <uv.h>
#include "test/common.h"
struct nf7_core_test;
struct nf7_core_test_run;
struct nf7_core_test {
const struct nf7_mod_meta* meta;
struct nf7* nf7;
struct nf7_util_malloc* malloc;
uv_loop_t* uv;
struct nf7_core_test_run* run;
};
extern const struct nf7_mod_meta nf7_core_test;

143
core/test/run.h Normal file
View File

@ -0,0 +1,143 @@
// No copyright
#pragma once
#include <assert.h>
#include <uv.h>
#include "test/run.h"
#include "util/log.h"
#include "util/malloc.h"
#include "core/test/mod.h"
static bool run_trigger_setup_(struct nf7_core_test*);
static void run_cancel_(struct nf7_core_test_run*);
static void run_cancel_close_(uv_handle_t*);
static void run_trigger_(uv_idle_t*);
static void run_single_test_(struct nf7_test*, const char*, nf7_test_func);
static void run_expect_(struct nf7_test*, bool, const char*);
static void run_finalize_(struct nf7_test*);
struct nf7_core_test_run {
struct nf7_core_test* mod;
struct nf7_util_malloc* malloc;
uv_loop_t* uv;
uv_idle_t idle;
struct nf7_test test;
uint64_t run_tests;
uint64_t succeeded_tests;
const char* running_test_name;
};
static bool run_trigger_setup_(struct nf7_core_test* mod) {
assert(nullptr != mod);
struct nf7_core_test_run* this = nf7_util_malloc_new(mod->malloc, sizeof(*this));
if (nullptr == this) {
nf7_util_log_error("failed to allocate an test context");
return false;
}
*this = (struct nf7_core_test_run) {
.mod = mod,
.malloc = mod->malloc,
.uv = mod->uv,
.test = {
.nf7 = mod->nf7,
.data = this,
.run = run_single_test_,
.expect = run_expect_,
.finalize = run_finalize_,
},
};
nf7_test_ref(&this->test);
nf7_util_log_uv_assert(uv_idle_init(this->uv, &this->idle));
this->idle.data = this;
nf7_util_log_uv_assert(uv_idle_start(&this->idle, run_trigger_));
mod->run = this;
return true;
}
static void run_cancel_(struct nf7_core_test_run* this) {
if (nullptr != this) {
if (nullptr != this->mod) {
this->mod->run = nullptr;
}
uv_idle_stop(&this->idle);
uv_close((uv_handle_t*) &this->idle, run_cancel_close_);
}
}
static void run_cancel_close_(uv_handle_t* handle) {
struct nf7_core_test_run* this = handle->data;
nf7_test_unref(&this->test);
}
static void run_trigger_(uv_idle_t* idle) {
struct nf7_core_test_run* this = idle->data;
nf7_util_log_info("triggering tests...");
nf7_test_run(&this->test);
nf7_util_log_info("all tests are triggered");
run_cancel_(this);
}
static void run_single_test_(
struct nf7_test* test, const char* name, nf7_test_func func) {
assert(nullptr != test);
assert(nullptr != name);
assert(nullptr != func);
struct nf7_core_test_run* this = test->data;
this->running_test_name = name;
const bool result = func(&this->test);
this->running_test_name = nullptr;
++this->run_tests;
if (result) {
++this->succeeded_tests;
nf7_util_log_info("test succeeded: %s", name);
} else {
nf7_util_log_error("TEST FAILED: %s", name);
}
}
static void run_expect_(struct nf7_test* test, bool val, const char* expr) {
assert(nullptr != test);
assert(nullptr != expr);
if (val) {
nf7_util_log_debug("expectation is met: %s", expr);
} else {
nf7_util_log_error("expectation is NOT met: %s", expr);
}
}
static void run_finalize_(struct nf7_test* test) {
assert(nullptr != test);
struct nf7_core_test_run* this = test->data;
if (0 < this->run_tests) {
if (this->succeeded_tests == this->run_tests) {
nf7_util_log_info(
"all tests (%" PRIu64 ") has passed! :)",
this->run_tests);
} else {
nf7_util_log_warn(
"%" PRIu64 "/%" PRIu64 " tests have FAILED! X(",
this->run_tests - this->succeeded_tests,
this->run_tests);
}
}
nf7_util_malloc_del(this->malloc, this);
}

32
test/CMakeLists.txt Normal file
View File

@ -0,0 +1,32 @@
# ---- test interfae library
add_library(nf7test_if INTERFACE)
target_link_libraries(nf7test_if INTERFACE nf7config)
target_sources(nf7test_if INTERFACE common.h)
# ---- test library
add_library(nf7test)
target_link_libraries(nf7test
PUBLIC
nf7config
nf7test_if
)
target_meta_source(nf7test
PRIVATE run.c.sh
ARGS $<TARGET_PROPERTY:nf7test_src,SOURCES>
DEPENDS $<TARGET_PROPERTY:nf7test_src,SOURCES>
)
# ---- a custom target to keep source files of tests
add_custom_target(nf7test_src)
function(target_tests target)
if (NOT TARGET "${target}")
message(FATAL_ERROR "unknown target: ${target}")
endif()
target_sources(${target} PRIVATE ${ARGN})
target_link_libraries(${target} PRIVATE nf7test_if)
target_sources(nf7test_src PRIVATE ${ARGN})
target_link_libraries(nf7test PRIVATE ${target})
endfunction()

40
test/common.h Normal file
View File

@ -0,0 +1,40 @@
// No copyright
#pragma once
#include <stdint.h>
#define NF7_TEST(name) bool name([[maybe_unused]] struct nf7_test*)
struct nf7;
struct nf7_test;
typedef bool (*nf7_test_func)(struct nf7_test*);
struct nf7_test {
struct nf7* nf7;
void* data;
uint64_t refcnt;
void (*run)(struct nf7_test*, const char* name, nf7_test_func);
void (*expect)(struct nf7_test*, bool val, const char* expr);
void (*finalize)(struct nf7_test*);
};
static inline void nf7_test_ref(struct nf7_test* test) {
++test->refcnt;
}
static inline void nf7_test_unref(struct nf7_test* test) {
if (--test->refcnt == 0) {
test->finalize(test);
}
}
#define nf7_test_expect(test, expr) nf7_test_expect_(test, (expr), #expr)
static inline bool nf7_test_expect_(
struct nf7_test* test, bool val, const char* expr) {
test->expect(test, val, expr);
return val;
}

17
test/run.c.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
tests=$(cat $@ | sed -e '/^NF7_TEST(/!d; s|^NF7_TEST(\([^)]*\)).*$|\1|' | xargs echo)
echo "#include \"test/common.h\""
echo "#include \"test/run.h\""
echo
echo
echo "bool nf7_test_run(struct nf7_test* test) {"
echo " // $tests"
for test in $tests; do
echo " extern bool $test(struct nf7_test*);"
echo " test->run(test, \"$test\", $test);"
done
echo " return true;"
echo "}"

7
test/run.h Normal file
View File

@ -0,0 +1,7 @@
// No copyright
#pragma once
#include "test/common.h"
bool nf7_test_run(struct nf7_test*);

View File

@ -10,7 +10,7 @@ function(target_meta_source args_target args_scope args_src)
${ARGN}
)
string(REGEX REPLACE "^${PROJECT_SOURCE_DIR}/" "" CURRENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
file(RELATIVE_PATH CURRENT_DIR "${PROJECT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
set(GENERATED_DIR "${PROJECT_BINARY_DIR}/generated")
set(GENERATED_CURRENT_DIR "${GENERATED_DIR}/${CURRENT_DIR}")

9
tool/testset.c.sh Normal file
View File

@ -0,0 +1,9 @@
#!/bin/bash
cat $@ | sed -e ''
echo "bool nf7_test_run(void) {"
echo " // $@"
echo " return true;"
echo "}"

View File

@ -8,4 +8,11 @@ target_sources(nf7util
malloc.h
signal.h
)
target_link_libraries(nf7util PRIVATE nf7config)
target_tests(nf7util
array.test.c
)
target_link_libraries(nf7util
PRIVATE
nf7config
uv
)

12
util/array.test.c Normal file
View File

@ -0,0 +1,12 @@
// No copyright
#include "util/array.h"
#include "test/common.h"
NF7_TEST(nf7_util_array_test) {
return true;
}
NF7_TEST(nf7_util_array_test2) {
return true;
}

View File

@ -1,10 +1,12 @@
// No copyright
#pragma once
#include <assert.h>
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#define NF7_UTIL_LOG_CURRENT_FILE \
((const char*) __FILE__ + NF7_PROJECT_DIR_LEN)
@ -27,6 +29,8 @@
#define nf7_util_log_error(...) \
nf7_util_log_sugar("ERR", __VA_ARGS__)
// ---- libuv utils
#define nf7_util_log_uv(ret) \
nf7_util_log_uv_((ret), NF7_UTIL_LOG_CURRENT_FILE, __LINE__, __func__)
static inline int nf7_util_log_uv_(
@ -36,3 +40,12 @@ static inline int nf7_util_log_uv_(
}
return ret;
}
#define nf7_util_log_uv_assert(ret) \
nf7_util_log_uv_((ret), NF7_UTIL_LOG_CURRENT_FILE, __LINE__, __func__)
static inline int nf7_util_log_uv_assert_(
int ret, const char* file, uint64_t line, const char* func) {
nf7_util_log_uv_(ret, file, line, func);
assert(0 == ret);
return ret;
}