Skip to content

Add dmod unit test framework: dmod_test.h, DMOD_TEST_STEP, dmod_add_test, Dmod_RunTests#306

Merged
JohnAmadis merged 4 commits into
developfrom
copilot/add-unit-test-framework
Jun 3, 2026
Merged

Add dmod unit test framework: dmod_test.h, DMOD_TEST_STEP, dmod_add_test, Dmod_RunTests#306
JohnAmadis merged 4 commits into
developfrom
copilot/add-unit-test-framework

Conversation

Copilot AI commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Modules frequently need lightweight in-process testing without the overhead of linking gtest or similar. This adds a first-class dmod test framework: a dmod_test.h header with assertion macros, a DMOD_TEST_STEP registration macro, and a dmod_add_test CMake function that injects a pre-built main() which auto-discovers and runs all registered steps.

Mechanics

Test steps register themselves into the .dmod.inputs ELF section (same mechanism as IRQ handlers) using a \021DTST\022-prefixed signature. Three additional reserved entries are registered automatically by dmod_test_main.c: __setup__, __teardown__, and __step_failed__. The system-side Dmod_RunTests() iterates the inputs section (following the Dmod_Irq pattern), discovers those fixture entries, and calls each test step in sequence — no manual registration list required. main() delegates entirely to Dmod_RunTests().

New API surface

  • DMOD_TEST_STEP(name) — defines and registers a test step function
  • Assertion macros (dmod_test.h): DMOD_TEST_EXPECT_EQ, DMOD_TEST_EXPECT_NE, DMOD_TEST_EXPECT_TRUE, DMOD_TEST_EXPECT_FALSE, DMOD_TEST_EXPECT_NULL, DMOD_TEST_EXPECT_NOT_NULL, DMOD_TEST_FAIL, DMOD_TEST_FAIL_MSG
  • dmod_test_setup() / dmod_test_teardown() — weak-symbol hooks called before/after each step; override with a strong definition to add fixtures
  • dmod_add_test(name version sources...) — CMake function; behaves like dmod_add_executable but appends dmod_test_main.c automatically
  • Dmod_ApiSignature_IsTest(signature) — public predicate in dmod_common.c/dmod.h, alongside Dmod_ApiSignature_IsMal() and Dmod_ApiSignature_IsBuiltin()
  • Dmod_Module_GetInputs(outEntries, outCount) — module-side helper in dmod_module.c/dmod_module.h that encapsulates footer pointer arithmetic for accessing the .dmod.inputs section
  • Dmod_RunTests(Context, argc, argv)DMOD_BUILTIN_API in dmod.h; system-side implementation in dmod_dmf_cb.c that iterates the module's .dmod.inputs section, discovers fixture entries, applies optional step-name filtering from argv, and returns the number of failed steps
  • Dmod_RunModuleTests(ModuleName, argc, argv) — system-side convenience in dmod_system.h/dmod_system.c; loads the named module and runs its tests via Dmod_Run, following the Dmod_RunModule pattern
  • CLI step filteringDmod_RunTests accepts step names as positional arguments; only the named steps run (e.g. dmod_loader test.dmf arithmetic null_checks); no arguments runs all steps

Usage

// my_module_test.c
#include "dmod_test.h"

DMOD_TEST_STEP(buffer_roundtrip)
{
    uint8_t buf[4] = {0};
    my_module_write(buf, 0xDEAD);
    DMOD_TEST_EXPECT_EQ(my_module_read(buf), 0xDEAD);
}

// Fixture hooks — called before/after every step
void dmod_test_setup(void)    { /* initialise fixtures */ }
void dmod_test_teardown(void) { /* clean up fixtures  */ }
dmod_add_test(my_module_test "1.0"
    my_module_test.c
)

Files changed

  • inc/dmod_defs.hDMOD_TEST_SIGNATURE_PREFIX, DMOD_MAKE_TEST_SIGNATURE, DMOD_TEST_STEP macro
  • inc/dmod_test.h (new) — public test API header
  • inc/dmod_module.h — added Dmod_Module_GetInputs() declaration
  • src/module/dmod_test_main.c (new) — registers __setup__, __teardown__, __step_failed__ fixture entries in .dmod.inputs; main() calls Dmod_RunTests(Dmod_GetModuleContext(...), argc, argv)
  • src/module/dmod_module.c — added Dmod_Module_GetInputs() implementation
  • src/common/dmod_common.c — added Dmod_ApiSignature_IsTest() and extended Dmod_ApiSignature_IsValid to accept test signatures
  • inc/dmod.h — declared Dmod_ApiSignature_IsTest() and DMOD_BUILTIN_API for Dmod_RunTests
  • inc/dmod_system.h — declared Dmod_RunTests and Dmod_RunModuleTests
  • src/system/public/dmod_dmf_cb.c — implemented Dmod_RunTests (follows Dmod_Irq pattern)
  • src/system/dmod_system.c — implemented Dmod_RunModuleTests (follows Dmod_RunModule pattern)
  • scripts/CMakeLists.txtdmod_add_test function
  • templates/module/test/ (new) — module template for test modules, including dmod_test_setup()/dmod_test_teardown() stubs
  • examples/module/test_example/ (new) — example test module built with dmod_add_test; used by CI to verify the framework compiles and runs end-to-end
  • docs/cmake-functions.mddmod_add_test documentation
  • .github/workflows/ci.yml — CI step that loads test_example.dmf via dmod_loader and verifies all steps pass

Copilot AI linked an issue Jun 3, 2026 that may be closed by this pull request
Copilot AI changed the title [WIP] Add custom unit test framework for dmod modules Add dmod unit test framework: dmod_test.h, DMOD_TEST_STEP, dmod_add_test Jun 3, 2026
Copilot AI requested a review from JohnAmadis June 3, 2026 14:41

@JohnAmadis JohnAmadis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot popraw to proszę i jeszcze dodaj jakiś krok na CI który weryfikuje przykładowy test - żeby potwierdzić że się on kompiluje I działa

Comment thread src/module/dmod_test_main.c Outdated
/* Obtain the footer via the module header.
* DMOD_Header == binary base address (ModuleHeader is at offset 0).
* After loading, Footer.Ptr is the relocated pointer to __footer_start. */
Dmod_ModuleFooter_t* footer = (Dmod_ModuleFooter_t*)DMOD_Header->Footer.Ptr;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nie rób tej magii tutaj, bo będzie śmietnik - zamiast tego dodaj ją tak jak inne są dodawane, do dmod

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The footer/section pointer arithmetic is now in Dmod_Module_GetInputs() in dmod_module.c/dmod_module.h, consistent with how other module-side helpers live there. dmod_test_main.c now simply calls that function.

Comment thread src/module/dmod_test_main.c Outdated
/* Return 1 if sig starts with the test signature prefix, 0 otherwise. */
static int IsTestSignature( const char* sig )
{
const char prefix[] = DMOD_TEST_SIGNATURE_PREFIX;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Te funkcje też nie powinny być tutaj, tylko razem z innymi podobnymi

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved. IsTestSignature() is now Dmod_ApiSignature_IsTest() in dmod_common.c, declared in dmod.h alongside Dmod_ApiSignature_IsMal() and Dmod_ApiSignature_IsBuiltin(). GetTestName() was replaced by the existing Dmod_ApiSignature_GetName().

Comment thread src/module/dmod_test_main.c Outdated
int main( int argc, char* argv[] )
{
(void)argc;
(void)argv;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Jakieś wsparcie dla filtrów z linii komend by się przydało, może np po nazwie kroku?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added. main() now accepts step names as positional arguments — any argument after argv[0] is treated as a step-name allowlist. With no arguments all steps run as before. Example: dmod_loader ./test_example.dmf arithmetic null_checks runs only those two steps.

#include "dmod_test.h"

/* Optional: runs before every test step */
/* void dmod_test_setup(void) {} */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Setup I teardown też może być w szablonie

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. The template now includes real dmod_test_setup() / dmod_test_teardown() stubs (uncommented) so new test modules get fixture hooks out of the box.

…ample CI test

- Add Dmod_ApiSignature_IsTest() to dmod_common.c / dmod.h alongside
  IsMal() and IsBuiltin()
- Add Dmod_Module_GetInputs() to dmod_module.c / dmod_module.h to
  encapsulate footer/section pointer arithmetic
- Rewrite dmod_test_main.c to use the new proper APIs instead of
  inline magic; remove IsTestSignature() / GetTestName() helpers;
  add command-line step-name filtering (argv[1..] as allowlist)
- Add setup/teardown stubs (uncommented) to the test module template
- Add examples/module/test_example/ as a self-contained dmod_add_test
  example with arithmetic, boolean, and null-check steps
- Add CI step in .github/workflows/ci.yml to build and run the
  example test module via dmod_loader

Copilot AI commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

@copilot popraw to proszę i jeszcze dodaj jakiś krok na CI który weryfikuje przykładowy test - żeby potwierdzić że się on kompiluje I działa

All four review items addressed (see individual thread replies). A CI step was also added to .github/workflows/ci.yml that builds examples/module/test_example/ (an dmod_add_test module with three passing steps) and runs it via dmod_loader, confirming the framework compiles and works end-to-end.

Copilot AI requested a review from JohnAmadis June 3, 2026 15:01
@JohnAmadis JohnAmadis marked this pull request as ready for review June 3, 2026 15:58

@JohnAmadis JohnAmadis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot popraw to proszę. Nie chce grzebania po wnętrzu poza dmod

Comment thread src/module/dmod_test_main.c Outdated

for( uint32_t i = 0; i < count; i++ )
{
if( entries[i].Function == NULL ) { continue; }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot nie masz tutaj grzebać po bebechach dmoda - sprawdź sobie jak dif jest zaimplementowany - tam mamy odpowiednie API do uzyskania kolejnych wpisów, tutaj też coś takiego chcemy

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0d5f5aa. dmod_test_main.c no longer accesses DMOD_Header or the footer — it simply calls Dmod_RunTests(Dmod_GetModuleContext(...), argc, argv). The inputs-section iteration now lives inside Dmod_RunTests in dmod_dmf_cb.c, using Dmod_Api_GetNumberOfEntries + Context->Inputs.InputSection->Entries exactly as Dmod_Irq does.

Comment thread inc/dmod.h
@@ -93,6 +93,7 @@ extern bool Dmod_ApiSignature_ReadModuleVersion( const char* Signature,
extern bool Dmod_ApiSignature_AreCompatible( const char* Signature1, const char* Signature2 );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nie dodałeś wsparcia dla tej sygnatury do ApiSignature i nie ma szans by ten moduł ci ruszył

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 0d5f5aa. Dmod_RunTests is now declared as DMOD_BUILTIN_API(Dmod, 1.0, int, _RunTests, (Dmod_Context_t* Context, int argc, char* argv[])) in inc/dmod.h. On the system side this registers the implementation in .dmod.inputs; on the module side it creates the output function-pointer slot that gets resolved at load time — same mechanism as every other builtin.

Comment thread src/module/dmod_test_main.c Outdated

Dmod_Module_GetInputs( &entries, &count );

Dmod_Printf( "=== DMOD Test Runner ===\n" );

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dodaj nowa funkcje w dmod.h i dmod_system.h : Dmod_RunTests oraz Dmoda_RunModuleTests - jedna powinna brać jako parametr po prostu kontekst (oraz argc i argv), a druga nazwę modułu do odpalenia testów (podobnie jak to z innymi interfejsami jest zrobione, zobaczysz o co chodzi. Ja to z pamięci pisze teraz) wtedy tutaj w main wystarczy odpalić tylko te jedna funkcje

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit 0d5f5aa. Dmod_RunTests(Dmod_Context_t* Context, int argc, char* argv[]) is now a DMOD_BUILTIN_API in dmod.h / implemented in dmod_dmf_cb.c following the Dmod_Irq pattern (iterates Context->Inputs.InputSection->Entries, no internals exposed). Dmod_RunModuleTests(const char* ModuleName, int argc, char* argv[]) is in dmod_system.h / dmod_system.c following Dmod_RunModule. main() in dmod_test_main.c is now just return Dmod_RunTests(Dmod_GetModuleContext(Dmod_GetCurrentModuleName()), argc, argv);.

Copilot AI commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

@copilot popraw to proszę. Nie chce grzebania po wnętrzu poza dmod

Fixed in 0d5f5aa. No module internals are accessed outside of dmod — dmod_test_main.c now only calls Dmod_RunTests(Dmod_GetModuleContext(Dmod_GetCurrentModuleName()), argc, argv). All iteration and fixture discovery is done inside Dmod_RunTests in dmod_dmf_cb.c, following the same pattern as Dmod_Irq.

Copilot AI changed the title Add dmod unit test framework: dmod_test.h, DMOD_TEST_STEP, dmod_add_test Add dmod unit test framework: dmod_test.h, DMOD_TEST_STEP, dmod_add_test, Dmod_RunTests Jun 3, 2026
Copilot AI requested a review from JohnAmadis June 3, 2026 16:43
@JohnAmadis JohnAmadis merged commit 48d4e53 into develop Jun 3, 2026
6 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Dodać framework unit testów

2 participants