Piece of template code from shared library marked as uncovered while it is definitely covered
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
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).
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;
}
OK, thank you for the explanation,
Jean