CPSC 480-010 Software Engineering (SE) Fall 2024

Exercise 36: Team Code Coverage Posted: Oct 24

Setup

First, as a team, review the test cases and the code and determine if you got 100% code coverage or not and if any of the 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.

Second, we are going to use cmake to generate a Makefile instead of creating one directly. That is going to make it easier for changing the build. To convert, go to GitHub and accept the pull request. Once done, all of you will have to do a git pull to get these changes into your code.

To prepare for the build:

  1. Create a subdirectory build
  2. Change to the build directory
  3. Run cmake from the build directory referencing the CMakeLists.txt in the parent directory.
  4. CMake will generate a Makefile. You can then run that.

You will build and run the program from the build directory. You will edit the files of the project in the parent directory.

The following assumes you are in your build directory.

Generate a Code Coverage Report

The tool gcov can, with code compiled with the appropriate flags, produce a report that shows precisely how many times each line of code is called in a run of the program, and from that, we can get 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 .

Note: Once you run cmake referencing the source directory, i.e., cmake .., it remembers the source directory and you can then just reference the build directory, i.e., cmake .. This is nice because your build directory might not be a subdirectory of your source, and the commands still work. However, you do have to reference the source directory, i.e., cmake .., the first time.

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 is the option passed to the compiler.
  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 will show 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. They are in the file CMakeCache.txt. This is why the previous CMAKE_CXX_FLAGS setting still holds.
  4. Build the program and observe the commands. You should see the flag --coverage. Record in your notes the command line for the compile up to the first argument after the --coverage.
  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 open 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 would run 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 the test target the CMakeLists.txt so that it 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 one 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.