Set Up an Embedded C Project with Ceedling to Work on CircleCI

Setting up a comprehensive test suite for your embedded project is crucial to its success. Embedded issues are notoriously tricky to debug, and having a test suite complete with unit and integration tests will aid massively in fixing bugs that arise.

Fortunately, Ceedling provides a framework for developing embedded projects with C, and it comes with everything you need to get automated tests up and running. This guide will run you through the steps to get CircleCI setup with Ceedling.

Creating your CircleCI Config

The first step is to create your CircleCI config file. This is the file that Circle reads to understand how to run your test suite. If you’ve never worked with Circle before, it would be helpful to go through this guide to make sure your configuration is good to go.

Let’s begin by creating a job. We’ll just call it build:

version: 2

jobs:
  build:
    docker:
      - image: circleci/ruby:2.4.2-stretch
    steps:
      - checkout

So far, all we’re doing is telling Circle to spin up a container with Ruby on it, and to checkout our code (the checkout command is built into Circle). Let’s add some actual functionality. Underneath checkout, add this code:

      - run:
          name: Installing Bundler
          command: gem install bundler
      - run:
          name: Installing Ruby Gems
          command: bundle install && bundle clean

Since Ceedling is built with Ruby and uses Bundler to run tasks, we need to make sure those are installed on our container. Now that we have all of our dependencies, we can actually start running some tests. Add this code to your config file:

      - run:
          name: Running Unit Tests
          command: bundle exec rake test:all

This is telling Circle to run the test:all command using Rake, a task runner that was installed earlier as a Ruby Gem. If your project doesn’t use Rake, then you can replace it with ceedling. The test:all command comes built in with Ceedling, and it will run your entire suite.

Continued Improvements

After wiring up your CircleCI config and your Ceedling project file, you should be able to see your test suite run whenever you push new changes! If you’re having issues or your tests aren’t appearing on the UI, make sure that you’ve followed all the steps in this guide about linking Circle up to your version control remote host.

Now let’s take a look at some of the other valuable features you can take advantage of with CircleCI. Here’s how you can set up some of most important ones.

Build a release artifact

It’s often desirous for a CI system to create a release artifact that can be distributed or deployed. The same is true for an embedded project: You might want an .axf or a .hex file, for example, to be generated once your tests have run. This is easy to do with Circle. Just add this code to the end of your configuration file:

      - run:
          name: Building Release Artifact
          command: bundle exec rake release

Similar to test:all, the release command is built into Ceedling, and it will generate a release executable. Now that we have it built, though, we need to find a way to access it. Circle allows you to do this by adding a store_artifacts step, which takes a path to a directory or file to be stored at the conclusion of your test run. You’ll want to include this code after your build step:

      - store_artifacts:
          path: build/artifacts/release

This will allow you to access your build artifacts under the “Artifacts” tab on the Circle UI when your tests run. You might need to adjust the path if you’ve defined a custom build directory, rather than Ceedling’s default.

Store and display test results

Storing a release artifact is important, but what if you also want toalso store your test results for later retrieval? Doing so has the added benefit of allowing integration with CircleCI’s Test Summary feature, and, if your plan supports it, Insights. The tricky thing here is that Circle can only parse test results written in JUnit XML format, which Ceedling doesn’t output by default. Fortunately, Ceedling comes with a built-in JUnit formatter plugin. Simply add it your project.yml file like this:

:plugins:
  :enabled:
    - junit_tests_report

Now that we’re outputting test results in the correct format, we just need to tell Circle where to find those results. By default, they’ll be stored in the build/artifacts/test directory, so we can add this code to our Circle config file. (For best results, put this step directly after your tests run, and before doing a release build.)

      - store_test_results:
          path: build/artifacts/test

That’s all there is to it! Circle should now display your test summary at the top of each job. Note that this won’t store the actual output XML files as artifacts, but you can easily do that by adding another store_artifacts step with the same path as above.

Run multiple test suites

One final trick you can do with Ceedling and Circle is to break up your tests. Previously, we were just using the test:all command, but let’s say you have multiple groups of tests that you want to run as separate test suites. (For example, you might want to run your unit and integration tests separately.) It’s easy to do this since Ceedling allows you to run tests using a test:path[insert/your/path/here] command. Simply add this code:

      - run:
          name: Running Unit Tests
          command: bundle exec rake test:path[unit]
      - run:
          name: Running Integration Tests
          command: bundle exec rake test:path[integration]
          when: always

Notice that now, rather than just running test:all, we’re running both our unit and integration test suites as separate commands. Also note the when:always line that we’ve added. The reason we included this is that Circle will cancel all of your remaining run commands as soon as the first one fails. In our case, we want to run our integration tests even when we have unit test failures, so we specify the when:always.

That should get you up and running with an automated test suite that can help keep your code bug-free and create release builds without any extra work on your part. Feel free to share in the comments any other helpful tricks you’ve found for setting up a CI system on an embedded project!