Kernel and buildscript improvements (#477)
* **New Features** * Centralized module management with global symbol resolution * Level-aware logging with colored prefixes and millisecond timestamps * **Breaking Changes** * ModuleParent hierarchy and getModuleParent() removed * Logging API and adapter model replaced; LogLevel-driven log_generic signature changed * **Improvements** * Unified, simplified module registration across build targets * Tests updated to reflect new module lifecycle and global symbol resolution
This commit is contained in:
committed by
GitHub
parent
1a61eac8e0
commit
a935410f82
@@ -1,6 +1,8 @@
|
||||
#include "doctest.h"
|
||||
#include <tactility/module.h>
|
||||
|
||||
static void symbol_test_function() { /* NO-OP */ }
|
||||
|
||||
static error_t test_start_result = ERROR_NONE;
|
||||
static bool start_called = false;
|
||||
static error_t test_start() {
|
||||
@@ -15,84 +17,40 @@ static error_t test_stop() {
|
||||
return test_stop_result;
|
||||
}
|
||||
|
||||
TEST_CASE("ModuleParent construction and destruction") {
|
||||
struct ModuleParent parent = { "test_parent", nullptr };
|
||||
TEST_CASE("Module construction and destruction") {
|
||||
struct Module module = {
|
||||
.name = "test",
|
||||
.start = test_start,
|
||||
.stop = test_stop,
|
||||
.symbols = nullptr,
|
||||
.internal = {.started = false}
|
||||
};
|
||||
|
||||
// Test successful construction
|
||||
CHECK_EQ(module_parent_construct(&parent), ERROR_NONE);
|
||||
CHECK_NE(parent.module_parent_private, nullptr);
|
||||
CHECK_EQ(module_construct(&module), ERROR_NONE);
|
||||
CHECK_EQ(module.internal.started, false);
|
||||
|
||||
// Test successful destruction
|
||||
CHECK_EQ(module_parent_destruct(&parent), ERROR_NONE);
|
||||
CHECK_EQ(parent.module_parent_private, nullptr);
|
||||
CHECK_EQ(module_destruct(&module), ERROR_NONE);
|
||||
}
|
||||
|
||||
TEST_CASE("ModuleParent destruction with children") {
|
||||
struct ModuleParent parent = { "parent", nullptr };
|
||||
REQUIRE_EQ(module_parent_construct(&parent), ERROR_NONE);
|
||||
|
||||
TEST_CASE("Module registration") {
|
||||
struct Module module = {
|
||||
.name = "test",
|
||||
.start = test_start,
|
||||
.stop = test_stop,
|
||||
.symbols = nullptr,
|
||||
.internal = {.started = false, .parent = nullptr}
|
||||
.internal = {.started = false}
|
||||
};
|
||||
|
||||
REQUIRE_EQ(module_set_parent(&module, &parent), ERROR_NONE);
|
||||
// module_add should succeed
|
||||
CHECK_EQ(module_add(&module), ERROR_NONE);
|
||||
|
||||
// Should fail to destruct because it has a child
|
||||
CHECK_EQ(module_parent_destruct(&parent), ERROR_INVALID_STATE);
|
||||
CHECK_NE(parent.module_parent_private, nullptr);
|
||||
|
||||
// Remove child
|
||||
REQUIRE_EQ(module_set_parent(&module, nullptr), ERROR_NONE);
|
||||
|
||||
// Now it should succeed
|
||||
CHECK_EQ(module_parent_destruct(&parent), ERROR_NONE);
|
||||
CHECK_EQ(parent.module_parent_private, nullptr);
|
||||
}
|
||||
|
||||
TEST_CASE("Module parent management") {
|
||||
struct ModuleParent parent1 = { "parent1", nullptr };
|
||||
struct ModuleParent parent2 = { "parent2", nullptr };
|
||||
REQUIRE_EQ(module_parent_construct(&parent1), ERROR_NONE);
|
||||
REQUIRE_EQ(module_parent_construct(&parent2), ERROR_NONE);
|
||||
|
||||
struct Module module = {
|
||||
.name = "test",
|
||||
.start = test_start,
|
||||
.stop = test_stop,
|
||||
.symbols = nullptr,
|
||||
.internal = {.started = false, .parent = nullptr}
|
||||
};
|
||||
|
||||
// Set parent
|
||||
CHECK_EQ(module_set_parent(&module, &parent1), ERROR_NONE);
|
||||
CHECK_EQ(module.internal.parent, &parent1);
|
||||
|
||||
// Change parent
|
||||
CHECK_EQ(module_set_parent(&module, &parent2), ERROR_NONE);
|
||||
CHECK_EQ(module.internal.parent, &parent2);
|
||||
|
||||
// Clear parent
|
||||
CHECK_EQ(module_set_parent(&module, nullptr), ERROR_NONE);
|
||||
CHECK_EQ(module.internal.parent, nullptr);
|
||||
|
||||
// Set same parent (should be NOOP and return ERROR_NONE)
|
||||
CHECK_EQ(module_set_parent(&module, &parent1), ERROR_NONE);
|
||||
CHECK_EQ(module_set_parent(&module, &parent1), ERROR_NONE);
|
||||
CHECK_EQ(module.internal.parent, &parent1);
|
||||
|
||||
CHECK_EQ(module_set_parent(&module, nullptr), ERROR_NONE);
|
||||
CHECK_EQ(module_parent_destruct(&parent1), ERROR_NONE);
|
||||
CHECK_EQ(module_parent_destruct(&parent2), ERROR_NONE);
|
||||
// module_remove should succeed
|
||||
CHECK_EQ(module_remove(&module), ERROR_NONE);
|
||||
}
|
||||
|
||||
TEST_CASE("Module lifecycle") {
|
||||
struct ModuleParent parent = { "parent", nullptr };
|
||||
REQUIRE_EQ(module_parent_construct(&parent), ERROR_NONE);
|
||||
|
||||
start_called = false;
|
||||
stop_called = false;
|
||||
test_start_result = ERROR_NONE;
|
||||
@@ -103,48 +61,37 @@ TEST_CASE("Module lifecycle") {
|
||||
.start = test_start,
|
||||
.stop = test_stop,
|
||||
.symbols = nullptr,
|
||||
.internal = {.started = false, .parent = nullptr}
|
||||
.internal = {.started = false}
|
||||
};
|
||||
|
||||
// 1. Cannot start without parent
|
||||
CHECK_EQ(module_start(&module), ERROR_INVALID_STATE);
|
||||
CHECK_EQ(module_is_started(&module), false);
|
||||
CHECK_EQ(start_called, false);
|
||||
|
||||
CHECK_EQ(module_set_parent(&module, &parent), ERROR_NONE);
|
||||
|
||||
// 2. Successful start
|
||||
// 1. Successful start (no parent required anymore)
|
||||
CHECK_EQ(module_start(&module), ERROR_NONE);
|
||||
CHECK_EQ(module_is_started(&module), true);
|
||||
CHECK_EQ(start_called, true);
|
||||
|
||||
// 3. Start when already started (should return ERROR_NONE)
|
||||
// Start when already started (should return ERROR_NONE)
|
||||
start_called = false;
|
||||
CHECK_EQ(module_start(&module), ERROR_NONE);
|
||||
CHECK_EQ(start_called, false); // start() function should NOT be called again
|
||||
|
||||
// 4. Cannot change parent while started
|
||||
CHECK_EQ(module_set_parent(&module, nullptr), ERROR_INVALID_STATE);
|
||||
|
||||
// 5. Successful stop
|
||||
// Stop successful
|
||||
CHECK_EQ(module_stop(&module), ERROR_NONE);
|
||||
CHECK_EQ(module_is_started(&module), false);
|
||||
CHECK_EQ(stop_called, true);
|
||||
|
||||
// 6. Stop when already stopped (should return ERROR_NONE)
|
||||
// Stop when already stopped (should return ERROR_NONE)
|
||||
stop_called = false;
|
||||
CHECK_EQ(module_stop(&module), ERROR_NONE);
|
||||
CHECK_EQ(stop_called, false); // stop() function should NOT be called again
|
||||
|
||||
// 7. Test failed start
|
||||
// Test failed start
|
||||
test_start_result = ERROR_NOT_FOUND;
|
||||
start_called = false;
|
||||
CHECK_EQ(module_start(&module), ERROR_NOT_FOUND);
|
||||
CHECK_EQ(module_is_started(&module), false);
|
||||
CHECK_EQ(start_called, true);
|
||||
|
||||
// 8. Test failed stop
|
||||
CHECK_EQ(module_set_parent(&module, &parent), ERROR_NONE);
|
||||
// Test failed stop
|
||||
test_start_result = ERROR_NONE;
|
||||
CHECK_EQ(module_start(&module), ERROR_NONE);
|
||||
|
||||
@@ -157,7 +104,32 @@ 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_set_parent(&module, nullptr), ERROR_NONE);
|
||||
CHECK_EQ(module_parent_destruct(&parent), ERROR_NONE);
|
||||
}
|
||||
|
||||
TEST_CASE("Global symbol resolution") {
|
||||
static const struct ModuleSymbol test_symbols[] = {
|
||||
DEFINE_MODULE_SYMBOL(symbol_test_function),
|
||||
MODULE_SYMBOL_TERMINATOR
|
||||
};
|
||||
|
||||
struct Module module = {
|
||||
.name = "test_sym",
|
||||
.start = test_start,
|
||||
.stop = test_stop,
|
||||
.symbols = test_symbols,
|
||||
.internal = {.started = false}
|
||||
};
|
||||
|
||||
uintptr_t addr;
|
||||
// Should fail as it is not added or started
|
||||
CHECK_EQ(module_resolve_symbol_global("symbol_test_function", &addr), false);
|
||||
REQUIRE_EQ(module_add(&module), ERROR_NONE);
|
||||
CHECK_EQ(module_resolve_symbol_global("symbol_test_function", &addr), false);
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user