committed by
GitHub
parent
6d80144e12
commit
85e26636a3
@@ -0,0 +1,9 @@
|
||||
project(tests)
|
||||
|
||||
set(DOCTESTINC ${PROJECT_SOURCE_DIR}/Include)
|
||||
|
||||
enable_testing()
|
||||
add_subdirectory(TactilityCore)
|
||||
|
||||
add_custom_target(build-tests)
|
||||
add_dependencies(build-tests TactilityCoreTests)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,49 @@
|
||||
#include "doctest.h"
|
||||
#include "Bundle.h"
|
||||
|
||||
using namespace tt;
|
||||
|
||||
TEST_CASE("boolean can be stored and retrieved") {
|
||||
Bundle bundle;
|
||||
bundle.putBool("key", true);
|
||||
CHECK(bundle.hasBool("key"));
|
||||
CHECK(bundle.getBool("key"));
|
||||
bool opt_result = false;
|
||||
CHECK(bundle.optBool("key", opt_result));
|
||||
CHECK_EQ(opt_result, true);
|
||||
}
|
||||
|
||||
TEST_CASE("int32 can be stored and retrieved") {
|
||||
Bundle bundle;
|
||||
bundle.putInt32("key", true);
|
||||
CHECK(bundle.hasInt32("key"));
|
||||
CHECK(bundle.getInt32("key"));
|
||||
int32_t opt_result = false;
|
||||
CHECK(bundle.optInt32("key", opt_result));
|
||||
CHECK_EQ(opt_result, true);
|
||||
}
|
||||
|
||||
TEST_CASE("string can be stored and retrieved") {
|
||||
Bundle bundle;
|
||||
bundle.putString("key", "test");
|
||||
CHECK(bundle.hasString("key"));
|
||||
CHECK_EQ(bundle.getString("key"), "test");
|
||||
std::string opt_result;
|
||||
CHECK(bundle.optString("key", opt_result));
|
||||
CHECK_EQ(opt_result, "test");
|
||||
}
|
||||
|
||||
TEST_CASE("bundle copy makes an actual copy") {
|
||||
auto* original_ptr = new Bundle();
|
||||
Bundle& original = *original_ptr;
|
||||
original.putBool("bool", true);
|
||||
original.putInt32("int32", 123);
|
||||
original.putString("string", "text");
|
||||
|
||||
Bundle copy = original;
|
||||
delete original_ptr;
|
||||
|
||||
CHECK_EQ(copy.getBool("bool"), true);
|
||||
CHECK_EQ(copy.getInt32("int32"), 123);
|
||||
CHECK_EQ(copy.getString("string"), "text");
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
project(TactilityCoreTests)
|
||||
|
||||
enable_language(C CXX ASM)
|
||||
|
||||
set(CMAKE_CXX_COMPILER g++)
|
||||
|
||||
file(GLOB_RECURSE TEST_SOURCES ${PROJECT_SOURCE_DIR}/*.cpp)
|
||||
add_executable(TactilityCoreTests EXCLUDE_FROM_ALL ${TEST_SOURCES})
|
||||
|
||||
target_include_directories(TactilityCoreTests PRIVATE
|
||||
${DOCTESTINC}
|
||||
)
|
||||
|
||||
add_test(NAME TactilityCoreTests
|
||||
COMMAND TactilityCoreTests
|
||||
)
|
||||
|
||||
target_link_libraries(TactilityCoreTests
|
||||
PUBLIC TactilityCore
|
||||
freertos_kernel
|
||||
)
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "doctest.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "Dispatcher.h"
|
||||
|
||||
using namespace tt;
|
||||
|
||||
void increment_callback(void* context) {
|
||||
auto* counter = (uint32_t*)context;
|
||||
(*counter)++;
|
||||
}
|
||||
|
||||
TEST_CASE("dispatcher should not call callback if consume isn't called") {
|
||||
Dispatcher dispatcher;
|
||||
|
||||
uint32_t counter = 0;
|
||||
dispatcher.dispatch(&increment_callback, &counter);
|
||||
delay_tick(10);
|
||||
|
||||
CHECK_EQ(counter, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("dispatcher should be able to dealloc when message is not consumed") {
|
||||
auto* dispatcher = new Dispatcher();
|
||||
uint32_t counter = 0;
|
||||
dispatcher->dispatch(increment_callback, &counter);
|
||||
delete dispatcher;
|
||||
}
|
||||
|
||||
TEST_CASE("dispatcher should call callback when consume is called") {
|
||||
Dispatcher dispatcher;
|
||||
|
||||
uint32_t counter = 0;
|
||||
dispatcher.dispatch(increment_callback, &counter);
|
||||
dispatcher.consume(100);
|
||||
CHECK_EQ(counter, 1);
|
||||
}
|
||||
@@ -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(nullptr);
|
||||
}
|
||||
|
||||
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,
|
||||
nullptr
|
||||
);
|
||||
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,86 @@
|
||||
#include "doctest.h"
|
||||
#include "MessageQueue.h"
|
||||
|
||||
using namespace tt;
|
||||
|
||||
TEST_CASE("message queue capacity should be correct") {
|
||||
MessageQueue queue(10, 1);
|
||||
|
||||
uint32_t capacity = queue.getCapacity();
|
||||
CHECK_EQ(capacity, 10);
|
||||
}
|
||||
|
||||
TEST_CASE("message queue initial count should be 0") {
|
||||
MessageQueue queue(10, 1);
|
||||
|
||||
uint32_t count = queue.getCount();
|
||||
CHECK_EQ(count, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("message queue message size should be correct") {
|
||||
MessageQueue queue(1, 123);
|
||||
|
||||
uint32_t message_size = queue.getMessageSize();
|
||||
CHECK_EQ(message_size, 123);
|
||||
}
|
||||
|
||||
TEST_CASE("message queue count should increase when message is added") {
|
||||
MessageQueue queue(10, sizeof(uint32_t));
|
||||
|
||||
uint32_t message = 123;
|
||||
queue.put(&message, 100);
|
||||
uint32_t count = queue.getCount();
|
||||
CHECK_EQ(count, 1);
|
||||
}
|
||||
|
||||
TEST_CASE("message queue count should be 0 when message is added and queue is reset") {
|
||||
MessageQueue queue(10, sizeof(uint32_t));
|
||||
|
||||
uint32_t message = 123;
|
||||
|
||||
queue.put(&message, 100);
|
||||
queue.reset();
|
||||
uint32_t count = queue.getCount();
|
||||
CHECK_EQ(count, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("message queue consumption should work") {
|
||||
MessageQueue queue(10, sizeof(uint32_t));
|
||||
|
||||
uint32_t out_message = 123;
|
||||
queue.put(&out_message, 100);
|
||||
|
||||
uint32_t in_message = 0;
|
||||
queue.get(&in_message, 100);
|
||||
CHECK_EQ(in_message, 123);
|
||||
}
|
||||
|
||||
TEST_CASE("message queue count should decrease when message is consumed") {
|
||||
MessageQueue queue(10, sizeof(uint32_t));
|
||||
|
||||
uint32_t out_message = 123;
|
||||
queue.put(&out_message, 100);
|
||||
|
||||
uint32_t in_message = 0;
|
||||
queue.get(&in_message, 100);
|
||||
uint32_t count = queue.getCount();
|
||||
CHECK_EQ(count, 0);
|
||||
}
|
||||
|
||||
TEST_CASE("message queue should make copy of data") {
|
||||
// Given a number that we can later delete
|
||||
MessageQueue queue(1, sizeof(int32_t));
|
||||
const int32_t test_value = 123;
|
||||
auto* number = new int32_t();
|
||||
*number = test_value;
|
||||
|
||||
// When we put the number in the queue and then delete it
|
||||
queue.put(number, 100);
|
||||
delete number;
|
||||
|
||||
// We want to verify that the value was copied into the queue and retrieved properly
|
||||
int32_t queue_number = 0;
|
||||
CHECK_EQ(queue.get(&queue_number, 100), TtStatusOk);
|
||||
CHECK_EQ(queue_number, test_value);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "doctest.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "Mutex.h"
|
||||
|
||||
using namespace tt;
|
||||
|
||||
static int thread_with_mutex_parameter(void* parameter) {
|
||||
auto* mutex = (Mutex*)parameter;
|
||||
tt_mutex_acquire(mutex, TtWaitForever);
|
||||
return 0;
|
||||
}
|
||||
|
||||
TEST_CASE("a mutex can block a thread") {
|
||||
auto* mutex = tt_mutex_alloc(MutexTypeNormal);
|
||||
tt_mutex_acquire(mutex, TtWaitForever);
|
||||
|
||||
Thread* thread = thread_alloc_ex(
|
||||
"thread",
|
||||
1024,
|
||||
&thread_with_mutex_parameter,
|
||||
mutex
|
||||
);
|
||||
thread_start(thread);
|
||||
|
||||
delay_ms(5);
|
||||
CHECK_EQ(thread_get_state(thread), ThreadStateRunning);
|
||||
|
||||
tt_mutex_release(mutex);
|
||||
|
||||
delay_ms(5);
|
||||
CHECK_EQ(thread_get_state(thread), ThreadStateStopped);
|
||||
|
||||
thread_join(thread);
|
||||
thread_free(thread);
|
||||
tt_mutex_free(mutex);
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "doctest.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "Thread.h"
|
||||
|
||||
using namespace tt;
|
||||
|
||||
static int interruptable_thread(void* parameter) {
|
||||
bool* interrupted = (bool*)parameter;
|
||||
while (!*interrupted) {
|
||||
delay_ms(5);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int immediate_return_thread(void* parameter) {
|
||||
bool* has_called = (bool*)parameter;
|
||||
*has_called = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int thread_with_return_code(void* parameter) {
|
||||
int* code = (int*)parameter;
|
||||
return *code;
|
||||
}
|
||||
|
||||
TEST_CASE("when a thread is started then its callback should be called") {
|
||||
bool has_called = false;
|
||||
auto* thread = thread_alloc_ex(
|
||||
"immediate return task",
|
||||
4096,
|
||||
&immediate_return_thread,
|
||||
&has_called
|
||||
);
|
||||
CHECK(!has_called);
|
||||
thread_start(thread);
|
||||
thread_join(thread);
|
||||
thread_free(thread);
|
||||
CHECK(has_called);
|
||||
}
|
||||
|
||||
TEST_CASE("a thread can be started and stopped") {
|
||||
bool interrupted = false;
|
||||
auto* thread = thread_alloc_ex(
|
||||
"interruptable thread",
|
||||
4096,
|
||||
&interruptable_thread,
|
||||
&interrupted
|
||||
);
|
||||
CHECK(thread);
|
||||
thread_start(thread);
|
||||
interrupted = true;
|
||||
thread_join(thread);
|
||||
thread_free(thread);
|
||||
}
|
||||
|
||||
TEST_CASE("thread id should only be set at when thread is started") {
|
||||
bool interrupted = false;
|
||||
auto* thread = thread_alloc_ex(
|
||||
"interruptable thread",
|
||||
4096,
|
||||
&interruptable_thread,
|
||||
&interrupted
|
||||
);
|
||||
CHECK_EQ(thread_get_id(thread), nullptr);
|
||||
thread_start(thread);
|
||||
CHECK_NE(thread_get_id(thread), nullptr);
|
||||
interrupted = true;
|
||||
thread_join(thread);
|
||||
CHECK_EQ(thread_get_id(thread), nullptr);
|
||||
thread_free(thread);
|
||||
}
|
||||
|
||||
TEST_CASE("thread state should be correct") {
|
||||
bool interrupted = false;
|
||||
auto* thread = thread_alloc_ex(
|
||||
"interruptable thread",
|
||||
4096,
|
||||
&interruptable_thread,
|
||||
&interrupted
|
||||
);
|
||||
CHECK_EQ(thread_get_state(thread), ThreadStateStopped);
|
||||
thread_start(thread);
|
||||
ThreadState state = thread_get_state(thread);
|
||||
CHECK((state == ThreadStateStarting || state == ThreadStateRunning));
|
||||
interrupted = true;
|
||||
thread_join(thread);
|
||||
CHECK_EQ(thread_get_state(thread), ThreadStateStopped);
|
||||
thread_free(thread);
|
||||
}
|
||||
|
||||
TEST_CASE("thread id should only be set at when thread is started") {
|
||||
int code = 123;
|
||||
auto* thread = thread_alloc_ex(
|
||||
"return code",
|
||||
4096,
|
||||
&thread_with_return_code,
|
||||
&code
|
||||
);
|
||||
thread_start(thread);
|
||||
thread_join(thread);
|
||||
CHECK_EQ(thread_get_return_code(thread), code);
|
||||
thread_free(thread);
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#include "doctest.h"
|
||||
#include "TactilityCore.h"
|
||||
#include "Timer.h"
|
||||
|
||||
using namespace tt;
|
||||
|
||||
void* timer_callback_context = NULL;
|
||||
static void timer_callback_with_context(void* context) {
|
||||
timer_callback_context = context;
|
||||
}
|
||||
|
||||
static void timer_callback_with_counter(void* context) {
|
||||
int* int_ptr = (int*)context;
|
||||
(*int_ptr)++;
|
||||
}
|
||||
|
||||
TEST_CASE("a timer passes the context correctly") {
|
||||
int foo = 1;
|
||||
auto* timer = timer_alloc(&timer_callback_with_context, TimerTypeOnce, &foo);
|
||||
timer_start(timer, 1);
|
||||
delay_tick(10);
|
||||
timer_stop(timer);
|
||||
timer_free(timer);
|
||||
|
||||
CHECK_EQ(timer_callback_context, &foo);
|
||||
}
|
||||
|
||||
TEST_CASE("TimerTypePeriodic timers can be stopped and restarted") {
|
||||
int counter = 0;
|
||||
auto* timer = timer_alloc(&timer_callback_with_counter, TimerTypePeriodic, &counter);
|
||||
timer_start(timer, 1);
|
||||
delay_tick(10);
|
||||
timer_stop(timer);
|
||||
delay_tick(10);
|
||||
timer_stop(timer);
|
||||
timer_free(timer);
|
||||
|
||||
CHECK_GE(counter, 2);
|
||||
}
|
||||
|
||||
TEST_CASE("TimerTypePeriodic calls the callback periodically") {
|
||||
int counter = 0;
|
||||
int ticks_to_run = 10;
|
||||
auto* timer = timer_alloc(&timer_callback_with_counter, TimerTypePeriodic, &counter);
|
||||
timer_start(timer, 1);
|
||||
delay_tick(ticks_to_run);
|
||||
timer_stop(timer);
|
||||
timer_free(timer);
|
||||
|
||||
CHECK_EQ(counter, ticks_to_run);
|
||||
}
|
||||
|
||||
TEST_CASE("restarting TimerTypeOnce timers has no effect") {
|
||||
int counter = 0;
|
||||
auto* timer = timer_alloc(&timer_callback_with_counter, TimerTypeOnce, &counter);
|
||||
timer_start(timer, 1);
|
||||
delay_tick(10);
|
||||
timer_stop(timer);
|
||||
delay_tick(10);
|
||||
timer_stop(timer);
|
||||
timer_free(timer);
|
||||
|
||||
CHECK_EQ(counter, 1);
|
||||
}
|
||||
Reference in New Issue
Block a user