1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-04-15 11:36:40 +02:00

More improvements to custom coverage report.

* Fix a few issues with file names being truncated introduced in 787d3fd6.

* Use function line info from the lcov file to calculate which lines to show for uncovered functions.  This is more accurate than what we were doing before and function comment headers are now excluded which reduces clutter in the report.
This commit is contained in:
David Steele 2020-03-23 12:17:34 -04:00
parent dbb1248bfb
commit f9c86b11a5
2 changed files with 101 additions and 61 deletions

View File

@ -214,6 +214,7 @@ sub coverageValidateAndGenerate
my $oyTestRun = shift;
my $oStorage = shift;
my $bCoverageSummary = shift;
my $strWorkPath = shift;
my $strWorkTmpPath = shift;
my $strTestResultCoveragePath = shift . '/coverage';
my $strTestResultSummaryPath = shift;
@ -285,7 +286,7 @@ sub coverageValidateAndGenerate
{
executeTest(
"genhtml ${strLCovFile} --config-file=${strTestResultCoveragePath}/raw/lcov.conf" .
" --prefix=${strWorkTmpPath}/repo" .
" --prefix=${strWorkPath}/repo" .
" --output-directory=${strTestResultCoveragePath}/lcov");
foreach my $strCodeModule (sort(keys(%{$hCoverageActual})))
@ -335,7 +336,7 @@ sub coverageValidateAndGenerate
}
coverageGenerate(
$oStorage, "${strWorkTmpPath}/repo", "${strTestResultCoveragePath}/raw", "${strTestResultCoveragePath}/coverage.html");
$oStorage, "${strWorkPath}/repo", "${strTestResultCoveragePath}/raw", "${strTestResultCoveragePath}/coverage.html");
if ($bCoverageSummary)
{
@ -358,6 +359,29 @@ push @EXPORT, qw(coverageValidateAndGenerate);
####################################################################################################################################
# Generate a C coverage report
####################################################################################################################################
# Helper to get the function for the current line
sub coverageGenerateFunction
{
my $rhFile = shift;
my $iCurrentLine = shift;
my $rhFunction;
foreach my $iFunctionLine (sort(keys(%{$rhFile->{function}})))
{
last if $iCurrentLine < $iFunctionLine;
$rhFunction = $rhFile->{function}{$iFunctionLine};
}
if (!defined($rhFunction))
{
confess &log(ERROR, "function not found at line ${iCurrentLine}");
}
return $rhFunction;
}
sub coverageGenerate
{
my $oStorage = shift;
@ -397,6 +421,12 @@ sub coverageGenerate
# to create valid, disambiguos links.
$rhCoverage->{$strFile}{anchor} = sha1_hex(rand(16));
}
# Mark functions as initially covered
if ($strLine =~ /^FN\:/)
{
my ($strLineBegin) = split("\,", substr($strLine, 3));
$rhCoverage->{$strFile}{function}{sprintf("%09d", $strLineBegin - 1)}{covered} = true;
}
# Check branch coverage
elsif ($strLine =~ /^BRDA\:/)
{
@ -408,7 +438,6 @@ sub coverageGenerate
}
my $strBranchLine = sprintf("%09d", $stryData[0]);
$rhCoverage->{$strFile}{line}{$strBranchLine}{branch}{covered} = true;
if ($iBranchLine != $stryData[0])
{
@ -436,9 +465,10 @@ sub coverageGenerate
$rhCoverage->{$strFile}{line}{$strBranchLine}{branch}{$iBranchIdx}{$iBranchPart} =
$stryData[3] eq '-' || $stryData[3] eq '0' ? false : true;
# If the branch is uncovered then the function is uncovered
if (!$rhCoverage->{$strFile}{line}{$strBranchLine}{branch}{$iBranchIdx}{$iBranchPart})
{
$rhCoverage->{$strFile}{line}{$strBranchLine}{branch}{covered} = false;
coverageGenerateFunction($rhCoverage->{$strFile}, $strBranchLine)->{covered} = false;
}
}
@ -452,70 +482,77 @@ sub coverageGenerate
confess &log(ERROR, "'${strLine}' should have two fields");
}
my $strBranchLine = sprintf("%09d", $stryData[0]);
my $strStatementLine = sprintf("%09d", $stryData[0]);
# If the statement is uncovered then the function is uncovered
if ($stryData[1] eq '0')
{
$rhCoverage->{$strFile}{line}{$strBranchLine}{statement} = 0;
$rhCoverage->{$strFile}{line}{$strStatementLine}{statement} = 0;
coverageGenerateFunction($rhCoverage->{$strFile}, $strStatementLine)->{covered} = false;
}
}
}
}
}
# Remove file when no lines are uncovered
# Report on the entire function if any branches/lines in the function are uncovered
foreach my $strFile (sort(keys(%{$rhCoverage})))
{
my $bCovered = true;
my $bFileCovered = true;
foreach my $iLine (sort(keys(%{$rhCoverage->{$strFile}{line}})))
{
if (defined($rhCoverage->{$strFile}{line}{$iLine}{branch}) && !$rhCoverage->{$strFile}{line}{$iLine}{branch}{covered})
{
$bCovered = false;
last;
}
}
if ($bCovered)
{
delete($rhCoverage->{$strFile}{line});
}
}
# Report on the entire function if any lines in the function are uncovered
foreach my $strFile (sort(keys(%{$rhCoverage})))
{
# Proceed if there is some coverage data
if (defined($rhCoverage->{$strFile}{line}))
{
my $strC = ${$oStorage->get($strFile)};
my @stryC = split("\n", $strC);
my @stryC = split("\n", ${$oStorage->get($strFile)});
my $bInUncoveredFunction = false;
foreach my $iLine (sort(keys(%{$rhCoverage->{$strFile}{line}})))
# Iterate every line in the C file
for (my $iLineIdx = 0; $iLineIdx < @stryC; $iLineIdx++)
{
# Run back to the beginning of the function comment
for (my $iLineIdx = $iLine; $iLineIdx >= 0; $iLineIdx--)
{
if (!defined($rhCoverage->{$strFile}{line}{sprintf("%09d", $iLineIdx)}))
{
$rhCoverage->{$strFile}{line}{sprintf("%09d", $iLineIdx)} = undef;
}
my $iLine = sprintf("%09d", $iLineIdx + 1);
last if ($stryC[$iLineIdx - 1] =~ '^\/\*');
# If not in an uncovered function see if this line is the start of an uncovered function
if (!$bInUncoveredFunction)
{
$bInUncoveredFunction =
defined($rhCoverage->{$strFile}{function}{$iLine}) && !$rhCoverage->{$strFile}{function}{$iLine}{covered};
# If any function is uncovered then the file is uncovered
if ($bInUncoveredFunction)
{
$bFileCovered = false;
}
}
# Run forward to the end of the function
for (my $iLineIdx = $iLine; $iLineIdx < @stryC; $iLineIdx++)
# If not in an uncovered function remove coverage
if (!$bInUncoveredFunction)
{
if (!defined($rhCoverage->{$strFile}{line}{sprintf("%09d", $iLineIdx)}))
delete($rhCoverage->{$strFile}{line}{$iLine});
}
# Else in an uncovered function
else
{
# If there is no coverage for this line define it so it will show up on the report
if (!defined($rhCoverage->{$strFile}{line}{$iLine}))
{
$rhCoverage->{$strFile}{line}{sprintf("%09d", $iLineIdx)} = undef;
$rhCoverage->{$strFile}{line}{$iLine} = undef;
}
last if ($stryC[$iLineIdx - 1] eq '}');
# Stop processing at the function end brace. This depends on the file being formated correctly, but worst case
# is we run on a display the entire file rather than just uncovered functions.
if ($stryC[$iLineIdx] =~ '^\}')
{
$bInUncoveredFunction = false;
}
}
}
}
# Remove coverage info when file is fully covered
if ($bFileCovered)
{
delete($rhCoverage->{$strFile}{line});
}
}
# Build html
@ -709,31 +746,33 @@ sub coverageGenerate
foreach my $strFile (sort(keys(%{$rhCoverage})))
{
my $oRow = $oTable->addNew(HTML_TR, 'list-table-row-' . (defined($rhCoverage->{$strFile}{line}) ? 'uncovered' : 'covered'));
my $strFileDisplay = substr($strFile, length($strBasePath) + 1);
# Link only created when file is uncovered
if (defined($rhCoverage->{$strFile}{line}))
{
$oRow->addNew(HTML_TD, 'list-table-row-file')->addNew(
HTML_A, undef,
{strContent => substr($strFile, length($strBasePath) + 1), strRef => '#' . $rhCoverage->{$strFile}{anchor}});
HTML_A, undef, {strContent => $strFileDisplay, strRef => '#' . $rhCoverage->{$strFile}{anchor}});
}
# Else just show the file name
else
{
$oRow->addNew(HTML_TD, 'list-table-row-file', {strContent => substr($strFile, length($strBasePath) + 1)});
$oRow->addNew(HTML_TD, 'list-table-row-file', {strContent => $strFileDisplay});
}
}
# Report on files that are missing coverage
foreach my $strFile (sort(keys(%{$rhCoverage})))
{
my $strFileDisplay = substr($strFile, length($strBasePath) + 1);
if (defined($rhCoverage->{$strFile}{line}))
{
# Anchor only created when file is uncovered
$oHtml->bodyGet()->addNew(HTML_A, undef, {strId => $rhCoverage->{$strFile}{anchor}});
# Report table caption, i.e. the uncovered file name
$oHtml->bodyGet()->addNew(HTML_DIV, 'report-table-caption', {strContent => substr($strFile, length($strBasePath) + 1)});
$oHtml->bodyGet()->addNew(HTML_DIV, 'report-table-caption', {strContent => $strFileDisplay});
# Build the file report table
$oTable = $oHtml->bodyGet()->addNew(HTML_TABLE, 'report-table');
@ -763,12 +802,12 @@ sub coverageGenerate
my $strBranch;
# Show missing branch coverage
my $bBranchCovered = true;
if (defined($rhCoverage->{$strFile}{line}{$strLine}{branch}))
{
foreach my $iBranch (sort(keys(%{$rhCoverage->{$strFile}{line}{$strLine}{branch}})))
{
next if $iBranch eq 'covered';
$strBranch .= '[';
my $bBranchPartFirst = true;
@ -780,7 +819,15 @@ sub coverageGenerate
$strBranch .= ' ';
}
$strBranch .= $rhCoverage->{$strFile}{line}{$strLine}{branch}{$iBranch}{$iBranchPart} ? '+' : '-';
if ($rhCoverage->{$strFile}{line}{$strLine}{branch}{$iBranch}{$iBranchPart})
{
$strBranch .= '+';
}
else
{
$strBranch .= '-';
$bBranchCovered = false;
}
$bBranchPartFirst = false;
}
@ -790,19 +837,12 @@ sub coverageGenerate
}
$oRow->addNew(
HTML_TD,
'report-table-row-branch' .
(defined($strBranch) && !$rhCoverage->{$strFile}{line}{$strLine}{branch}{covered} ? '-uncovered' : ''),
{strContent => $strBranch});
HTML_TD, 'report-table-row-branch' . (!$bBranchCovered ? '-uncovered' : ''), {strContent => $strBranch});
# Color code based on coverage
my $bUncovered =
(defined($rhCoverage->{$strFile}{line}{$strLine}{branch}) &&
!$rhCoverage->{$strFile}{line}{$strLine}{branch}{covered}) ||
defined($rhCoverage->{$strFile}{line}{$strLine}{statement});
$oRow->addNew(
HTML_TD, 'report-table-row-code' . ($bUncovered ? '-uncovered' : ''),
HTML_TD,
'report-table-row-code' . (defined($rhCoverage->{$strFile}{line}{$strLine}{statement}) ? '-uncovered' : ''),
{bPre => true, strContent => $stryC[$strLine - 1]});
}
}

View File

@ -1149,8 +1149,8 @@ eval
if (vmCoverageC($strVm) && !$bNoCoverage && !$bDryRun && $iTestFail == 0)
{
$iUncoveredCodeModuleTotal = coverageValidateAndGenerate(
$oyTestRun, $oStorageBackRest, $bCoverageSummary, "${strTestPath}/temp", "${strBackRestBase}/test/result",
"${strBackRestBase}/doc/xml/auto");
$oyTestRun, $oStorageBackRest, $bCoverageSummary, $strTestPath, "${strTestPath}/temp",
"${strBackRestBase}/test/result", "${strBackRestBase}/doc/xml/auto");
}
# Print test info and exit