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 project scaffolding, the project.yml schema (:project / :paths / :files / :defines / :flags / :tools / :test_runner / :cmock / :unity / :cexception / :gcov / :plugins), the task surface (ceedling test:all, ceedling test:{name}, ceedling test:pattern, ceedling test:path, ceedling release, ceedling clean / clobber, ceedling gcov:all, ceedling module:create, ceedling environment, ceedling dumpconfig), JUnit XML output via the report_tests_pretty_stdout / report_tests_junit_xml plugins, gcov plugin integration, host vs cross-build flow, and CI wiring. Use when a C project wants the standard ThrowTheSwitch trio bundled by one build command. For the Unity assertion API see unity-test-framework-c; for CMock semantics see cmock-reference.

79

Quality

99%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

SKILL.md

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 project scaffolding, the project.yml schema (:project / :paths / :files / :defines / :flags / :tools / :test_runner / :cmock / :unity / :cexception / :gcov / :plugins), the task surface (ceedling test:all, ceedling test:{name}, ceedling test:pattern, ceedling test:path, ceedling release, ceedling clean / clobber, ceedling gcov:all, ceedling module:create, ceedling environment, ceedling dumpconfig), JUnit XML output via the report_tests_pretty_stdout / report_tests_junit_xml plugins, gcov plugin integration, host vs cross-build flow, and CI wiring. Use when a C project wants the standard ThrowTheSwitch trio bundled by one build command. For the Unity assertion API see unity-test-framework-c; for CMock semantics see cmock-reference.
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 cmock-reference; 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:

SKILL.md

tile.json