Using Parasoft C/C++test With CMake for Unit Testing
The release of C/C++test 2020.1 introduces the ability to integrate C/C++test with CMake for static analysis and unit testing. This post focuses on how to use Parasoft C/C++test with CMake-based projects for unit testing.
Unit testing is more complicated to integrate into the development process than static analysis. In the end, the executable binary file must be created. This is especially problematic for large and complex projects that are composed of multiple libraries and executables.
The compile-info JSON file, which we use for the static analysis, is not enough. It doesn’t provide the important information about the structure of the project—which files are included in which binaries and what should be tested together.
Integrating Unit Testing Into Cmake-Based Projects
To enable easy integration of Parasoft C/C++test unit testing into CMake-based projects, the 2020.1 release introduces a CMake extension, which allows defining unit testing targets for C/C++test. These targets automatically generate the C/C++test project files that can be used for creating, running, and maintaining unit tests.
The intention of this extension is to enable users to define the entire infrastructure that’s required for Parasoft C/C++test unit testing together with their regular targets in the CMake configuration files.
An Example of Unit Testing on Cmake-Based Projects
Let’s see how it works using an example using the C/C++test Professional – Eclipse-based tool, which features a rich UI. Download and install the 2020.1 release, if you haven’t already:
Follow These Steps
In the main installation directory of C/C++test Professional, you can find the CMake extension with accompanying example. See the following directory: <install dir>integration/cmake.
For our experiments I recommend copying the integration/cmake folder out from the installation to your preferred location. The structure of the example is shown below:
The app folder contains the “executable” target. The modules folder with its three subfolders (mod1, mod2 and mod3) represents static libraries used to link with the executable. Additionally, the cmake folder contains the C/C++test extension to CMake. You may want to copy this folder to your real project in order to use this extension.
This simplified structure paints a picture of typical challenges teams trying to implement unit testing face: How to divide the entire project into testable chunks, representing groups of files that logically belong together.
In most cases, developers will not want to execute unit tests against one blob binary created by compilation of all the files from an entire project. Users will most likely be interested in adding the test cases for the same binary targets (executables and libraries) they have defined in the project.
How to Add Test Cases for the Same Binary Targets Defined in the Project
Before discussing the details of the configuration syntax let’s see how it works using the provided example.
1. In the top-level directory where app folder is located, create a build directory, cd into it, and call a build scripts generation, like below:
2. After CMake is done with the build scripts (makefiles) generation, call make to build the example project:
3. Start C/C++test professional (the cpptest executable in the installation folder). Select an empty workspace, use “File Menu -> Import … -> Existing Projects into Workspace” and select “root directory” to be the main folder where you copied the example. Wizard will recursively scan the selected folder and find all the projects that were automatically generated. If you see something like below, you can click “Finish”.
Eclipse will import all the projects and you will have a fresh workspace ready for your unit testing. You don’t have to do anything else to start adding and running the test cases and working to improve your code coverage.
4. In the project navigator, select all three projects, right click and use “Parasoft -> Test Using -> Builtin -> Unit Testing -> Generate Unit Tests” test configuration to automatically generate the test cases. Once tests are generated, execute “Run Unit Tests” configuration from the same location.
With these simple four steps, you prepared the project infrastructure for unit testing. The team is now all set to start crunching the test cases.
How Does It Work?
In addition to regular build targets, the CMakeLists.txt files contain unit testing targets. When generating the build scripts with cmake, C/C++test project files are also automatically generated. Later during the build process additional build-data-files, which are required by C/C++test projects, are automatically generated.
Everything that is needed to define the structure of the unit testing projects is kept inside CMakeLists.txt files. The team does not need to maintain any additional configuration files. Everything is nice and clean. The project can be checked out, build scripts and C/C++test projects can be generated, tests can be added and checked in together with the source code, and full testing can be performed in the CI.
A Closer Look
Let’s take a closer look at the C/C++test CMake extension and how it helps in defining the test projects infrastructure. Open the modules/mod1/CMakeLists.txt file. You should see something like below:
In the first six lines, you can see the regular target definition. Here we are defining a simple library target, with one source file mod1.cpp, and include files directory. This is what you will typically see in your CMake projects.
In the line 11, however, we have a new CMake function that is provided with C/C++test extension. This function allows you to define the unit testing target, which generates all required C/C++test project files. You can find the full description of this function in the integrations/cmake/README.md. In this specific example, the following is done:
- Line 11: Target name is defined. By default, it will be used as the eclipse project name
- Line 12: Compiler configuration is specified for C/C++test to be able to parse and instrument the code.
- Line 13: Source files, which should be included in the unit testing target, are added
- Line 14: Target-specific include files location is added to the unit testing target. This is the same as you do for your regular targets.
This simple target definition is enough to generate a unit testing project for Parasoft C/C++test. Project files will be generated in the location of CMakeLists.txt file, which is probably the most convenient. If you prefer, you can generate the project files outside of the source tree and avoid any merging issues. This is shown in app/CMakeLists.txt:
- Line 11: Target name is defined. By default, it will be used as the eclipse project name.
- Lines 12, 13: The C/C++test project location is specified. Here we are creating the project Outside of the source tree.
- Lines 14, 15: An eclipse-linked folder is created. You need to specify the name of the folder and the location that will be linked.
- Line 16, 17: Source files, which should be included in the unit testing target, are added
- Line 19: Three libraries are added for the unit testing target. C/C++test will use those libraries when building the test executable.
For more details, review the following files from the <install dir>/integrations/cmake directory:
Here are the important points about the extension.
- The CMake extension can be found in the C/C++test Professional installation in the following directory: <Installdir>/integrations/cmake/cmake. You may need to copy it to your project structure.
- To enable CMake with C/C++test related extension function, you will need to include the extension in your CMakeLists.txt file structure using a command like below:
- Include (cmake/cpptest.cmake)
- See the example in integration/cmake/CMakeLists.txt.
- You can define your unit testing targets (which effects in the C/C++test projects) in any way you want. You can add a unit testing project for your executables, libraries, combination of libraries, or any arbitrary collection of files.
- You need to make sure that all required header files and libraries are included in your target definition. Regular rules of CMake apply here. If included, the library or option was set at the global level. It will then be included in your unit testing target. If you have any target-specific settings, you will need to add them.
- If you are adding the unit testing target for existing binary target (like library or executable), the recommended procedure is to replicate all settings that are done for the original target. Note: You can add entire targets to the cpptest_add_executable function, which will copy all source files to the new target.
- If you don’t mind C/C++test project files being created in your source files directories, this is the simplest and recommended setup. If you like the project files to be placed somewhere outside of your source tree, you can easily do it.
- You don’t need to check in generated C/C++test project files (.project and .parasoft). They will be recreated for every new build. However, you should check in all the test cases and stubs.
Product Manager for Parasoft's embedded testing solutions, Miroslaw's specialties include C/C++, RTOSes, static code analysis, unit testing, managing software quality for safety critical applications, and software compliance to safety standards.