Master Code Coverage Tools: A Beginner-Friendly, In-Depth Tutorial (2025)

Master Code Coverage Tools: A Beginner-Friendly, In-Depth Tutorial (2025)

Code Coverage Tools : In this comprehensive and beginner-friendly tutorial, we will explore the world of code coverage tools, which play a crucial role in ensuring your software is thoroughly tested and reliable. Whether you’re working on embedded systems, desktop applications, or large-scale software projects, understanding code coverage is essential for improving software quality and identifying untested parts of your codebase.

By the end of this tutorial, you will have a solid understanding of how code coverage tools work and how to integrate them into your own development workflow. Whether you are a student, a developer in testing, or an embedded systems engineer, this guide will empower you to write better-tested and more robust code in 2025 and beyond.

What Is Code Coverage?

Code coverage is a metric used to measure how much of your source code has been executed (or “covered”) during testing. It helps answer questions like:

  • Have all functions been tested?
  • Have all branches or conditions been exercised?
  • Are there any unused lines of code?

In simple terms, code coverage tools help ensure your tests are doing their job.

Why Code Coverage Matters

  • ✅ Identifies untested parts of code
  • 🚨 Helps find dead code or redundant logic
  • 🔍 Improves software reliability
  • 📊 Provides quality insights for QA, DevOps, and Developers
  • 💬 Useful in code reviews and CI/CD pipelines

Types of Code Coverage Metrics

TypeDescriptionExample
Line Coverage% of lines executed90 out of 100 lines = 90%
Function Coverage% of functions called8 of 10 functions = 80%
Branch Coverage% of if/else branches executedBoth if and else parts
Condition Coverage% of boolean sub-expressions testedif (A && B) tests A and B
Path Coverage% of possible execution paths testedAll loops + conditions

Popular Code Coverage Tools

ToolLanguagePlatformUsage
gcovC/C++LinuxBuilt-in with GCC
lcovC/C++LinuxVisual frontend for gcov
gcovrC/C++Cross-platformOutputs XML/HTML
JaCoCoJavaCross-platformJava code coverage
Coverage.pyPythonCross-platformSimple and powerful
IstanbulJavaScriptNode.jsFor JS/TS projects

Tutorial: Using gcov and lcov (For C/C++ Beginners)

Let’s walk through a simple example.

Step 1: Setup a Simple C Program

// file: example.c
#include <stdio.h>

void test_function(int x) {
    if (x > 0)
        printf("Positive\n");
    else
        printf("Non-positive\n");
}

int main() {
    test_function(5);
    return 0;
}

Step 2: Compile with Coverage Flags

gcc -fprofile-arcs -ftest-coverage example.c -o example

Explanation:

  • -fprofile-arcs – Instruments code to generate branch info.
  • -ftest-coverage – Generates .gcno file for test info.

Step 3: Run the Program

./example

What happens?

  • It runs the code.
  • Creates .gcda file (stores execution count).

Step 4: Run gcov to See Coverage

gcov example.c

You’ll get output like:

File 'example.c'
Lines executed:80.00%
example.c.gcov created

Look into example.c.gcov – it has line-by-line coverage.

Step 5 (Optional): Visualize with lcov and genhtml

lcov --capture --directory . --output-file coverage.info
genhtml coverage.info --output-directory out
xdg-open out/index.html

Coverage in CI/CD (Advanced Tip)

Use tools like gcovr, lcov, or Codecov in GitHub Actions or Jenkins pipelines to:

  • Fail builds if coverage is low
  • Track coverage over time
  • Generate HTML badges for repos

Understanding Output Files

FilePurpose
.gcnoGenerated during compilation (static info)
.gcdaGenerated after running executable (runtime info)
.gcovResult from combining both (readable format)

Code Coverage vs Testing

ConceptPurpose
Unit TestsCheck if individual components work
Code CoverageChecks if tests touch all code
🧠 Best Practice: Use both together!

Common Mistakes to Avoid

  1. Thinking 100% coverage means no bugs – Not true! Code may run but still produce wrong output.
  2. Forgetting to run all test cases before measuring – Always execute full test suite first.
  3. Not cleaning old coverage data – Use gcovr -r . --delete or clean .gcda files before retesting.

Bonus: gcovr for Easy Automation

pip install gcovr
gcovr --html --html-details -o coverage.html

This tool summarizes all .gcov into HTML – very handy!

Summary

  • Code coverage tools help validate testing effectiveness.
  • For C/C++, gcov, lcov, and gcovr are most used.
  • Compile with coverage flags, execute the binary, then generate reports.
  • Visual tools help QA, CI, and reviews.
  • Code coverage != code quality but is a crucial metric.

We’ll:

  1. Write a simple C program.
  2. Compile it with code coverage flags.
  3. Run it to generate coverage data.
  4. Use gcov to view the coverage.
  5. Optionally, use lcov + genhtml to generate an HTML report.

Step-by-Step Code Coverage Example with Code

Step 1: Write the C Program (math_utils.c)

// math_utils.c
#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

int subtract(int a, int b) {
    return a - b;
}

int multiply(int a, int b) {
    return a * b;
}

int divide(int a, int b) {
    if (b == 0) {
        printf("Division by zero error!\n");
        return 0;
    }
    return a / b;
}

int main() {
    printf("Add: %d\n", add(5, 3));
    printf("Subtract: %d\n", subtract(5, 3));
    // multiply and divide not called

    return 0;
}

Step 2: Compile with Coverage Flags

gcc -fprofile-arcs -ftest-coverage math_utils.c -o math_utils

This creates extra files like math_utils.gcno (for structure data).

Step 3: Run the Program

./math_utils

Output:

Add: 8
Subtract: 2

This execution will generate math_utils.gcda (runtime data file showing which lines ran).

Step 4: Run gcov to See Coverage

gcov math_utils.c

You’ll see something like:

File 'math_utils.c'
Lines executed:60.00% of 15
Creating 'math_utils.c.gcov'

Then open the file math_utils.c.gcov:

        -:    1: // math_utils.c
        -:    2: #include <stdio.h>
    #####:    3:
        1:    4: int add(int a, int b) {
        1:    5:     return a + b;
        -:    6: }
        1:    7:
        1:    8: int subtract(int a, int b) {
        1:    9:     return a - b;
        -:   10: }
    #####:   11: int multiply(int a, int b) {
    #####:   12:     return a * b;
        -:   13: }
    #####:   14: int divide(int a, int b) {
    #####:   15:     if (b == 0) {
    #####:   16:         printf("Division by zero error!\n");
    #####:   17:         return 0;
    #####:   18:     }
    #####:   19:     return a / b;
        -:   20: }
        1:   21:
        1:   22: int main() {
        1:   23:     printf("Add: %d\n", add(5, 3));
        1:   24:     printf("Subtract: %d\n", subtract(5, 3));
        -:   25:     // multiply and divide not called
        1:   26:     return 0;
        -:   27: }

What You Learn From This

  • Lines marked #####: <line number> are never executed.
  • Functions not tested = 0% coverage.
  • You only called add() and subtract() in main() – so only those got executed.

Bonus: Generate Visual Report with lcov + genhtml

Step 1: Install lcov

sudo apt install lcov

Step 2: Collect Coverage Info

lcov --capture --directory . --output-file coverage.info

Step 3: Generate HTML Report

genhtml coverage.info --output-directory out

Step 4: Open Report

xdg-open out/index.html

You’ll see a colorful, user-friendly HTML page showing function/line/branch coverage.

Summary

✅ Code coverage tools like gcov and lcov help track which parts of your code were tested.
✅ Use flags -fprofile-arcs -ftest-coverage during compilation.
gcov gives line-by-line coverage in plain text.
lcov + genhtml gives interactive HTML reports.
✅ Helps you spot untested functions (like multiply() and divide() above).

You can also Visit other tutorials of Embedded Prep 

Special thanks to @mr-raj for contributing to this article on EmbeddedPrep

Leave a Reply

Your email address will not be published. Required fields are marked *