CPSC 480-010 Software Engineering (SE) Fall 2025

Exercise 38: Team Code Coverage Posted: Oct 23

First, as a team, review the test cases and the code to determine if you achieved 100% code coverage and whether any test cases are not unique and cover the same code. Do not spend much time on this, as you pretty much said so with your previous exercise result.

The following assumes you are in your build directory.

Generate a Code Coverage Report

The tool gcov can, when used with code compiled with the appropriate flags, produce a report that shows exactly how many times each line of code is called during a run of the program. We could use this to determine where our program is taking the most time and perhaps optimize the program. For our purposes today, we can also use it to obtain code coverage. So, we want to run the gcov program with our test program and see the code coverage of the function dotProduct() in the file dotProduct.cpp .

To produce the gcov report, do the following:

  1. Clean your build
  2. Run cmake to add the option --coverage to the flags provided to the compiler: The CMake variable CMAKE_CXX_FLAGS contains the options passed to the compiler, and CMAKE_EXE_LINKER_FLAGS contains the options passed to the linker. The CMake configuration sets them for things such as Debug and Release builds. The CMake variables CMAKE_CXX_FLAGS_INIT and CMAKE_EXE_LINKER_FLAGS_INIT are the starting values before the CMake configuration sets them.
  3. Run cmake so that make will output the commands it is executing (instead of hiding them from you): The CMake variable CMAKE_MAKE_VERBOSE controls whether make shows the commands it uses to build the program. Note: ninja will not work here. This is one exception to ninja being a drop-in replacement for make. Note that cmake caches its settings. So once you have this configured, you can run CMake again and it will still have these values. They are stored in the file CMakeCache.txt.
  4. Build the program and observe the commands. You should see the flag --coverage. It will occur on every compile and link command. Record in your notes the command for the compile up to and including the --coverage option.
  5. Turn off CMAKE_VERBOSE_MAKEFILE as it is annoying getting that much output with every make command:
  6. Run the test program. Do this only once.
  7. This run of the program should produce a file with the extension .gcda. The one you are interested in is dotProduct.cpp.gcda. CMake has an extensive directory structure for the object code, and the .gcda file is with the object code.
  8. Now that you have found the dotProduct.cpp.gcda we must run it through gcov to get the report. Carefully view the output of the program to find where dotProduct.cpp is and the line that starts with Lines executed:. You can safely rerun this command. You will then see how much code coverage your test program produced. Record in your notes this percentage. You should find a file dotProduct.cpp.gcov in your build directory.
  9. If you want to rerun the test program, you must delete the .gcda and .gcno files. A command to do this is (I strongly recommend copy and paste):

Code Coverage Results

View the file dotProduct.cpp.gcov in an editor. You will see lines such as:

Columns are separated by ":". Column 2 is the line number, and Column 3 is the source code. Column 1 is the number of times that line of code was executed, or a - if it is not an executable line (i.e., comment or whitespace). If you see a #####, that means that that line of code was never executed. So, therefore, you do not have 100% code coverage.

Automation

Correctly rerunning all of the above commands is repetitive and prone to mistakes. So let's automate it by taking the above lines and making them targets in our CMake. Add the following code at the end of your CMakeLists.txt file.

Commit this change to the repository with the commit message:

Any time you make changes to the cmake file CMakeLists.txt, rerun cmake:

Now, the sequence to run the coverage for the tests is the following:

Improving the Code Coverage

To fix this problem, you will have to change the file dotProduct.cpp. Look carefully at the lines of code that were not executed, any conditional statements that had to be true to reach that unexecuted line, and your test cases.

The change to dotProduct.cpp is to rearrange some lines. Make this change, build the program, run the test program, and make sure the tests still pass. If the tests all pass, commit the change to the file dotProduct.cpp:

Now, rerun the coverage using our new make targets. Continue making changes to your test cases until you get 100% code coverage. Record in your notes any additional test cases.

Further Automation

This sequence is more complex than it needs to be:

If the build file ran the target coverage-clean before every test, then we would not have to remember to do so. Any knowledge we can put into the build file makes everything easier to repeat. So change (do not just add this code) the test target in the file CMakeLists.txt so that it also DEPENDS on the target coverage-clean:

Since CMakeLists.txt changed, rerun cmake.

Now, the new sequence of commands is:

An even further automation is to automatically run the target test whenever you run the target coverage-report. So, running a new report is a single command. See if you can get this working.

Commit these automation changes to the repository:

Test Cases

Using the coverage, find the percentage of the code in dotProduct.cpp covered by each test case. Do this by commenting out all but one test case at a time and then running a coverage report. Record in your notes the new percentages and how they compare to what you manually calculated.