Create TactilityFreertos subproject (#440)

This commit is contained in:
Ken Van Hoeylandt
2026-01-03 00:19:40 +01:00
committed by GitHub
parent a4dc633063
commit 7283920def
154 changed files with 1926 additions and 2676 deletions
+1
View File
@@ -20,5 +20,6 @@ add_test(NAME TactilityCoreTests
target_link_libraries(TactilityCoreTests PUBLIC
TactilityCore
TactilityFreeRtos
freertos_kernel
)
-38
View File
@@ -1,38 +0,0 @@
#include "doctest.h"
#include <Tactility/TactilityCore.h>
#include <Tactility/Dispatcher.h>
using namespace tt;
TEST_CASE("dispatcher should not call callback if consume isn't called") {
int counter = 0;
Dispatcher dispatcher;
dispatcher.dispatch([&counter]() { counter++; });
kernel::delayTicks(10);
CHECK_EQ(counter, 0);
}
TEST_CASE("dispatcher should be able to dealloc when message is not consumed") {
auto* dispatcher = new Dispatcher();
auto context = std::make_shared<uint32_t>();
dispatcher->dispatch([]() { /* NO-OP */ });
delete dispatcher;
}
TEST_CASE("dispatcher should call callback when consume is called") {
int counter = 0;
Dispatcher dispatcher;
dispatcher.dispatch([&counter]() { counter++; });
dispatcher.consume(100);
CHECK_EQ(counter, 1);
}
TEST_CASE("message should be passed on correctly") {
Dispatcher dispatcher;
dispatcher.dispatch([]() { /* NO-OP */ });
dispatcher.consume(100);
}
@@ -1,29 +0,0 @@
#include "doctest.h"
#include <Tactility/TactilityCore.h>
#include <Tactility/DispatcherThread.h>
using namespace tt;
TEST_CASE("DispatcherThread state test") {
DispatcherThread thread("test");
CHECK_EQ(thread.isStarted(), false);
thread.start();
CHECK_EQ(thread.isStarted(), true);
thread.stop();
CHECK_EQ(thread.isStarted(), false);
}
TEST_CASE("DispatcherThread should consume jobs") {
DispatcherThread thread("test");
thread.start();
int counter = 0;
thread.dispatch([&counter]() { counter++; });
tt::kernel::delayTicks(10);
CHECK_EQ(counter, 1);
thread.stop();
}
-39
View File
@@ -1,39 +0,0 @@
#include "doctest.h"
#include <Tactility/Lock.h>
#include <Tactility/Mutex.h>
#include <Tactility/Semaphore.h>
using namespace tt;
TEST_CASE("withLock() locks correctly on Semaphore") {
auto semaphore = std::make_shared<Semaphore>(2U);
semaphore->withLock([semaphore](){
CHECK_EQ(semaphore->getAvailable(), 1);
});
}
TEST_CASE("withLock() unlocks correctly on Semaphore") {
auto semaphore = std::make_shared<Semaphore>(2U);
semaphore->withLock([=](){
// NO-OP
});
CHECK_EQ(semaphore->getAvailable(), 2);
}
TEST_CASE("withLock() locks correctly on Mutex") {
auto mutex = std::make_shared<Mutex>();
mutex->withLock([mutex](){
CHECK_EQ(mutex->lock(1), false);
});
}
TEST_CASE("withLock() unlocks correctly on Mutex") {
auto mutex = std::make_shared<Mutex>();
mutex->withLock([=](){
// NO-OP
});
CHECK_EQ(mutex->lock(1), true);
CHECK_EQ(mutex->unlock(), true);
}
+2
View File
@@ -48,6 +48,8 @@ int main(int argc, char** argv) {
assert(task_result == pdPASS);
vTaskStartScheduler();
return data.result;
}
extern "C" {
-86
View File
@@ -1,86 +0,0 @@
#include "doctest.h"
#include <Tactility/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), true);
CHECK_EQ(queue_number, test_value);
}
-49
View File
@@ -1,49 +0,0 @@
#include "doctest.h"
#include <Tactility/TactilityCore.h>
#include <Tactility/Mutex.h>
using namespace tt;
TEST_CASE("a mutex can block a thread") {
auto mutex = Mutex();
mutex.lock(portMAX_DELAY);
Thread thread = Thread(
"thread",
1024,
[&mutex]() {
mutex.lock(portMAX_DELAY);
return 0;
}
);
thread.start();
kernel::delayMillis(5);
CHECK_EQ(thread.getState(), Thread::State::Running);
mutex.unlock();
kernel::delayMillis(5);
CHECK_EQ(thread.getState(), Thread::State::Stopped);
thread.join();
}
TEST_CASE("a Mutex can be locked exactly once") {
Mutex mutex;
CHECK_EQ(mutex.lock(0), true);
CHECK_EQ(mutex.lock(0), false);
CHECK_EQ(mutex.unlock(), true);
}
TEST_CASE("unlocking a Mutex without locking returns false") {
Mutex mutex;
CHECK_EQ(mutex.unlock(), false);
}
TEST_CASE("unlocking a Mutex twice returns false on the second attempt") {
Mutex mutex;
CHECK_EQ(mutex.lock(0), true);
CHECK_EQ(mutex.unlock(), true);
CHECK_EQ(mutex.unlock(), false);
}
-35
View File
@@ -1,35 +0,0 @@
#include "doctest.h"
#include <Tactility/TactilityCore.h>
#include <Tactility/PubSub.h>
using namespace tt;
TEST_CASE("PubSub publishing with no subscriptions should not crash") {
PubSub<int> pubsub;
pubsub.publish(1);
}
TEST_CASE("PubSub subscription receives published data") {
PubSub<int> pubsub;
int value = 0;
auto subscription = pubsub.subscribe([&value](auto newValue) {
value = newValue;
});
pubsub.publish(1);
CHECK_EQ(value, 1);
}
TEST_CASE("PubSub unsubscribed subscription does not receive published data") {
PubSub<int> pubsub;
int value = 0;
auto subscription = pubsub.subscribe([&value](auto newValue) {
value = newValue;
});
pubsub.unsubscribe(subscription);
pubsub.publish(1);
CHECK_EQ(value, 0);
}
-35
View File
@@ -1,35 +0,0 @@
#include "doctest.h"
#include <Tactility/Semaphore.h>
using namespace tt;
// We want a distinct test for 1 item, because it creates the Semaphore differently
TEST_CASE("a Semaphore with max count of 1 can be acquired exactly once") {
auto semaphore = Semaphore(1);
CHECK_EQ(semaphore.acquire(0), true);
CHECK_EQ(semaphore.getAvailable(), 0);
CHECK_EQ(semaphore.acquire(0), false);
CHECK_EQ(semaphore.release(), true);
CHECK_EQ(semaphore.getAvailable(), 1);
}
TEST_CASE("a Semaphore with max count of 2 can be acquired exactly twice") {
auto semaphore = Semaphore(2);
CHECK_EQ(semaphore.acquire(0), true);
CHECK_EQ(semaphore.getAvailable(), 1);
CHECK_EQ(semaphore.acquire(0), true);
CHECK_EQ(semaphore.getAvailable(), 0);
CHECK_EQ(semaphore.acquire(0), false);
CHECK_EQ(semaphore.release(), true);
CHECK_EQ(semaphore.getAvailable(), 1);
CHECK_EQ(semaphore.release(), true);
CHECK_EQ(semaphore.getAvailable(), 2);
}
TEST_CASE("the semaphore count should be correct initially") {
auto semaphore_a = Semaphore(2);
CHECK_EQ(semaphore_a.getAvailable(), 2);
auto semaphore_b = Semaphore(2, 0);
CHECK_EQ(semaphore_b.getAvailable(), 0);
}
-100
View File
@@ -1,100 +0,0 @@
#include "doctest.h"
#include <Tactility/TactilityCore.h>
#include <Tactility/Thread.h>
using namespace tt;
TEST_CASE("when a thread is started then its callback should be called") {
bool has_called = false;
auto* thread = new Thread(
"immediate return task",
4096,
[&has_called]() {
has_called = true;
return 0;
}
);
CHECK(!has_called);
thread->start();
thread->join();
delete thread;
CHECK(has_called);
}
TEST_CASE("a thread can be started and stopped") {
bool interrupted = false;
auto* thread = new Thread(
"interruptable thread",
4096,
[&interrupted]() {
while (!interrupted) {
kernel::delayMillis(5);
}
return 0;
}
);
CHECK(thread);
thread->start();
interrupted = true;
thread->join();
delete thread;
}
TEST_CASE("thread id should only be set at when thread is started") {
bool interrupted = false;
auto* thread = new Thread(
"interruptable thread",
4096,
[&interrupted]() {
while (!interrupted) {
kernel::delayMillis(5);
}
return 0;
}
);
CHECK_EQ(thread->getId(), nullptr);
thread->start();
CHECK_NE(thread->getId(), nullptr);
interrupted = true;
thread->join();
CHECK_EQ(thread->getId(), nullptr);
delete thread;
}
TEST_CASE("thread state should be correct") {
bool interrupted = false;
auto* thread = new Thread(
"interruptable thread",
4096,
[&interrupted]() {
while (!interrupted) {
kernel::delayMillis(5);
}
return 0;
}
);
CHECK_EQ(thread->getState(), Thread::State::Stopped);
thread->start();
Thread::State state = thread->getState();
CHECK((state == Thread::State::Starting || state == Thread::State::Running));
interrupted = true;
thread->join();
CHECK_EQ(thread->getState(), Thread::State::Stopped);
delete thread;
}
TEST_CASE("thread id should only be set at when thread is started") {
int code = 123;
auto* thread = new Thread(
"return code",
4096,
[&code]() { return code; }
);
thread->start();
thread->join();
CHECK_EQ(thread->getReturnCode(), code);
delete thread;
}
-45
View File
@@ -1,45 +0,0 @@
#include "doctest.h"
#include <Tactility/TactilityCore.h>
#include <Tactility/Timer.h>
using namespace tt;
TEST_CASE("TimerType::Periodic timers can be stopped and restarted") {
int counter = 0;
auto* timer = new Timer(Timer::Type::Periodic, [&counter]() { counter++; });
timer->start(1);
kernel::delayTicks(10);
timer->stop();
timer->start(1);
kernel::delayTicks(10);
timer->stop();
delete timer;
CHECK_GE(counter, 2);
}
TEST_CASE("TimerType::Periodic calls the callback periodically") {
int ticks_to_run = 10;
int counter = 0;
auto* timer = new Timer(Timer::Type::Periodic, [&counter]() { counter++; });
timer->start(1);
kernel::delayTicks(ticks_to_run);
timer->stop();
delete timer;
CHECK_EQ(counter, ticks_to_run);
}
TEST_CASE("restarting TimerType::Once timers calls the callback again") {
int counter = 0;
auto* timer = new Timer(Timer::Type::Once, [&counter]() { counter++; });
timer->start(1);
kernel::delayTicks(10);
timer->stop();
timer->start(1);
kernel::delayTicks(10);
timer->stop();
delete timer;
CHECK_EQ(counter, 2);
}