OpenCppCoverage icon indicating copy to clipboard operation
OpenCppCoverage copied to clipboard

Piece of template code from shared library marked as uncovered while it is definitely covered

Open jpo38 opened this issue 4 years ago • 4 comments

To Reproduce Create a shared library:

coverage.h:

#pragma once

#ifdef COVERAGE_EXPORTS
#define COVERAGE_API __declspec(dllexport)
#else
#define COVERAGE_API __declspec(dllimport)
#endif

#include <iostream>

class COVERAGE_API MyClass
{
public:
    template <typename T>
    static void templatefunc(T u)
    {
        if ( u == 4 )
            std::cout << "OK" << std::endl;
        else
            std::cout << "KO" << std::endl;
    }

    static void testfunc()
    {
        templatefunc<uint8_t>( 4 );
    }
};

coverage.cpp:

#include "coverage.h"

Then, your main test program:

#include "coverage.h"

int main( int argc, char* argv[] )
{
    MyClass::testfunc();
    MyClass::templatefunc<uint8_t>( 7 );
    return 0;
}

I then compile in Debug (using Visual Studio 2019), run OpenCppCoverage 0.9.9 with no particular arguments (but export_type or sources/exclude_sources).

Output it:

[info] Start Program:
Path:"C:\\dev\\vobs_sde\\build/lib_coverage_only/win64/stg/Debug\\test_cppunit_coverage_utl.exe"
Arguments: 
Working directory: "C:\\dev\\vobs_sde\\build/lib_coverage_only/win64/stg/Debug"
Modules: Selected: * Excluded: test_cppunit_* 
Sources: Selected: C:\dev\vobs_sde Excluded: C:\dev\vobs_sde\sde\3rdparty C:\dev\vobs_sde\public\tst     C:\dev\vobs_sde\sde\code\tst C:\dev\vobs_sde\private\clinatec\tst C:\dev\vobs_sde\private\le2s\tst C:\dev\vobs_sde\private\sdetests\tst C:\dev\vobs_sde\private\valkyrie_lite\tst 
Log Level: Normal
Cover Children: 0
Aggregate by file: 1
Continue after C++ exception: 0
Optimized build support: 0
Export: html C:\dev\vobs_sde\build\lib_coverage_only\win64\stg\Debug\OpenCppCoverage\test_cppunit_coverage_utl binary C:\dev\vobs_sde\build\lib_coverage_only\win64\stg\Debug\OpenCppCoverage\test_cppunit_coverage_utl/opencpp.bin 
Input coverage: 
Unified diff: 
Excluded line regular expressions: 
Substitute pdb source paths: 
[info] Module: C:\Windows\System32\ntdll.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\kernel32.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\KernelBase.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\sysfer.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\msvcp140d.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\vcruntime140_1d.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\ucrtbased.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\vcruntime140d.dll is selected because it matches selected pattern: *
[info] Module: C:\dev\vobs_sde\build\lib_coverage_only\win64\stg\Debug\coverage.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\kernel.appcore.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\msvcrt.dll is selected because it matches selected pattern: *
[info] Module: C:\Windows\System32\rpcrt4.dll is selected because it matches selected pattern: *
[info] ----------------------------------------------------
[info] Coverage generated in Folder C:\dev\vobs_sde\build\lib_coverage_only\win64\stg\Debug\OpenCppCoverage\test_cppunit_coverage_utl
[info] ----------------------------------------------------
[info] ----------------------------------------------------
[info] Coverage binary generated in file: C:\dev\vobs_sde\build\lib_coverage_only\win64\stg\Debug\OpenCppCoverage\test_cppunit_coverage_utl/opencpp.bin
[info] ----------------------------------------------------
[info] The code coverage report is not what you expect? See the FAQ https://github.com/OpenCppCoverage/OpenCppCoverage/wiki/FAQ.

Expected behavior MyClass::templatefunc should be fully covered (if statement covered as called by testfunc() called by main, else statement covered as called directly by main). However, std::cout << "KO" << std::endl; is marked as uncovered (while program output shows OK and KO, so it's definitely covered).

Desktop (please complete the following information):

  • Version of OpenCppCoverage: 0.9.9
  • Architecture (x86/ x64): x64
  • Windows version: Windows 10

jpo38 avatar Feb 25 '21 10:02 jpo38

I realize I have the test program ( main) be part of excluded_modules list.

Looks like coverage is reported correctly if the test program is not in excluded_modules (that's due to templates being compiled on the fly). But then, test program itself is part of the report, which is not expected, I want the report for my library code only.

Is there a way to have this work as expected? (have only the shared library code in the report and have it be marked as fully covered).

jpo38 avatar Feb 25 '21 10:02 jpo38

The abstract template code in the shared library is not executed; it's only the instantiations that are, so what you have observed is the expected behaviour. One instantiation takes one path, the other is excluded from coverage so the fact that it takes the second path is not observed.

One approach to getting coverage of an instantiation, when the defining library has none, would be to have an essentially trivial shim library, from which coverage is gathered, between the unit tests and the defining one, which just does the necessary instantiation, like

class TemplateShims
{
  public:
    static void MyClass_templatefunc_uint8_t(uint8_t n)
    {
        MyClass::templatefunc<uint8_t>(n);
    }
}

and call that from the test, like

#include "coverage.h"

int main( int argc, char* argv[] )
{
    MyClass::testfunc();
    TemplateShims::MyClass_templatefunc_uint8_t( 7 );
    return 0;
}

SteveGilham avatar Sep 27 '21 19:09 SteveGilham

OK, thank you for the explanation,

Jean

jpo38 avatar Sep 28 '21 04:09 jpo38