Partial Directive Macros
Documentation convention
*is a stand-in or wildcard to refer to all variations of a particular macro type.- For brevity, some classes of directive macros are referred to simply by their common
suffix (e.g
_MODULErefers to all the macros with this suffix.)
All Partial configuration is expressed through C macros placed
in your test file. No separate configuration file is required. The macros
require #include "ceedling.h" present in the test file above and before
their use.
The Partial _MODULE macros accomplish the following:
- Expand to a filename for the preceding
#includedirective. - Provide a module name for Ceedling to process for the resulting Partial.
- Assemble a base set of functions to test or mock.
The optional Partial _CONFIG macros modify the base set of functions
from (3). Modifying a base set of functions is documented in detail in the
Partials function list configuration macros
section.
Configuring Partials
For enabling and configuring Partials, see Configuring Partials.
Directive Macros Example
// Required before we can use the Partials directive macros
#include "ceedling.h"
// Mock all `static` and/or `inline` functions of `mymodule`
#include MOCK_PARTIAL_PRIVATE_MODULE(mymodule)
// But, remove `InternalHelper()` from the mocks
MOCK_PARTIAL_CONFIG(mymodule, -_InternalHelper)
Enable obnoxious level logging to inspect Partials generation
Add --verbosity obnoxious (or -v 4) to your test: command line invocation
for detailed logging of the C extraction and Partials generations for your test
build. The logged lists of functions and variables can help you fine tune or
troubleshoot your Partials configuration.
#include conventions
The _MODULE macros each expand to a string literal that names a
generated header file. This means you use them as the argument to #include:
// Must come first -- defines all Partial macros
#include "ceedling.h"
#include TEST_PARTIAL_*_MODULE(sensor)
// ↑ Expands to: #include "ceedling_partial_sensor_impl.h"
A TEST_PARTIAL_*_MODULE macro always names an implementation header.
#include "ceedling.h"
#include MOCK_PARTIAL_*_MODULE(sensor)
// ↑ Expands to: #include "mock_ceedling_partial_sensor_interface.h"
A MOCK_PARTIAL_*_MODULE macro always names a mockable interface header.
The filters in place of * in the macro names — PUBLIC, PRIVATE, ALL,
and none — tell Ceedling how to initialize internal function lists (that
can be optionally modified) towards injecting the collected functions into
each Partial.
Naming a module by directory
Every _MODULE and _CONFIG macro has an _AT variant that takes the module’s
directory as a separate first argument. Use it when a module name alone is
ambiguous — that is, when your project holds two modules of the same name in
different directories.
#include "ceedling.h"
#include TEST_PARTIAL_PRIVATE_MODULE_AT(sensors/soil, Calibration)
// ↑ Expands to: #include "sensors/soil/ceedling_partial_Calibration_impl.h"
The generated filename keeps the directory and takes its prefix on the basename (mirroring your source tree inside the build directory). Two same-named modules therefore generate two distinct files rather than overwriting one another.
| Macro family | Bare form | Directory-qualified form |
|---|---|---|
| Test module | TEST_PARTIAL_*_MODULE(module) |
TEST_PARTIAL_*_MODULE_AT(dir, module) |
| Mock module | MOCK_PARTIAL_*_MODULE(module) |
MOCK_PARTIAL_*_MODULE_AT(dir, module) |
| Test function list | TEST_PARTIAL_CONFIG(module, ...) |
TEST_PARTIAL_CONFIG_AT(dir, module, ...) |
| Mock function list | MOCK_PARTIAL_CONFIG(module, ...) |
MOCK_PARTIAL_CONFIG_AT(dir, module, ...) |
Two same-named modules in one test file
TEST_PARTIAL_*_MODULE_AT() and MOCK_PARTIAL_*_MODULE_AT() both work for two
modules sharing a basename.
A _CONFIG_AT macro must name the same directory as the _MODULE_AT macro it
refines. The directory and module together identify which Partial a function
list belongs to.
#include TEST_PARTIAL_MODULE_AT(sensors/soil, Calibration)
TEST_PARTIAL_CONFIG_AT(sensors/soil, Calibration, Soil__Clamp)
How much path to supply
The directory is matched against the trailing end of a real file’s path. So,
supply only as much as it takes to distinguish the module you mean. Given
src/sensors/soil/Calibration.c, both of the following could reach it.
The amount of path information to provide changes depending on the depth of
duplicated subdirectories in path names.
#include TEST_PARTIAL_PRIVATE_MODULE_AT(sensors/soil, Calibration)
#include TEST_PARTIAL_PRIVATE_MODULE_AT(soil, Calibration)
A Partials path that matches no file triggers a breaking test build early in the build rather than a missing-include failure later in the build.
Rules for the directory argument
- Written as bare text, like the module name. No quotation marks.
- Relative, never absolute. A Partial is generated relative to the project root.
- Optional. A bare module name using non
_ATmacros is entirely acceptable for modules without duplicated names elsewhere in your project. - Supplied as the macro’s own first argument, never inside the module argument.
A path belongs in the directory argument, not the module argument
The directory must be the macro’s first argument. A path written inside the module argument of a bare macro does not work:
A bare module name resolves like the compiler’s own search
Without a directory, Ceedling selects the first match among your ordered
:paths search paths, the same way a compiler resolves a header. That is
unambiguous in most projects and only needs an _AT macro when two modules
share a name.
Partials function-selection by macro
Each test or mock Partial is independently configured by exactly
one _MODULE macro call. The macro determines the base set of
functions that Ceedling collects towards generating a Partial. Once
the base set of functions is determined, explicit additions or
subtractions can be applied with _CONFIG macros.
This scheme gives you the, test author, full control of which functions are injected into which type of Partial while avoiding laboriously listing each function individually.
Partials base function filters
[TEST/MOCK]_PARTIAL_ALL_MODULE()[TEST/MOCK]_PARTIAL_PUBLIC_MODULE()[TEST/MOCK]_PARTIAL_PRIVATE_MODULE()[TEST/MOCK]_PARTIAL_MODULE()
Each has an _AT variant taking the module’s directory, documented in
Naming a module by directory. The filter and
the function-list rules below apply identically to both forms.
| Macro | Base set of functions | Additions | Subtractions |
|---|---|---|---|
_ALL_MODULE |
All functions | Forbidden | Any function |
_PUBLIC_MODULE |
All public functions | Add private | Remove public |
_PRIVATE_MODULE |
All private functions | Add public | Remove private |
_MODULE |
Empty | Any function (at least one) | Forbidden |
Example base sets of function by filter
| Functions | ALL |
PUBLIC |
PRIVATE |
None |
|---|---|---|---|---|
| void foo(void) | ✅ | ✅ | ||
| static void bar(void) | ✅ | ✅ | ||
| int baz(void) | ✅ | ✅ | ||
| inline int oof(int) | ✅ | ✅ |
Notes:
*_PARTIAL_MODULErequires at least one addition via*_PARTIAL_CONFIG(see next section).*_PARTIAL_ALL_MODULEwith no subtractions adds every module function to base set of functions.- Each module can appear in at most one
TEST_PARTIAL_*_MODULEandMOCK_PARTIAL_*_MODULEmacro within a given test file. Two same-named modules in different directories are two modules, and each may have its own pair.
Partials function list configuration macros
TEST_PARTIAL_CONFIG and MOCK_PARTIAL_CONFIG refine the base set of functions
filtered by the corresponding _MODULE macro. Both _CONFIG macros require at
least one function name argument beyond the module name.
Note that no quotation marks are needed.
Similar to the convenion in Ceedling‘s paths: and files: YAML configuration
sections, each function name argument is treated as an addition or a
subtraction depending on an optional prefix character:
| Prefix | Meaning |
|---|---|
(none) or + |
Add this function to the Partial ( <function> or +<function>) |
- |
Exclude this function from the Partial ( -<function>) |
TEST_PARTIAL_CONFIG(module, +func1, +func2, -func3)
MOCK_PARTIAL_CONFIG(module, func1, func2, -func3)
Addition & subtraction rules by mode
| Macro | Filter | Subtraction target | Addition target |
|---|---|---|---|
_PUBLIC_MODULE |
Public | Public functions only | Private functions |
_PRIVATE_MODULE |
Private | Private functions only | Public functions |
_MODULE |
Accumulate | Forbidden | Any function (one required) |
_ALL_MODULE |
Deduct | Any function | Forbidden |
TEST_ / MOCK_ Partials exclusion
Any function explicitly added on one side via a Partial _CONFIG macro is
automatically removed from the complementary function list (if it exists).
This prevents the same function from accidentally appearing both in a Partial
implementation and Partial mock, which would produce a duplicate symbol linker
error.
// `_InternalHelper()` added to the test side
// while automatically removed from the mock side.
#include TEST_PARTIAL_PRIVATE_MODULE(mymodule)
TEST_PARTIAL_CONFIG(mymodule, _InternalHelper)
// `_InternalHelper()` will NOT appear in the mock.
#include MOCK_PARTIAL_PUBLIC_MODULE(mymodule)
Partials configuration examples
Test a specific private function; Mock everything else
#include "ceedling.h"
#include TEST_PARTIAL_MODULE(sensor) // Starts empty
TEST_PARTIAL_CONFIG(sensor, _ConvertRaw) // Add exactly this one function
#include MOCK_PARTIAL_ALL_MODULE(sensor) // Starts with all functions
// `_ConvertRaw()` is automatically excluded from the Partial mock
Test all functions except one; Mock nothing
#include "ceedling.h"
#include TEST_PARTIAL_ALL_MODULE(sensor) // All functions
TEST_PARTIAL_CONFIG(sensor, -Sensor_Init) // Subtract one
Test public functions plus one private helper; Mock selected private functions
#include "ceedling.h"
// Test: Start with all public functions
// and add one private function
#include TEST_PARTIAL_PUBLIC_MODULE(sensor)
TEST_PARTIAL_CONFIG(sensor, _ConvertRaw)
// Mock: Start with no functions
// and add a private function
#include MOCK_PARTIAL_MODULE(sensor)
MOCK_PARTIAL_CONFIG(sensor, _ReadIOValue)