|
||||||||||
Embedded Software Unit Testing with CeedlingBy Bhumi Shah, eInfochips Unit testing is a technique of breaking the code in small units of the entire code. These units can be verified to check the behaviour of a specific aspect of the software. One of the major challenges involved in unit testing of embedded software is that the code interacts with the hardware peripherals. In mostcases, hardware cannot be accessed during unit tests. Keeping hardware interaction as thin as possible helps in testing most of the code by dividing it into small pieces. These pieces can then be independently tested without hardware interaction. Ceedling[1] is one of the best automation frameworks available for Embedded C software unit testing. It works as a build system and provides functionality to mock source code and execute tests. Ceedling build system is made up of Rakefiles in Ruby language, which is similar to Makefiles. Ceedling contains three main utilities (Unity, CMock and CException), which individually contribute to Ceedling functionality. Unity (Unit Test Environment) is a test framework written in C language. It contains a single source file, two header files and helper tools to generate test runners (Auto generated test files for each test). Unity provides different assertion statements to verify tests for datatypes and its attributes, such as bit-size, hex-value, signed-unsigned types, pointers, memory assertions and many more.[5][3] CMock[2] is a tool to generate mock functions from C source header files. It generates mocks for each function and puts these into new mock files that aregenerated at run-time. It takes the provided CMock configurations into project configuration file (project.yml) and source header files. Based on both of these it generates five different types of mock functions for each functions of the module. These five mock functions are (Expect, Array, Callback, Cexception and Ignore). Below are the samples of the mock functions. void DoesSomething_ExpectAndReturn(int a, int b, int toReturn); void DoesSomething_ExpectAndThrow(int a, int b, EXCEPTION_T error); void DoesSomething_StubWithCallback(CMOCK_DoesSomething_CALLBACK YourCallback); void DoesSomething_IgnoreAndReturn(int toReturn); These mock functions are generated at runtime into runner files and can be used to unit test different scenarios as suggested by the function name(e,g,:, ExpectAndReturn can be used to check return value of the functions, ExpectAndThrow can be used to check the exception from the function and so on. Mostly the syntax for mock functions follows as: Return_Type SourceFunctionName_MockFuntionality(args); These mock files are compiled and linked along with test module code. CException is an exception library, which provides exception-handling functionalities. Directory structure of a ceedling project contains source, test, and build directories along with a top-level configuration file (project.yml). This configuration file contains tags and field values for paths, environments, libraries, CMock, Project and many more required/optional settings. For reference, please see the Project.yml. This file is auto-generated at the time of project creation and requires further updation as per project configurations Project.yml: Unit Test Code Format: Ceedling defines a specified format for parsing and execution of test code. It contains header files (framework, mock module, and source headers), setUp and tearDown functions along with test functions. setUp function is called at the start of each test whereas, tearDown function is called at the end of each test. To identify test code and source code files, Ceedling framework uses “test_file_prefix” attribute defined in project.yml file. All test code files must start with “test_file_prefix”. e.g. in the project.yml file above, the “test_file_prefix” is defined with “_test“, hence each test code files in this project must start with “_test“. Figure1 defines the unit test example file. It has included “unity.h(Unity framework) and “mock_bar.h”(Mocked functions) file. Here inclusion of “mock_bar.h” says that the “bar.h” is the source file and it needs to be mocked for testing. Now, CMock searches for the “bar.h” file in the source code directory. On locating the file, CMockgenerates “mock_bar.h” file with mock functions for each of the function available in the header file. It also creates runner files and puts these into the build directory with test_<testfilename>_runner.c containing the main function and other CMock, unity functions. Figure 1: Unit Test Code Template Ceedling also provides functionality to generate the source coverage report. It requires the gcov and gvcovr module package to do this. These modules need to be configured into the Project.yml file to generate a report in HTML or XML file format. This helps in discovering any dead code or source code, which is not tested. Step-By-Step guide to using Ceedling:
Examples: This is a basic demo application composed and unit testing has been performed using Ceedling. The source files appear below: Point.c Display.c Point.h Display.h This contains two source modules point and display, where point module uses the functionality of the display module by calling its function. As shown in point.c, it contains two source code functions MakePoint()and DrawPoint(). Here MakePoint() assigns the argument values to the structure variable whereas DrawPoint() calls the Draw_Int() function of the display module which is into display.c file. point.h and display.h header files contains the definitions for the source functions. Now, Let’s write a code to test MakePoint() and DrawPoint() point module functionality. The unit test code above is written to test the Point module functionality. The display module is mocked to test point module functionality, hence “point.h” and “mock_display.h” have been included. This test code does not require any initialization/de-initialization so the setUp and TearDown functions are empty. There are two test functions written to unit test code. test_Makepoint_creates_new_point( ) uses the unity assertion functions to check that the Makepoint() is called with integer values and that correct values are assigned to the structure variables. Here it calls the MakePoint(2,5) as known inputs at line 15 of Test_point.c file. As shown in point.c line 6-8, structure pt’s variables are assigned with input values provided. Now when it checks for pt’s x and y variables values at lines 16-17 (test_point.c), the test is passed.. Another test function test_MakePoint_Draws_Both_of_its_Coordinates() is created to test that the DrawPoint() function calls the Draw_Int() function from the display unit with expected values. This function calls the MakePoint() function with known values (3,4) and calls DrawPoint() function with structure pt. To test the functionality that DrawPoint(pt) calls the Draw_Int() with x and y value, unity assertion Expect is used as shown in line 23-24 of Test_point.c. As seen in point.c line 12, DrawPoint() calls the Draw_Int() function from display.c with structure variable as the argument. This test will pass as DrawPoint() calls the Draw_Int() with expected values. The result window will be as shown below:
Now let us change the unit test code for the point module to see the behaviour of the test framework and its results. This test (given below) is used to check the sequence of the DrawInt() calls. As shown in the test, test_point.c is changed and Draw_Int_Expect() values have been changed to 4 and 3 instead of 3 and 4 at line 23-24. As per source code implementation of DrawPoint(), it calls Draw_Int() with x value first and then y value. This fails the unit test by saying that the expected and actual values do not match.
Result: Now, Let’s take a case where DrawPoint() is not called as shown in the unit code snippet below. In this case, Draw_Int() is been expected to be called twice but it will not be called and thus the unit test will fail. Refer to the snapshot below for the error message.
Result: Considerations/Limitations: Here is a list of considerations/limitations to keep in mind while working with Ceedling.
Hope you find this article helpful in setting up a unit testing environment using Ceedling, while creating Embedded C software/firmware programming. Happy bug free coding. References
If you wish to download a copy of this white paper, click here
|
Home | Feedback | Register | Site Map |
All material on this site Copyright © 2017 Design And Reuse S.A. All rights reserved. |