Kernel improvements (#485)
* **New Features** * Added public accessors for querying module/device start and ready state. * **Refactor** * Internal state moved to opaque internal objects; module/device/driver initializers now explicitly initialize internal pointers. * Lifecycle handling updated to construct/destruct internal state and use accessors. * **Tests** * Tests updated to use public accessors and explicit construct/destruct lifecycle calls. * **Chores** * Test build/include paths and small metadata updated.
This commit is contained in:
committed by
GitHub
parent
1757af859c
commit
79e43b093a
@@ -0,0 +1,98 @@
|
||||
#include "doctest.h"
|
||||
#include <tactility/hal/Device.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace tt;
|
||||
|
||||
class TestDevice final : public hal::Device {
|
||||
|
||||
private:
|
||||
|
||||
hal::Device::Type type;
|
||||
std::string name;
|
||||
std::string description;
|
||||
|
||||
public:
|
||||
|
||||
TestDevice(hal::Device::Type type, std::string name, std::string description) :
|
||||
type(type),
|
||||
name(std::move(name)),
|
||||
description(std::move(description))
|
||||
{}
|
||||
|
||||
TestDevice() : TestDevice(hal::Device::Type::Power, "PowerMock", "PowerMock description") {}
|
||||
|
||||
~TestDevice() final = default;
|
||||
|
||||
Type getType() const final { return type; }
|
||||
std::string getName() const final { return name; }
|
||||
std::string getDescription() const final { return description; }
|
||||
};
|
||||
|
||||
class DeviceAutoRegistration {
|
||||
|
||||
std::shared_ptr<hal::Device> device;
|
||||
|
||||
public:
|
||||
|
||||
explicit DeviceAutoRegistration(std::shared_ptr<hal::Device> inDevice) : device(std::move(inDevice)) {
|
||||
hal::registerDevice(device);
|
||||
}
|
||||
|
||||
~DeviceAutoRegistration() {
|
||||
hal::deregisterDevice(device);
|
||||
}
|
||||
};
|
||||
|
||||
/** We add 3 tests into 1 to ensure cleanup happens */
|
||||
TEST_CASE("registering and deregistering a device works") {
|
||||
auto device = std::make_shared<TestDevice>();
|
||||
|
||||
// Pre-registration
|
||||
CHECK_EQ(hal::findDevice(device->getId()), nullptr);
|
||||
|
||||
// Registration
|
||||
hal::registerDevice(device);
|
||||
auto found_device = hal::findDevice(device->getId());
|
||||
CHECK_NE(found_device, nullptr);
|
||||
CHECK_EQ(found_device->getId(), device->getId());
|
||||
|
||||
// Deregistration
|
||||
hal::deregisterDevice(device);
|
||||
CHECK_EQ(hal::findDevice(device->getId()), nullptr);
|
||||
found_device = nullptr; // to decrease use count
|
||||
CHECK_EQ(device.use_count(), 1);
|
||||
}
|
||||
|
||||
TEST_CASE("find device by id") {
|
||||
auto device = std::make_shared<TestDevice>();
|
||||
DeviceAutoRegistration auto_registration(device);
|
||||
|
||||
auto found_device = hal::findDevice(device->getId());
|
||||
CHECK_NE(found_device, nullptr);
|
||||
CHECK_EQ(found_device->getId(), device->getId());
|
||||
}
|
||||
|
||||
TEST_CASE("find device by name") {
|
||||
auto device = std::make_shared<TestDevice>();
|
||||
DeviceAutoRegistration auto_registration(device);
|
||||
|
||||
auto found_device = hal::findDevice(device->getName());
|
||||
CHECK_NE(found_device, nullptr);
|
||||
CHECK_EQ(found_device->getId(), device->getId());
|
||||
}
|
||||
|
||||
TEST_CASE("find device by type") {
|
||||
// Headless mode shouldn't have a display, so we want to create one to find only our own display as unique device
|
||||
// We first verify the initial assumption that there is no display:
|
||||
auto unexpected_display = hal::findFirstDevice<TestDevice>(hal::Device::Type::Display);
|
||||
CHECK_EQ(unexpected_display, nullptr);
|
||||
|
||||
auto device = std::make_shared<TestDevice>(hal::Device::Type::Display, "DisplayMock", "");
|
||||
DeviceAutoRegistration auto_registration(device);
|
||||
|
||||
auto found_device = hal::findFirstDevice<TestDevice>(hal::Device::Type::Display);
|
||||
CHECK_NE(found_device, nullptr);
|
||||
CHECK_EQ(found_device->getId(), device->getId());
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#define DOCTEST_CONFIG_IMPLEMENT
|
||||
#include "doctest.h"
|
||||
#include <cassert>
|
||||
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
|
||||
#include <tactility/kernel_init.h>
|
||||
#include <tactility/hal_device_module.h>
|
||||
|
||||
typedef struct {
|
||||
int argc;
|
||||
char** argv;
|
||||
int result;
|
||||
} TestTaskData;
|
||||
|
||||
// From the relevant platform
|
||||
extern "C" struct Module platform_module;
|
||||
|
||||
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
|
||||
|
||||
check(kernel_init(&platform_module, &hal_device_module, nullptr) == ERROR_NONE);
|
||||
|
||||
data->result = context.run();
|
||||
|
||||
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();
|
||||
|
||||
return data.result;
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "doctest.h"
|
||||
#include <Tactility/file/ObjectFile.h>
|
||||
|
||||
using tt::file::ObjectFileWriter;
|
||||
using tt::file::ObjectFileReader;
|
||||
|
||||
constexpr const char* TEMP_FILE = "test.tmp";
|
||||
|
||||
struct TestStruct {
|
||||
uint32_t value;
|
||||
};
|
||||
|
||||
TEST_CASE("Writing and reading multiple records to a file") {
|
||||
ObjectFileWriter writer = ObjectFileWriter(TEMP_FILE, sizeof(TestStruct), 1, false);
|
||||
|
||||
TestStruct record_out_1 = { .value = 0xAAAAAAAA };
|
||||
TestStruct record_out_2 = { .value = 0xBBBBBBBB };
|
||||
|
||||
CHECK_EQ(writer.open(), true);
|
||||
CHECK_EQ(writer.write(&record_out_1), true);
|
||||
CHECK_EQ(writer.write(&record_out_2), true);
|
||||
writer.close();
|
||||
|
||||
TestStruct record_in;
|
||||
ObjectFileReader reader = ObjectFileReader(TEMP_FILE, sizeof(TestStruct));
|
||||
CHECK_EQ(reader.open(), true);
|
||||
CHECK_EQ(reader.hasNext(), true);
|
||||
CHECK_EQ(reader.readNext(&record_in), true);
|
||||
CHECK_EQ(reader.hasNext(), true);
|
||||
CHECK_EQ(reader.readNext(&record_in), true);
|
||||
CHECK_EQ(reader.hasNext(), false);
|
||||
reader.close();
|
||||
|
||||
remove(TEMP_FILE);
|
||||
}
|
||||
|
||||
TEST_CASE("Appending records to a file") {
|
||||
remove(TEMP_FILE);
|
||||
|
||||
ObjectFileWriter writer = ObjectFileWriter(TEMP_FILE, sizeof(TestStruct), 1, false);
|
||||
|
||||
TestStruct record_out_1 = { .value = 0xAAAAAAAA };
|
||||
TestStruct record_out_2 = { .value = 0xBBBBBBBB };
|
||||
|
||||
CHECK_EQ(writer.open(), true);
|
||||
CHECK_EQ(writer.write(&record_out_1), true);
|
||||
writer.close();
|
||||
|
||||
ObjectFileWriter appender = ObjectFileWriter(TEMP_FILE, sizeof(TestStruct), 1, true);
|
||||
CHECK_EQ(appender.open(), true);
|
||||
CHECK_EQ(appender.write(&record_out_2), true);
|
||||
appender.close();
|
||||
|
||||
TestStruct record_in;
|
||||
ObjectFileReader reader = ObjectFileReader(TEMP_FILE, sizeof(TestStruct));
|
||||
CHECK_EQ(reader.open(), true);
|
||||
CHECK_EQ(reader.hasNext(), true);
|
||||
CHECK_EQ(reader.readNext(&record_in), true);
|
||||
CHECK_EQ(record_in.value, 0xAAAAAAAA);
|
||||
CHECK_EQ(reader.hasNext(), true);
|
||||
CHECK_EQ(reader.readNext(&record_in), true);
|
||||
CHECK_EQ(record_in.value, 0xBBBBBBBB);
|
||||
CHECK_EQ(reader.hasNext(), false);
|
||||
reader.close();
|
||||
|
||||
remove(TEMP_FILE);
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#include "../../TactilityCore/Source/TestFile.h"
|
||||
#include "../../Tactility/Include/Tactility/file/PropertiesFile.h"
|
||||
|
||||
#include "doctest.h"
|
||||
|
||||
using namespace tt;
|
||||
|
||||
TEST_CASE("loadPropertiesFile() should return false when the file does not exist") {
|
||||
std::map<std::string, std::string> properties;
|
||||
CHECK_EQ(file::loadPropertiesFile("does_not_exist.properties", properties), false);
|
||||
}
|
||||
|
||||
TEST_CASE("PropertiesFile should parse a valid file properly") {
|
||||
TestFile file("test.properties");
|
||||
file.writeData(
|
||||
"# Comment\n" // Regular comment
|
||||
" \t# Comment\n" // Prefixed comment
|
||||
"key1=value1\n" // Regular property
|
||||
" \tkey 2\t = \tvalue 2\t " // Property with empty space
|
||||
);
|
||||
|
||||
std::map<std::string, std::string> properties;
|
||||
|
||||
// Load data
|
||||
CHECK_EQ(file::loadPropertiesFile(file.getPath(), properties), true);
|
||||
|
||||
CHECK_EQ(properties.size(), 2);
|
||||
CHECK_EQ(properties["key1"], "value1");
|
||||
CHECK_EQ(properties["key 2"], "value 2");
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
#include "doctest.h"
|
||||
#include <Tactility/network/Url.h>
|
||||
|
||||
using namespace tt;
|
||||
|
||||
TEST_CASE("parseUrlQuery can handle a single key-value pair") {
|
||||
auto map = network::parseUrlQuery("?key=value");
|
||||
CHECK_EQ(map.size(), 1);
|
||||
CHECK_EQ(map["key"], "value");
|
||||
}
|
||||
|
||||
TEST_CASE("parseUrlQuery can handle empty value in the middle") {
|
||||
auto map = network::parseUrlQuery("?a=1&b=&c=3");
|
||||
CHECK_EQ(map.size(), 3);
|
||||
CHECK_EQ(map["a"], "1");
|
||||
CHECK_EQ(map["b"], "");
|
||||
CHECK_EQ(map["c"], "3");
|
||||
}
|
||||
|
||||
TEST_CASE("parseUrlQuery can handle empty value at the end") {
|
||||
auto map = network::parseUrlQuery("?a=1&b=");
|
||||
CHECK_EQ(map.size(), 2);
|
||||
CHECK_EQ(map["a"], "1");
|
||||
CHECK_EQ(map["b"], "");
|
||||
}
|
||||
|
||||
TEST_CASE("parseUrlQuery returns empty map when query s questionmark with a key without a value") {
|
||||
auto map = network::parseUrlQuery("?a");
|
||||
CHECK_EQ(map.size(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE("parseUrlQuery returns empty map when query is a questionmark") {
|
||||
auto map = network::parseUrlQuery("?");
|
||||
CHECK_EQ(map.size(), 0);
|
||||
}
|
||||
|
||||
TEST_CASE("parseUrlQuery should url-decode the value") {
|
||||
auto map = network::parseUrlQuery("?key=Test%21Test");
|
||||
CHECK_EQ(map.size(), 1);
|
||||
CHECK_EQ(map["key"], "Test!Test");
|
||||
}
|
||||
|
||||
TEST_CASE("parseUrlQuery should url-decode the key") {
|
||||
auto map = network::parseUrlQuery("?Test%21Test=value");
|
||||
CHECK_EQ(map.size(), 1);
|
||||
CHECK_EQ(map["Test!Test"], "value");
|
||||
}
|
||||
|
||||
TEST_CASE("urlDecode") {
|
||||
auto input = std::string("prefix!*'();:@&=+$,/?#[]<>%-.^_`{}|~ \\");
|
||||
auto expected = std::string("prefix%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%23%5B%5D%3C%3E%25-.%5E_%60%7B%7D%7C~+%5C");
|
||||
auto encoded = network::urlEncode(input);
|
||||
CHECK_EQ(encoded, expected);
|
||||
}
|
||||
|
||||
TEST_CASE("urlDecode") {
|
||||
auto input = std::string("prefix%21%2A%27%28%29%3B%3A%40%26%3D%2B%24%2C%2F%3F%23%5B%5D%3C%3E%25-.%5E_%60%7B%7D%7C~+%5C");
|
||||
auto expected = std::string("prefix!*'();:@&=+$,/?#[]<>%-.^_`{}|~ \\");
|
||||
auto decoded = network::urlDecode(input);
|
||||
CHECK_EQ(decoded, expected);
|
||||
}
|
||||
Reference in New Issue
Block a user