Skip to content

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 _MODULE refers 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:

  1. Expand to a filename for the preceding #include directive.
  2. Provide a module name for Ceedling to process for the resulting Partial.
  3. 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_AT macros 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:

// Wrong
#include TEST_PARTIAL_PRIVATE_MODULE(sensors/soil/Calibration)

// Right
#include TEST_PARTIAL_PRIVATE_MODULE_AT(sensors/soil, Calibration)

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_MODULE requires at least one addition via *_PARTIAL_CONFIG (see next section).
  • *_PARTIAL_ALL_MODULE with no subtractions adds every module function to base set of functions.
  • Each module can appear in at most one TEST_PARTIAL_*_MODULE and MOCK_PARTIAL_*_MODULE macro 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.

TEST_PARTIAL_CONFIG(module, func1, func2, ...)
MOCK_PARTIAL_CONFIG(module, func1, func2, ...)

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)