Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ jobs:
./examples/system/dmod_loader/dmod_loader ./dmf/log_step.dmf
echo "log_step module loaded and executed successfully"

- name: Run example dmod_add_test module
working-directory: build
run: |
./examples/system/dmod_loader/dmod_loader ./dmf/test_example.dmf
echo "test_example module passed all test steps"

- name: Run manifest library tests
working-directory: build
run: ./tests/tests_dmod_manifest
Expand Down
75 changes: 75 additions & 0 deletions docs/cmake-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,81 @@ build/
└── my_lib.zip
```

### `dmod_add_test(moduleName version sources...)`

Creates a DMOD test module.

The test runner `main()` is provided automatically by the framework — do **not**
define `main()` in your test sources. Test steps are registered with the
`DMOD_TEST_STEP()` macro from `dmod_test.h` and are discovered and executed
automatically at runtime.

The exit code of the resulting binary equals the number of failed steps, making
it suitable for use in CI pipelines.

**Parameters:**
- `moduleName` - Name of the test module
- `version` - Module version (e.g., "1.0")
- `sources...` - List of test source files (must **not** define `main()`)

**Example:**
```cmake
set(DMOD_MODULE_NAME my_module_tests)
set(DMOD_MODULE_VERSION "1.0")
set(DMOD_AUTHOR_NAME "Jane Smith")
set(DMOD_STACK_SIZE 2048)

dmod_add_test(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION}
test_feature_a.c
test_feature_b.c
)
```

**Test source example:**
```c
#include "dmod_test.h"

/* Optional lifecycle hooks */
void dmod_test_setup(void) { /* reset state */ }
void dmod_test_teardown(void) { /* cleanup */ }

DMOD_TEST_STEP(addition_works)
{
DMOD_TEST_EXPECT_EQ(1 + 1, 2);
}

DMOD_TEST_STEP(null_pointer_check)
{
void* ptr = get_something();
DMOD_TEST_EXPECT_NOT_NULL(ptr);
}
```

**Output example:**
```
=== DMOD Test Runner ===
[ RUN ] addition_works
[ OK ] addition_works
[ RUN ] null_pointer_check
[ OK ] null_pointer_check

=== Results: 2/2 passed ===
```

**Available assertion macros (from `dmod_test.h`):**

| Macro | Description |
|-------|-------------|
| `DMOD_TEST_EXPECT(cond)` | Fail if condition is false |
| `DMOD_TEST_EXPECT_TRUE(cond)` | Alias for `DMOD_TEST_EXPECT` |
| `DMOD_TEST_EXPECT_FALSE(cond)` | Fail if condition is true |
| `DMOD_TEST_EXPECT_EQ(a, b)` | Fail if `a != b` |
| `DMOD_TEST_EXPECT_NE(a, b)` | Fail if `a == b` |
| `DMOD_TEST_EXPECT_NULL(ptr)` | Fail if `ptr != NULL` |
| `DMOD_TEST_EXPECT_NOT_NULL(ptr)` | Fail if `ptr == NULL` |
| `DMOD_TEST_FAIL()` | Unconditionally fail |
| `DMOD_TEST_FAIL_MSG(msg, ...)` | Unconditionally fail with message |

## Dependency Management Functions

### `dmod_link_modules(targetName [PRIVATE|PUBLIC|INTERFACE] modules...)`
Expand Down
3 changes: 2 additions & 1 deletion examples/module/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
add_subdirectory(library)
add_subdirectory(application)
add_subdirectory(log_step)
add_subdirectory(log_step)
add_subdirectory(test_example)
7 changes: 7 additions & 0 deletions examples/module/test_example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
set(DMOD_MODULE_NAME test_example)
set(DMOD_MODULE_VERSION "1.0")
set(DMOD_AUTHOR_NAME "DMOD")

dmod_add_test(${DMOD_MODULE_NAME} ${DMOD_MODULE_VERSION}
test_example_test.c
)
38 changes: 38 additions & 0 deletions examples/module/test_example/test_example_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* @file test_example_test.c
* @brief Example DMOD test module
*
* Demonstrates the dmod_add_test / DMOD_TEST_STEP framework.
* All steps here must pass so the CI job exits with 0.
*/
#include "dmod_test.h"

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

void dmod_test_teardown(void)
{
/* runs after every step */
}

DMOD_TEST_STEP(arithmetic)
{
DMOD_TEST_EXPECT_EQ( 1 + 1, 2 );
DMOD_TEST_EXPECT_NE( 1 + 1, 3 );
}

DMOD_TEST_STEP(boolean)
{
DMOD_TEST_EXPECT_TRUE( 1 );
DMOD_TEST_EXPECT_FALSE( 0 );
}

DMOD_TEST_STEP(null_checks)
{
void* p = (void*)0;
DMOD_TEST_EXPECT_NULL( p );
p = (void*)1;
DMOD_TEST_EXPECT_NOT_NULL( p );
}
2 changes: 2 additions & 0 deletions inc/dmod.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.

extern bool Dmod_ApiSignature_IsMal( const char* Signature );
extern bool Dmod_ApiSignature_IsBuiltin( const char* Signature );
extern bool Dmod_ApiSignature_IsTest( const char* Signature );

DMOD_BUILTIN_API( Dmod, 1.0, void , _SetLogLevel, ( Dmod_LogLevel_t LogLevel ) );
DMOD_BUILTIN_API( Dmod, 1.0, void , _SetModuleLogLevel, ( const char* ModuleName, Dmod_LogLevel_t LogLevel ) );
Expand Down Expand Up @@ -124,6 +125,7 @@ DMOD_BUILTIN_API( Dmod, 1.0, bool , _IsFunctionConnected, (void* FunctionP
DMOD_BUILTIN_API( Dmod, 1.0, Dmod_Context_t*, _GetNextDifModule, (const char* DifSignature, Dmod_Context_t* Previous) );
DMOD_BUILTIN_API( Dmod, 1.0, void* , _GetDifFunction, (Dmod_Context_t* Context, const char* DifSignature) );
DMOD_BUILTIN_API( Dmod, 1.0, const char*, _GetName, (Dmod_Context_t* Context) );
DMOD_BUILTIN_API( Dmod, 1.0, int , _RunTests, (Dmod_Context_t* Context, int argc, char* argv[]) );

DMOD_BUILTIN_API( Dmod, 1.0, bool , _AddPackageBuffer, ( const void* Buffer, size_t Size, uint32_t* outIndex ) );
DMOD_BUILTIN_API( Dmod, 1.0, bool , _AddPackageFile, ( const char* FilePath, uint32_t* outIndex ) );
Expand Down
31 changes: 31 additions & 0 deletions inc/dmod_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,14 @@ extern "C" {
#define DMOD_IRQ_SIGNATURE_PREFIX "\021DIRQ\022"
#define DMOD_MAL_SIGNATURE_PREFIX "\021DMAL\022"
#define DMOD_DIF_SIGNATURE_PREFIX "\021DDIF\022"
#define DMOD_TEST_SIGNATURE_PREFIX "\021DTST\022"
#define DMOD_SIGNATURE_SUFFIX "\0"
#define DMOD_MAKE_SIGNATURE( MODULE, VERSION, NAME ) DMOD_SIGNATURE_PREFIX #NAME "@" #MODULE ":" #VERSION DMOD_SIGNATURE_SUFFIX
#define DMOD_MAKE_BUILTIN_SIGNATURE( MODULE, VERSION, NAME ) DMOD_BUILTIN_SIGNATURE_PREFIX #NAME "@" #MODULE ":" #VERSION DMOD_SIGNATURE_SUFFIX
#define DMOD_MAKE_IRQ_SIGNATURE( NAME ) DMOD_IRQ_SIGNATURE_PREFIX #NAME
#define DMOD_MAKE_MAL_SIGNATURE( MODULE, VERSION, NAME ) DMOD_MAL_SIGNATURE_PREFIX #NAME "@" #MODULE ":" #VERSION DMOD_SIGNATURE_SUFFIX
#define DMOD_MAKE_DIF_SIGNATURE( MODULE, VERSION, NAME ) DMOD_DIF_SIGNATURE_PREFIX #NAME "@" #MODULE ":" #VERSION DMOD_SIGNATURE_SUFFIX
#define DMOD_MAKE_TEST_SIGNATURE( NAME ) DMOD_TEST_SIGNATURE_PREFIX #NAME DMOD_SIGNATURE_SUFFIX
#define DMOD_MAKE_VERSION(API_VERSION, MODULE_VERSION) API_VERSION/MODULE_VERSION

//==============================================================================
Expand Down Expand Up @@ -231,6 +233,8 @@ extern "C" {
#define DMOD_IRQ_MAKE_HANDLER_NAME(IRQ_NUMBER) __irq_##IRQ_NUMBER
#define DMOD_IRQ_MAKE_REG_NAME(IRQ_NUMBER) __irq_##IRQ_NUMBER##_registration
#define DMOD_IRQ_SIGNATURE_BUFFER_SIZE ( sizeof(DMOD_IRQ_SIGNATURE_PREFIX) + 20 )
#define DMOD_TEST_MAKE_STEP_NAME(NAME) dmod_test_step_##NAME
#define DMOD_TEST_MAKE_REG_NAME(NAME) dmod_test_step_##NAME##_registration

#define DMOD_MAL_CONNECT( MODULE, NAME, FUNCTION_NAME ) \
DMOD_FUNCTION_REDEFINITION( DMOD_MAKE_MAL_API_FUNCTION_NAME(MODULE,NAME), FUNCTION_NAME )
Expand Down Expand Up @@ -260,6 +264,33 @@ extern "C" {
};\
static void DMOD_IRQ_MAKE_HANDLER_NAME(IRQ_NUMBER)(void)

/**
* @brief Defines a test step function and registers it for automatic discovery.
*
* Use this macro to define a test step function. All test steps are automatically
* discovered by the test runner provided by dmod_add_test.
*
* @param NAME Name of the test step (must be a valid C identifier)
*
* @note Include dmod_test.h to use assertion macros (DMOD_TEST_EXPECT_*) inside steps.
*
* Example:
* @code
* DMOD_TEST_STEP(my_step)
* {
* DMOD_TEST_EXPECT_EQ(1 + 1, 2);
* }
* @endcode
*/
#define DMOD_TEST_STEP( NAME ) \
static void DMOD_TEST_MAKE_STEP_NAME(NAME)(void);\
volatile const Dmod_ApiRegistration_t DMOD_TEST_MAKE_REG_NAME(NAME) DMOD_USED_SECTION(".dmod.inputs") = \
{ \
.Function = (void*)DMOD_TEST_MAKE_STEP_NAME(NAME), \
.Signature = DMOD_MAKE_TEST_SIGNATURE(NAME) \
};\
static void DMOD_TEST_MAKE_STEP_NAME(NAME)(void)

#ifdef DOXYGEN
/**
* @brief Defines Builtin API
Expand Down
16 changes: 16 additions & 0 deletions inc/dmod_module.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,22 @@ extern "C" {
*/
extern Dmod_LogLevel_t Dmod_GetLogLevel(void);

/**
* @brief Get the input API registrations of the running module.
*
* Reads the module footer to locate the .dmod.inputs section and returns
* a pointer to its entries together with the entry count. This is the
* canonical way for module-side code to iterate its own registrations;
* it avoids open-coding the footer pointer arithmetic in multiple places.
*
* @param outEntries Set to the first Dmod_ApiRegistration_t in the section.
* Must not be NULL.
* @param outCount Set to the number of entries in the section.
* Must not be NULL.
* @return true on success, false if the module header or footer is unavailable.
*/
extern bool Dmod_Module_GetInputs( Dmod_ApiRegistration_t** outEntries, uint32_t* outCount );

#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 2 additions & 0 deletions inc/dmod_system.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ extern int Dmod_Deinit ( Dmod_Context_t* Context );
extern int Dmod_Signal ( Dmod_Context_t* Context, int SignalNumber );
extern int Dmod_Irq ( Dmod_Context_t* Context, int IrqNumber );
extern void Dmod_IrqAll ( int IrqNumber );
extern int Dmod_RunTests ( Dmod_Context_t* Context, int argc, char* argv[] );
extern int Dmod_RunModuleTests ( const char* ModuleName, int argc, char* argv[] );

// DMF Getters
extern uint64_t Dmod_GetStackSize ( Dmod_Context_t* Context );
Expand Down
Loading
Loading