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:
Ken Van Hoeylandt
2026-02-06 16:32:30 +01:00
committed by GitHub
parent 1757af859c
commit 79e43b093a
105 changed files with 338 additions and 331 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ enable_language(C CXX ASM)
set(CMAKE_CXX_COMPILER g++)
file(GLOB_RECURSE TEST_SOURCES ${PROJECT_SOURCE_DIR}/*.cpp)
file(GLOB_RECURSE TEST_SOURCES ${PROJECT_SOURCE_DIR}/Source/*.cpp)
add_executable(TactilityKernelTests EXCLUDE_FROM_ALL ${TEST_SOURCES})
target_include_directories(TactilityKernelTests PRIVATE ${DOCTESTINC})
@@ -12,30 +12,26 @@ static Module module = {
.stop = nullptr
};
TEST_CASE("device_construct and device_destruct should set and unset the correct fields") {
TEST_CASE("device_construct and device_destruct should set and unset the internal field") {
Device device = { 0 };
error_t error = device_construct(&device);
CHECK_EQ(error, ERROR_NONE);
CHECK_NE(device.internal.device_private, nullptr);
CHECK_NE(device.internal.mutex.handle, nullptr);
CHECK_NE(device.internal, nullptr);
CHECK_EQ(device_destruct(&device), ERROR_NONE);
CHECK_EQ(device.internal.device_private, nullptr);
CHECK_EQ(device.internal.mutex.handle, nullptr);
Device comparison_device = { 0 };
comparison_device.internal.device_private = device.internal.device_private;
comparison_device.internal.mutex.handle = device.internal.mutex.handle;
// Check that no other data was set
CHECK_EQ(memcmp(&device, &comparison_device, sizeof(Device)), 0);
CHECK_EQ(device.internal, nullptr);
}
TEST_CASE("device_add should add the device to the list of all devices") {
Device device = { 0 };
Device device = {
.name = "device",
.config = nullptr,
.parent = nullptr,
.internal = nullptr
};
CHECK_EQ(device_construct(&device), ERROR_NONE);
CHECK_EQ(device_add(&device), ERROR_NONE);
@@ -55,12 +51,18 @@ TEST_CASE("device_add should add the device to the list of all devices") {
}
TEST_CASE("device_add should add the device to its parent") {
Device parent = { 0 };
Device parent = {
.name = "parent",
.config = nullptr,
.parent = nullptr,
.internal = nullptr
};
Device child = {
.name = nullptr,
.name = "child",
.config = nullptr,
.parent = &parent
.parent = &parent,
.internal = nullptr
};
CHECK_EQ(device_construct(&parent), ERROR_NONE);
@@ -88,19 +90,31 @@ TEST_CASE("device_add should add the device to its parent") {
}
TEST_CASE("device_add should set the state to 'added'") {
Device device = { 0 };
Device device = {
.name = "device",
.config = nullptr,
.parent = nullptr,
.internal = nullptr
};
CHECK_EQ(device_construct(&device), ERROR_NONE);
CHECK_EQ(device.internal.state.added, false);
CHECK_EQ(device_is_added(&device), false);
CHECK_EQ(device_add(&device), ERROR_NONE);
CHECK_EQ(device.internal.state.added, true);
CHECK_EQ(device_is_added(&device), true);
CHECK_EQ(device_remove(&device), ERROR_NONE);
CHECK_EQ(device_destruct(&device), ERROR_NONE);
}
TEST_CASE("device_remove should remove it from the list of all devices") {
Device device = { 0 };
Device device = {
.name = "device",
.config = nullptr,
.parent = nullptr,
.internal = nullptr
};
CHECK_EQ(device_construct(&device), ERROR_NONE);
CHECK_EQ(device_add(&device), ERROR_NONE);
CHECK_EQ(device_remove(&device), ERROR_NONE);
@@ -119,12 +133,18 @@ TEST_CASE("device_remove should remove it from the list of all devices") {
}
TEST_CASE("device_remove should remove the device from its parent") {
Device parent = { 0 };
Device parent = {
.name = "parent",
.config = nullptr,
.parent = nullptr,
.internal = nullptr
};
Device child = {
.name = nullptr,
.name = "child",
.config = nullptr,
.parent = &parent
.parent = &parent,
.internal = nullptr
};
CHECK_EQ(device_construct(&parent), ERROR_NONE);
@@ -151,13 +171,19 @@ TEST_CASE("device_remove should remove the device from its parent") {
}
TEST_CASE("device_remove should clear the state 'added'") {
Device device = { 0 };
Device device = {
.name = "device",
.config = nullptr,
.parent = nullptr,
.internal = nullptr
};
CHECK_EQ(device_construct(&device), ERROR_NONE);
CHECK_EQ(device_add(&device), ERROR_NONE);
CHECK_EQ(device.internal.state.added, true);
CHECK_EQ(device_is_added(&device), true);
CHECK_EQ(device_remove(&device), ERROR_NONE);
CHECK_EQ(device.internal.state.added, false);
CHECK_EQ(device_is_added(&device), false);
CHECK_EQ(device_destruct(&device), ERROR_NONE);
}
@@ -172,7 +198,7 @@ TEST_CASE("device_is_ready should return true only when it is started") {
.api = nullptr,
.device_type = nullptr,
.owner = &module,
.driver_private = nullptr
.internal = nullptr
};
Device device = { 0 };
@@ -180,17 +206,17 @@ TEST_CASE("device_is_ready should return true only when it is started") {
CHECK_EQ(driver_construct_add(&driver), ERROR_NONE);
CHECK_EQ(device_construct(&device), ERROR_NONE);
CHECK_EQ(device.internal.state.started, false);
CHECK_EQ(device_is_ready(&device), false);
device_set_driver(&device, &driver);
CHECK_EQ(device.internal.state.started, false);
CHECK_EQ(device_is_ready(&device), false);
CHECK_EQ(device_add(&device), ERROR_NONE);
CHECK_EQ(device.internal.state.started, false);
CHECK_EQ(device_is_ready(&device), false);
CHECK_EQ(device_start(&device), ERROR_NONE);
CHECK_EQ(device.internal.state.started, true);
CHECK_EQ(device_is_ready(&device), true);
CHECK_EQ(device_stop(&device), ERROR_NONE);
CHECK_EQ(device.internal.state.started, false);
CHECK_EQ(device_is_ready(&device), false);
CHECK_EQ(device_remove(&device), ERROR_NONE);
CHECK_EQ(device.internal.state.started, false);
CHECK_EQ(device_is_ready(&device), false);
CHECK_EQ(device_destruct(&device), ERROR_NONE);
CHECK_EQ(driver_remove_destruct(&driver), ERROR_NONE);
@@ -38,7 +38,7 @@ static Driver integration_driver = {
.api = nullptr,
.device_type = nullptr,
.owner = &module,
.driver_private = nullptr,
.internal = nullptr,
};
TEST_CASE("driver with with start success and stop success should start and stop a device") {
@@ -15,10 +15,10 @@ TEST_CASE("driver_construct and driver_destruct should set and unset the correct
CHECK_EQ(driver_construct(&driver), ERROR_NONE);
CHECK_EQ(driver_add(&driver), ERROR_NONE);
CHECK_NE(driver.driver_private, nullptr);
CHECK_NE(driver.internal, nullptr);
CHECK_EQ(driver_remove(&driver), ERROR_NONE);
CHECK_EQ(driver_destruct(&driver), ERROR_NONE);
CHECK_EQ(driver.driver_private, nullptr);
CHECK_EQ(driver.internal, nullptr);
}
TEST_CASE("a driver without a module should not be destructible") {
@@ -40,7 +40,7 @@ TEST_CASE("driver_is_compatible should return true if a compatible value is foun
.api = nullptr,
.device_type = nullptr,
.owner = &module,
.driver_private = nullptr
.internal = nullptr
};
CHECK_EQ(driver_is_compatible(&driver, "test_compatible"), true);
CHECK_EQ(driver_is_compatible(&driver, "nope"), false);
@@ -57,7 +57,7 @@ TEST_CASE("driver_find should only find a compatible driver when the driver was
.api = nullptr,
.device_type = nullptr,
.owner = &module,
.driver_private = nullptr
.internal = nullptr
};
Driver* found_driver = driver_find_compatible("test_compatible");
@@ -23,12 +23,12 @@ TEST_CASE("Module construction and destruction") {
.start = test_start,
.stop = test_stop,
.symbols = nullptr,
.internal = {.started = false}
.internal = nullptr
};
// Test successful construction
CHECK_EQ(module_construct(&module), ERROR_NONE);
CHECK_EQ(module.internal.started, false);
CHECK_EQ(module_is_started(&module), false);
// Test successful destruction
CHECK_EQ(module_destruct(&module), ERROR_NONE);
@@ -40,7 +40,7 @@ TEST_CASE("Module registration") {
.start = test_start,
.stop = test_stop,
.symbols = nullptr,
.internal = {.started = false}
.internal = nullptr
};
// module_add should succeed
@@ -61,9 +61,11 @@ TEST_CASE("Module lifecycle") {
.start = test_start,
.stop = test_stop,
.symbols = nullptr,
.internal = {.started = false}
.internal = nullptr
};
CHECK_EQ(module_construct(&module), ERROR_NONE);
// 1. Successful start (no parent required anymore)
CHECK_EQ(module_start(&module), ERROR_NONE);
CHECK_EQ(module_is_started(&module), true);
@@ -104,6 +106,8 @@ TEST_CASE("Module lifecycle") {
// Clean up: fix stop result so we can stop it
test_stop_result = ERROR_NONE;
CHECK_EQ(module_stop(&module), ERROR_NONE);
CHECK_EQ(module_destruct(&module), ERROR_NONE);
}
TEST_CASE("Global symbol resolution") {
@@ -117,9 +121,11 @@ TEST_CASE("Global symbol resolution") {
.start = test_start,
.stop = test_stop,
.symbols = test_symbols,
.internal = {.started = false}
.internal = nullptr
};
REQUIRE_EQ(module_construct(&module), ERROR_NONE);
uintptr_t addr;
// Should fail as it is not added or started
CHECK_EQ(module_resolve_symbol_global("symbol_test_function", &addr), false);
@@ -128,8 +134,8 @@ TEST_CASE("Global symbol resolution") {
REQUIRE_EQ(module_start(&module), ERROR_NONE);
// Still fails as symbols are null
CHECK_EQ(module_resolve_symbol_global("symbol_test_function", &addr), true);
// Cleanup
CHECK_EQ(module_remove(&module), ERROR_NONE);
CHECK_EQ(module_destruct(&module), ERROR_NONE);
}