CtrlK
BlogDocsLog inGet started
Tessl Logo

testland/ceedling-build-runner

Author and run the Ceedling build system for C unit testing - the canonical build orchestration on top of Unity (assertions) + CMock (mocks) + CException (exceptions). Covers ceedling new scaffolding, the project.yml schema (:project / :paths / :files / :defines / :flags / :tools / :test_runner / :cmock / :unity / :cexception / :gcov / :plugins), the task surface (test:all, test:{name}, test:pattern, test:path, release, clean / clobber, gcov:all, module:create, environment, dumpconfig), JUnit XML via the report_tests_* plugins, gcov integration, host vs cross-build flow, and CI wiring. CMock semantics - the generated Expect / Ignore / IgnoreArg / ReturnThruPtr / AddCallback / Stub / ExpectAndThrow family, cmock.yml :plugins, mock naming, tearDown verification, strict-vs-ignore matching - are in references/cmock.md. Use when a C project wants the ThrowTheSwitch trio bundled by one build command, or when authoring / reading CMock mocks. For the Unity assertion API see unity-test-framework-c.

74

Quality

93%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

Overview
Quality
Evals
Security
Files
name:
ceedling-build-runner
description:
Author and run the Ceedling build system for C unit testing - the canonical build orchestration on top of Unity (assertions) + CMock (mocks) + CException (exceptions). Covers ceedling new scaffolding, the project.yml schema (:project / :paths / :files / :defines / :flags / :tools / :test_runner / :cmock / :unity / :cexception / :gcov / :plugins), the task surface (test:all, test:{name}, test:pattern, test:path, release, clean / clobber, gcov:all, module:create, environment, dumpconfig), JUnit XML via the report_tests_* plugins, gcov integration, host vs cross-build flow, and CI wiring. CMock semantics - the generated Expect / Ignore / IgnoreArg / ReturnThruPtr / AddCallback / Stub / ExpectAndThrow family, cmock.yml :plugins, mock naming, tearDown verification, strict-vs-ignore matching - are in references/cmock.md. Use when a C project wants the ThrowTheSwitch trio bundled by one build command, or when authoring / reading CMock mocks. For the Unity assertion API see unity-test-framework-c.
metadata:
{"keywords":"ceedling, unity, cmock, cexception, c, embedded, rake, throwtheswitch"}

ceedling-build-runner

Overview

This skill covers the Ceedling build orchestration - the ceedling command-line tool, project.yml schema, and rake tasks, per throwtheswitch.org/ceedling. For the Unity assertion API see unity-test-framework-c; for CMock's generated mock API see references/cmock.md; for cross-target run see qemu-system-test-runner; for coverage see embedded-coverage-strategy-reference.

When to use

  • C unit-test project - Ceedling is the canonical setup for the ThrowTheSwitch stack.
  • Host-build-driven test loop (most embedded teams test on the host first, then cross-compile under qemu-system-test-runner) - Ceedling handles the host pipeline natively.
  • Coverage required - the bundled gcov plugin produces LCOV / HTML / Cobertura without external glue.
  • Mock-heavy module - CMock is integrated; no separate generator invocation needed.

If the unit-under-test is C++ instead of C, prefer googletest-embedded-arm; Ceedling does not target C++.

Authoring

Scaffolding a new project

Per the Ceedling README and the command-line reference at github.com/ThrowTheSwitch/Ceedling/.../getting-started/command-line.md:

gem install ceedling
ceedling new my-firmware --docs --local
cd my-firmware

--docs includes the documentation locally; --local vendors Unity / CMock / CException into vendor/ceedling/ so the build doesn't need network at compile time. The generated layout:

my-firmware/
  project.yml          # the schema covered below
  src/                 # production code under test
  test/                # one test_<module>.c per module
  test/support/        # shared test helpers
  build/               # generated; gitignore'd
  vendor/ceedling/     # bundled Unity + CMock + CException

project.yml

A minimal project.yml for a host test loop:

:project:
  :build_root: build/
  :use_mocks: TRUE
  :test_file_prefix: test_
:paths:
  :test:
    - test/**
  :source:
    - src/**
  :include:
    - inc/**
:plugins:
  :enabled:
    - report_tests_pretty_stdout
    - report_tests_junit_xml
    - gcov

The full schema (every top-level section, test vs release :flags, the :cmock / :cexception / :gcov blocks, per-section notes, and the plugin list) is in references/project-yml-schema.md.

Creating a module

ceedling module:create[ringbuffer]
# Creates src/ringbuffer.c, src/ringbuffer.h, test/test_ringbuffer.c

The generator emits the canonical skeleton - production header / source with includes wired, test file with setUp / tearDown / one test_ringbuffer_NeedToImplement placeholder.

Running

ceedling test:all         # Run every test_*.c
ceedling test:ringbuffer  # Run only test_ringbuffer.c
ceedling gcov:all         # Run under gcov instrumentation + generate report

ceedling test:all returns non-zero on any test failure; CI gates on the exit code. gcov:all writes build/artifacts/gcov/GcovCoverageResults.html (HtmlDetailed) or GcovCoverageCobertura.xml (Cobertura) - see embedded-coverage-strategy-reference.

The canonical CI invocation chains tasks on one command line:

ceedling clobber test:all release gcov:all
# Clean -> run tests -> build release -> produce coverage report

The full task surface (test:pattern, test:path, --test-case, release, clean / clobber, environment, dumpconfig) is in references/task-reference.md.

Parsing results

Pretty stdout output

-------------------
OVERALL TEST SUMMARY
-------------------
TESTED:  47
PASSED:  46
FAILED:   1
IGNORED:  0

Failures are reported per assertion with file:line:test:reason:

test/test_ringbuffer.c:48:test_overflow_returns_minus_one:FAIL: Expected -1 Was 0

JUnit XML

With report_tests_junit_xml enabled, ceedling test:all writes build/artifacts/test/report.xml in the canonical JUnit schema - the same one GoogleTest produces, so the same CI pipeline plugin (GitHub mikepenz/action-junit-report, GitLab JUnit, Jenkins JUnit) consumes both.

CI integration

jobs:
  ceedling-tests:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - name: Setup Ruby + Ceedling
        uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2'
      - run: gem install ceedling
      - name: Run tests + coverage
        run: ceedling clobber test:all gcov:all
      - name: Publish JUnit
        if: always()
        uses: mikepenz/action-junit-report@v4
        with:
          report_paths: 'build/artifacts/test/*.xml'
      - name: Publish coverage
        uses: codecov/codecov-action@v5
        with:
          files: build/artifacts/gcov/GcovCoverageCobertura.xml

For a cross-compile path (host build for CI, ARM build for hardware-in-loop) the recipe is: keep two project.yml files (project.yml for host, project-arm.yml for ARM) and pass --project=project-arm.yml for the ARM build. The ARM build's :tools: overrides the compiler to arm-none-eabi-gcc and the results route through qemu-system-test-runner.

Anti-patterns

Anti-patternWhy it failsFix
Editing the generated *_Runner.c filesRegenerated on every build; edits lostEdit the source test_*.c; the runner is derived
:enforce_strict_ordering: FALSE to "make tests pass"Mock-call-order bugs become invisibleKeep :enforce_strict_ordering: TRUE; fix the SUT's call shape
Commit build/ artifactsRepo bloat; merges conflict on generated files.gitignore build/ always
Skipping ceedling clobber in CIStale mocks survive header changes; ghost test failuresAlways clobber before the CI run
Mixing test compile flags with release flags--coverage in release build inflates the binaryKeep :flags: :test: separate from :flags: :release:
Naming a test file _test.c instead of test_*.cDefault :test_file_prefix: test_ won't pick it upMatch the prefix or change the project.yml setting
Using :use_mocks: FALSE then including a Mock*.hCMock isn't invoked; link fails on the mock symbolEither enable mocks globally or use Ceedling's --mocks runtime flag
Hardcoding the build directory in scripts:build_root is project.yml-driven; scripts break on renameRead it from ceedling environment

Limitations

  • Ruby dependency. Ceedling runs on Ruby; embedded teams without a Ruby toolchain have an adoption cost.
  • C only. No C++ support - for C++ pair googletest-embedded-arm with CMake.
  • Per-file flag overrides are verbose. The :flags schema's glob keys are powerful but error-prone - dumpconfig is the only reliable verifier.
  • No native parallel test execution. Each test_* runs serially. Parallelise across multiple test:path[...] jobs in CI if needed.
  • Plugin discovery is path-sensitive. Custom plugins must live under :plugins: :load_paths: in project.yml. Ceedling's error messages on missing plugins are terse.
  • gcov plugin reports are coupled to host-built coverage. For on-target coverage, write a custom plugin or feed gcovr manually after a QEMU run.

References

Cited inline. Foundational documents:

Workspace
testland
Visibility
Public
Created
Last updated
Publish Source
GitHub
Badge
testland/ceedling-build-runner badge