1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2026-05-22 10:15:16 +02:00

Use lcov for C unit test coverage reporting.

Switch from Devel::Cover because it would not report on branch coverage for reports converted from gcov.

Branch coverage is not complete, so for the time being errors will only be generated when statement coverage is not complete. Coverage of unit tests is not displayed in the report unless they are incomplete for either statement or branch coverage.
This commit is contained in:
David Steele
2018-03-19 23:33:28 -04:00
parent 2a3d6ecde8
commit 07f38f584a
11 changed files with 167 additions and 103 deletions
+1 -1
View File
@@ -101,7 +101,7 @@ sub process
"before_install:\n" .
" - sudo apt-get -qq update && sudo apt-get install libxml-checker-perl libdbd-pg-perl libperl-critic-perl" .
" libtemplate-perl libpod-coverage-perl libtest-differences-perl libhtml-parser-perl lintian debhelper txt2man" .
" devscripts libjson-perl libio-socket-ssl-perl libxml-libxml-perl libyaml-perl python-pip\n" .
" devscripts libjson-perl libio-socket-ssl-perl libxml-libxml-perl libyaml-perl python-pip lcov\n" .
" - |\n" .
" # Install & Configure AWS CLI\n" .
" pip install --upgrade --user awscli\n" .
@@ -361,7 +361,7 @@ sub containerBuild
}
elsif ($strOS eq VM_U16)
{
$strScript .= ' clang-5.0';
$strScript .= ' clang-5.0 lcov';
}
}
+66 -48
View File
@@ -445,73 +445,91 @@ sub end
}
# Generate coverage reports for the modules
executeTest(
'docker exec -i -u ' . TEST_USER . " ${strImage} bash -l -c '" .
"cd $self->{strGCovPath} && " .
"gcov test.c'", {bSuppressStdErr => true});
my $strLCovExe = "lcov --config-file=$self->{strBackRestBase}/test/src/lcov.conf";
my $strLCovOut = $self->{strGCovPath} . '/test.lcov';
# Mark uncoverable statements as successful
executeTest(
'docker exec -i -u ' . TEST_USER . " ${strImage} " .
"${strLCovExe} --capture --directory=$self->{strGCovPath} --o=${strLCovOut}");
# Generate coverage report for each module
foreach my $strModule (sort(keys(%{$hTestCoverage})))
{
# File that contains coverage info for the module
my $strCoverageFile = "$self->{strGCovPath}/" . testRunName(basename($strModule), false) . ".c.gcov";
my $strModuleName = testRunName($strModule, false);
my $strModuleOutName = $strModuleName;
my $bTest = false;
# If marked as no code then error if there is code
if ($hTestCoverage->{$strModule} eq TESTDEF_COVERAGE_NOCODE)
if ($strModuleOutName =~ /^module/mg)
{
if ($self->{oStorageTest}->exists($strCoverageFile))
$strModuleOutName =~ s/^module/test/mg;
$bTest = true;
}
# Generate lcov reports
my $strModulePath = $self->{strBackRestBase} . "/test/.vagrant/code/${strModuleOutName}";
my $strLCovFile = "${strModulePath}.lcov";
my $strLCovTotal = $self->{strBackRestBase} . "/test/.vagrant/code/all.lcov";
executeTest(
'docker exec -i -u ' . TEST_USER . " ${strImage} " .
"${strLCovExe} --extract=${strLCovOut} */${strModuleName}.c --o=${strLCovFile}");
# Update source file
my $strCoverage = ${$self->{oStorageTest}->get($strLCovFile)};
if (defined($strCoverage))
{
if ($hTestCoverage->{$strModule} eq TESTDEF_COVERAGE_NOCODE)
{
confess &log(ERROR, "module '${strModule}' is marked 'no code' but has code");
}
next;
}
# Get coverage info
my $iTotalLines = (split(':', ($strCoverage =~ m/^LF:.*$/mg)[0]))[1] + 0;
my $iCoveredLines = (split(':', ($strCoverage =~ m/^LH:.*$/mg)[0]))[1] + 0;
# Load the coverage file
my $strCoverage = ${$self->{oStorageTest}->get($strCoverageFile)};
my $iTotalBranches = 0;
my $iCoveredBranches = 0;
# Go line by line and update uncovered statements
my $strUpdatedCoverage;
my $bFirst = true;
foreach my $strLine (split("\n", trim($strCoverage)))
{
# Rewrite source line to the original source path
if ($bFirst)
if ($strCoverage =~ /^BRF\:$/mg && $strCoverage =~ /^BRH\:$/mg)
{
$strLine =
" -: 0:Source:" .
$self->{strBackRestBase} . ($strModule =~ /Test$/ ? '/test' : '') . "/src/" .
testRunName(${strModule}, false) . ".c";
$bFirst = false;
$iTotalBranches = (split(':', ($strCoverage =~ m/^BRF:.*$/mg)[0]))[1] + 0;
$iCoveredBranches = (split(':', ($strCoverage =~ m/^BRH:.*$/mg)[0]))[1] + 0;
}
# Else if the statement is marked uncoverable
elsif ($strLine =~ /\/\/ \{(uncoverable - [^\}]+|\+uncoverable|uncovered - [^\}]+|\+uncovered)\}\s*$/)
# Report coverage if this is not a test or if the test does not have complete coverage
if (!$bTest || $iTotalLines != $iCoveredLines || $iTotalBranches != $iCoveredBranches)
{
# Error if the statement is marked uncoverable but is in fact covered
if ($strLine !~ /^ \#\#\#\#\#/)
# Fix source file name
$strCoverage =~ s/^SF\:.*$/SF:$strModulePath\.c/mg;
# Save coverage file
$self->{oStorageTest}->put($strLCovFile, $strCoverage);
if ($self->{oStorageTest}->exists(${strLCovTotal}))
{
&log(ERROR, "line in ${strModule}.c is marked as uncoverable but is covered:\n${strLine}");
executeTest(
'docker exec -i -u ' . TEST_USER . " ${strImage} " .
"${strLCovExe} --add-tracefile=${strLCovFile} --add-tracefile=${strLCovTotal} --o=${strLCovTotal}");
}
else
{
$self->{oStorageTest}->copy($strLCovFile, $strLCovTotal)
}
# Mark the statement as covered with 0 executions
$strLine =~ s/^ \#\#\#\#\#/^ 0/;
}
# Add to updated file
$strUpdatedCoverage .= "${strLine}\n";
else
{
$self->{oStorageTest}->remove($strLCovFile);
}
}
else
{
if ($hTestCoverage->{$strModule} ne TESTDEF_COVERAGE_NOCODE)
{
confess &log(ERROR, "module '${strModule}' is marked 'code' but has no code");
}
}
# Store the updated file
$self->{oStorageTest}->put($strCoverageFile, $strUpdatedCoverage);
}
executeTest(
'docker exec -i -u ' . TEST_USER . " ${strImage} bash -l -c '" .
"cd $self->{strGCovPath} && " .
# Only generate coverage files for VMs that support coverage
(vmCoverage($self->{oTest}->{&TEST_VM}) ?
"gcov2perl -db ../cover_db " . join(' ', @stryCoveredModule) : '') . "'");
}
# Record elapsed time