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
99%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
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.
qemu-system-test-runner) - Ceedling handles the host pipeline natively.If the unit-under-test is C++ instead of C, prefer
googletest-embedded-arm;
Ceedling does not target C++.
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 + CExceptionA 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
- gcovThe 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.
ceedling module:create[ringbuffer]
# Creates src/ringbuffer.c, src/ringbuffer.h, test/test_ringbuffer.cThe generator emits the canonical skeleton - production header /
source with includes wired, test file with setUp / tearDown /
one test_ringbuffer_NeedToImplement placeholder.
ceedling test:all # Run every test_*.c
ceedling test:ringbuffer # Run only test_ringbuffer.c
ceedling gcov:all # Run under gcov instrumentation + generate reportceedling 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 reportThe full task surface (test:pattern, test:path,
--test-case, release, clean / clobber, environment,
dumpconfig) is in
references/task-reference.md.
-------------------
OVERALL TEST SUMMARY
-------------------
TESTED: 47
PASSED: 46
FAILED: 1
IGNORED: 0Failures are reported per assertion with file:line:test:reason:
test/test_ringbuffer.c:48:test_overflow_returns_minus_one:FAIL: Expected -1 Was 0With 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.
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.xmlFor 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-pattern | Why it fails | Fix |
|---|---|---|
Editing the generated *_Runner.c files | Regenerated on every build; edits lost | Edit the source test_*.c; the runner is derived |
:enforce_strict_ordering: FALSE to "make tests pass" | Mock-call-order bugs become invisible | Keep :enforce_strict_ordering: TRUE; fix the SUT's call shape |
Commit build/ artifacts | Repo bloat; merges conflict on generated files | .gitignore build/ always |
Skipping ceedling clobber in CI | Stale mocks survive header changes; ghost test failures | Always clobber before the CI run |
| Mixing test compile flags with release flags | --coverage in release build inflates the binary | Keep :flags: :test: separate from :flags: :release: |
Naming a test file _test.c instead of test_*.c | Default :test_file_prefix: test_ won't pick it up | Match the prefix or change the project.yml setting |
Using :use_mocks: FALSE then including a Mock*.h | CMock isn't invoked; link fails on the mock symbol | Either 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 rename | Read it from ceedling environment |
googletest-embedded-arm
with CMake.:flags schema's
glob keys are powerful but error-prone - dumpconfig is the
only reliable verifier.test_* runs
serially. Parallelise across multiple test:path[...] jobs in
CI if needed.:plugins: :load_paths: in project.yml. Ceedling's
error messages on missing plugins are terse.Cited inline. Foundational documents:
unity-test-framework-c,
cmock-reference,
embedded-coverage-strategy-reference,
qemu-system-test-runner,
googletest-embedded-arm.