Implement unit testing (#30)
- Create `test` and `tactility-core-tests` subprojects - Created tests for `thread.c` - Fixed issue with thread cleanup (see what I did there? :P) - Removed functions from `thread.h` that did not exist anymore - Updated `ideas.md`
This commit is contained in:
committed by
GitHub
parent
50e7fb92c8
commit
47377439dd
@@ -0,0 +1,9 @@
|
||||
project(tests)
|
||||
|
||||
set(DOCTESTINC ${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(tactility-core)
|
||||
|
||||
add_custom_target(build-tests)
|
||||
add_dependencies(build-tests tactility-core-tests)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
project(tactility-core-tests)
|
||||
|
||||
enable_language(C CXX ASM)
|
||||
|
||||
set(CMAKE_CXX_COMPILER g++)
|
||||
|
||||
file(GLOB_RECURSE TEST_SOURCES ${PROJECT_SOURCE_DIR}/*.cpp)
|
||||
add_executable(tactility-core-tests EXCLUDE_FROM_ALL ${TEST_SOURCES})
|
||||
|
||||
target_include_directories(tactility-core-tests PRIVATE
|
||||
${DOCTESTINC}
|
||||
)
|
||||
|
||||
add_test(NAME tactility-core-tests
|
||||
COMMAND tactility-core-tests
|
||||
)
|
||||
|
||||
target_link_libraries(tactility-core-tests
|
||||
PUBLIC tactility-core
|
||||
freertos-kernel
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT
|
||||
#include "doctest.h"
|
||||
#include <cassert>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
typedef struct {
|
||||
int argc;
|
||||
char** argv;
|
||||
int result;
|
||||
} TestTaskData;
|
||||
|
||||
void test_task(void* parameter) {
|
||||
auto* data = (TestTaskData*)parameter;
|
||||
|
||||
doctest::Context context;
|
||||
|
||||
context.applyCommandLine(data->argc, data->argv);
|
||||
|
||||
// overrides
|
||||
context.setOption("no-breaks", true); // don't break in the debugger when assertions fail
|
||||
|
||||
data->result = context.run();
|
||||
|
||||
if (context.shouldExit()) { // important - query flags (and --exit) rely on the user doing this
|
||||
vTaskEndScheduler();
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
TestTaskData data = {
|
||||
.argc = argc,
|
||||
.argv = argv,
|
||||
.result = 0
|
||||
};
|
||||
|
||||
BaseType_t task_result = xTaskCreate(
|
||||
test_task,
|
||||
"test_task",
|
||||
8192,
|
||||
&data,
|
||||
1,
|
||||
NULL
|
||||
);
|
||||
assert(task_result == pdPASS);
|
||||
|
||||
vTaskStartScheduler();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// Required for FreeRTOS
|
||||
void vAssertCalled(unsigned long line, const char* const file) {
|
||||
__assert_fail("assert failed", file, line, "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#include "doctest.h"
|
||||
#include "thread.h"
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
static int interruptable_thread(TT_UNUSED void* parameter) {
|
||||
bool* interrupted = (bool*)parameter;
|
||||
while (!*interrupted) {
|
||||
vTaskDelay(5);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool immedate_return_thread_called = false;
|
||||
static int immediate_return_thread(TT_UNUSED void* parameter) {
|
||||
immedate_return_thread_called = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thread_with_return_code(TT_UNUSED void* parameter) {
|
||||
int* code = (int*)parameter;
|
||||
return *code;
|
||||
}
|
||||
|
||||
TEST_CASE("when a thread is started then its callback should be called") {
|
||||
Thread* thread = tt_thread_alloc_ex(
|
||||
"immediate return task",
|
||||
4096,
|
||||
&immediate_return_thread,
|
||||
NULL
|
||||
);
|
||||
CHECK(!immedate_return_thread_called);
|
||||
tt_thread_start(thread);
|
||||
tt_thread_join(thread);
|
||||
tt_thread_free(thread);
|
||||
CHECK(immedate_return_thread_called);
|
||||
}
|
||||
|
||||
TEST_CASE("a thread can be started and stopped") {
|
||||
bool interrupted = false;
|
||||
Thread* thread = tt_thread_alloc_ex(
|
||||
"interruptable thread",
|
||||
4096,
|
||||
&interruptable_thread,
|
||||
&interrupted
|
||||
);
|
||||
CHECK(thread);
|
||||
tt_thread_start(thread);
|
||||
interrupted = true;
|
||||
tt_thread_join(thread);
|
||||
tt_thread_free(thread);
|
||||
}
|
||||
|
||||
TEST_CASE("thread id should only be set at when thread is started") {
|
||||
bool interrupted = false;
|
||||
Thread* thread = tt_thread_alloc_ex(
|
||||
"interruptable thread",
|
||||
4096,
|
||||
&interruptable_thread,
|
||||
&interrupted
|
||||
);
|
||||
CHECK(tt_thread_get_id(thread) == NULL);
|
||||
tt_thread_start(thread);
|
||||
CHECK(tt_thread_get_id(thread) != NULL);
|
||||
interrupted = true;
|
||||
tt_thread_join(thread);
|
||||
CHECK(tt_thread_get_id(thread) == NULL);
|
||||
tt_thread_free(thread);
|
||||
}
|
||||
|
||||
TEST_CASE("thread state should be correct") {
|
||||
bool interrupted = false;
|
||||
Thread* thread = tt_thread_alloc_ex(
|
||||
"interruptable thread",
|
||||
4096,
|
||||
&interruptable_thread,
|
||||
&interrupted
|
||||
);
|
||||
CHECK_EQ(tt_thread_get_state(thread), ThreadStateStopped);
|
||||
tt_thread_start(thread);
|
||||
ThreadState state = tt_thread_get_state(thread);
|
||||
CHECK((state == ThreadStateStarting || state == ThreadStateRunning));
|
||||
interrupted = true;
|
||||
tt_thread_join(thread);
|
||||
CHECK_EQ(tt_thread_get_state(thread), ThreadStateStopped);
|
||||
tt_thread_free(thread);
|
||||
}
|
||||
|
||||
TEST_CASE("thread id should only be set at when thread is started") {
|
||||
int code = 123;
|
||||
Thread* thread = tt_thread_alloc_ex(
|
||||
"return code",
|
||||
4096,
|
||||
&thread_with_return_code,
|
||||
&code
|
||||
);
|
||||
tt_thread_start(thread);
|
||||
tt_thread_join(thread);
|
||||
CHECK_EQ(tt_thread_get_return_code(thread), code);
|
||||
tt_thread_free(thread);
|
||||
}
|
||||
Reference in New Issue
Block a user