After failing to get coverage with-cmake I set up a minimalistic project to see if I can get coverage working that way. It's derived from using-gtest-without-cmake
It has a src
folder with a header and source file in it.
QuickMaths.hpp :
#include <cstdint>
using size_t = std::size_t;
size_t
multiply(size_t a, size_t b);
inline size_t
add(size_t a, size_t b)
{
return a b;
}
class QuickMaths
{
public:
size_t operator*() const;
friend QuickMaths operator (QuickMaths const&, QuickMaths const&);
QuickMaths(size_t x);
private:
size_t x;
};
QuickMaths.cpp:
#include "QuickMaths.hpp"
size_t
multiply(size_t a, size_t b)
{
return a * b;
}
size_t
QuickMaths::operator*() const
{
return x;
}
QuickMaths::QuickMaths(size_t x)
: x(x)
{}
QuickMaths
operator (QuickMaths const& a, QuickMaths const& b)
{
return a.x b.x;
}
And a test folder with QuickMaths.cpp :
#include <gtest/gtest.h>
#include <QuickMaths.hpp>
TEST(AddTest, shouldAdd)
{
EXPECT_EQ(add(1UL, 1UL), 2UL);
}
TEST(MultiplyTest, shouldMultiply)
{
EXPECT_EQ(multiply(2UL, 4UL), 8UL);
}
TEST(QuickMathTest, haveValue)
{
auto v = QuickMaths{ 4UL };
EXPECT_EQ(*v, 4UL);
}
and main.cpp :
#include <gtest/gtest.h>
int
main(int argc, char** argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
- I create a
build
folder andcd
into it, then compile usingg --coverage -O0 ../src/QuickMaths.cpp ../test/*.cpp -I../src/ -pthread -lgtest -lgtest_main -lgcov
- I run
./a.out
=> output shows tests being run and passing
Lastly I run gcovr -r ../ .
and get the following output:
------------------------------------------------------------------------------
GCC Code Coverage Report
Directory: ../
------------------------------------------------------------------------------
File Lines Exec Cover Missing
------------------------------------------------------------------------------
src/QuickMaths.hpp 2 0 0% 9,11
test/QuickMaths.cpp 7 0 0% 5,7,10,12,15,17-18
test/main.cpp 3 3 100%
------------------------------------------------------------------------------
TOTAL 12 3 25%
------------------------------------------------------------------------------
So it's visible that the gtest setup situated in main is being picked up, but the test cases themselves as well as the code from the src
directory is not picked up as executed.
CodePudding user response:
This error occurs because you are linking multiple files with the same name.
There are two clues to the problem:
When running the test, you will see a warning such as the following:
libgcov profiling error:REDACTED/a-QuickMaths.gcda:overwriting an existing profile data with a different timestamp
The coverage report lists three files:
src/QuickMaths.hpp
test/QuickMaths.cpp
test/main.cpp
But one file is missing entirely:
src/QuickMaths.cpp
What has happened?
When your tests shut down, raw coverage data is written by the test process into .gcda files. The name of these files depends on the compilation unit.
Looking into your build dir, we see the following data (.gcda) and notes (.gcno) files:
build |-- a-QuickMaths.gcda |-- a-QuickMaths.gcno |-- a-main.gcda |-- a-main.gcno `-- a.out
You have provided a total of three files to your compilation command
- Your command is
g ... ../src/QuickMaths.cpp ../test/*.cpp ...
. - The three files are
src/QuickMaths.cpp
,test/QuickMaths.cpp
,test/main.cpp
- Your command is
The autogenerated names for the compilation units seems to only consider the input file's basename, ignoring the directory. Since two files have the same basename, the same name for the compilation unit
a-QuickMaths
is used.Since there's a name clash for the compilation unit, there is also a conflict for the coverage data file names.
The result is corrupted coverage data.
The solution is to compile each compilation unit separately and linking them afterwards. You must give each compilation unit a distinct name, possibly using multiple subdirectories. For example:
set -euo pipefail
# compile foo/bar.cpp -> build/foo/bar.o
for source in src/*.cpp test/*.cpp; do
mkdir -p build/"$(dirname "$source")"
cd build
g --coverage -O0 -pthread -I../src -c -o "${source%.cpp}".o ../"${source}"
cd -
done
# link the tests
cd build;
g --coverage -pthread -o testcase src/*.o test/*.o -lgtest
cd -
# run the test
cd build
./testcase
cd -
tree build # show directory structure
# run gcovr
cd build
gcovr -r ..
cd -
In this example, the build directory would look like:
build
|-- src
| |-- QuickMaths.gcda
| |-- QuickMaths.gcno
| `-- QuickMaths.o
|-- test
| |-- QuickMaths.gcda
| |-- QuickMaths.gcno
| |-- QuickMaths.o
| |-- main.gcda
| |-- main.gcno
| `-- main.o
`-- testcase
And the coverage report is as expected:
------------------------------------------------------------------------------
GCC Code Coverage Report
Directory: ..
------------------------------------------------------------------------------
File Lines Exec Cover Missing
------------------------------------------------------------------------------
src/QuickMaths.cpp 9 7 77% 20,22
src/QuickMaths.hpp 2 2 100%
test/QuickMaths.cpp 10 10 100%
test/main.cpp 3 3 100%
------------------------------------------------------------------------------
TOTAL 24 22 91%
------------------------------------------------------------------------------
Additional notes:
- You're providing a
main()
but are also linking-lgtest_main
. This is unnecessary. - The
--coverage
flag already includes-lgcov
.