One important activity we shall not miss is unit testing, a “must” on software development, saves a lot of time debugging errors in advance and make our life easier by far. There several frameworks we can use out there to test our code but we gonna choose Ceedling since it was created explicitly to cover embedded needs. besides is relatively easy to use well documented and supported, for instance down below you can find very useful links to completed the information you will find here
Useful links:
Remember we start with the assumption you have installed the tools from here https://embedded-house.ghost.io/the-way-we-work/
For Linux Users
We need to install ruby first
$ sudo pacman -S ruby
Then install the ceedling gem
$ gem install ceedling
$ gem update
open the shell configuration file
$ code ~/.zshrc
or
$ code ~/.bashrc
and type at the bottom, save and reset your terminal
export PATH=$PATH:~/.local/share/gem/ruby/3.0.0/bin
# Escape square brackets by default (workaraund for ceedling)
unsetopt nomatch
For Windows users
To install it first install ruby using choco
$ choco install ruby
Now add the path to to ruby bin directory to environment variable Path, you can use power shell too
$ $addPath = 'C:\tools\ruby31\bin'
close the power shell window and open it again to type
$ gem install ceedling
Your first test case
We will assume a fresh new project from the template we usually use. At folder app lets create a custom driver with some dummy functions to perform a simple unit testing.
app/dummy.h
#ifndef __DUMMY_H__
#define __DUMMY_H__
uint32_t sum( uint32_t a, uint32_t b );
#endif // __DUMMY_H__
app/dummy.c
#include <stdint.h>
#include "dummy.h"
uint32_t sum( uint32_t a, uint32_t b )
{
return a + b;
}
Create a folder called test and make a new file test_dummy.c to place the unit testing functions. By rule of thump name your test files with the name of the file to test with the prefix test_ you can change the prefix in Ceedling options but this is the option by default.
#include "unity.h"
#include "dummy.h"
void setUp(void)
{
}
void tearDown(void)
{
}
/*test case. by default all your test cases shall be start with test_
but as will see later this can be override*/
void test__sum__two_integers(void)
{
/*run the function under test with known paramters and catch the return value
in variable res*/
uint32_t res = sum( 2, 3 );
/*the following macro validates the result, first parameter is the expected result,
second one is the actual result from our function under test, and finally the thirtd one
is just a message to display in case of error*/
TEST_ASSERT_EQUAL_MESSAGE( 5, res, "2 + 3 = 5" );
/*if you don't want to display messages on errors you can use the macro
TEST_ASSERT_EQUAL( 5, res )*/
}
In order to run Ceedling with the test cases created we need a configuration file called project.yml in the root of your project
:project:
:build_root: Build/ceedling/ # Directory where ceedling will place its output
:release_build: TRUE
:paths:
:test:
- test/** # directory where the unit testing are
:source:
- app/** # directory where the functions to test are
Run the tests to see a beautiful result with no failures
$ ceedling test:all
Test 'test_dummy.c'
-------------------
Compiling dummy.c...
Linking test_dummy.out...
Running test_dummy.out...
test_dummy.c:12:test__sum__two_integers:PASS
-----------------------
1 Tests 0 Failures 0 Ignored
OK
But let’s force a failure, lets assume 2 + 3 = 6 (for some magic reason) just to make and example, the expected result will be in this case 6, adding a new test case.
void test__sum__error_result(void)
{
uint32_t res = sum( 2, 3 );
/*for simplicity reasons we assume 2+3=6 that is why the expected result is 6,
but our function will return 5, failing the test in turn*/
TEST_ASSERT_EQUAL_MESSAGE( 6, res, "2 + 3 = 6" );
}
run the tests to see one test passes and the other fails and also display the message as an extra information
$ ceedling test:all
Test 'test_dummy.c'
-------------------
Running test_dummy.out...
test_dummy.c:12:test__sum__two_integers:PASS
test_dummy.c:23:test__sum__error_result:FAIL: Expected 6 Was 5. 2 + 3 = 6
-----------------------
2 Tests 1 Failures 0 Ignored
FAIL
Maybe Ceedling will throw some warning like these ones
C:/tools/ruby31/lib/ruby/gems/3.1.0/gems/ceedling-0.31.1/vendor/unity/auto/generate_test_runner.rb:344: warning: Passing safe_level with the 2nd argument of ERB.new is deprecated. Do not use it, and specify other arguments as keyword arguments.
C:/tools/ruby31/lib/ruby/gems/3.1.0/gems/ceedling-0.31.1/vendor/unity/auto/generate_test_runner.rb:344: warning: Passing trim_mode with the 3rd argument of ERB.new is deprecated. Use keyword argument like ERB.new(str, trim_mode: ...) instead.
Just ignore them, this has to do with the language use by ceedling which is ruby. I assume the maintainer will fix this problem soon or later