You've already forked pgbackrest
mirror of
https://github.com/pgbackrest/pgbackrest.git
synced 2025-07-07 00:35:37 +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:
@ -214,6 +214,7 @@ sub coverageValidateAndGenerate
|
|||||||
my $oyTestRun = shift;
|
my $oyTestRun = shift;
|
||||||
my $oStorage = shift;
|
my $oStorage = shift;
|
||||||
my $bCoverageSummary = shift;
|
my $bCoverageSummary = shift;
|
||||||
|
my $strWorkPath = shift;
|
||||||
my $strWorkTmpPath = shift;
|
my $strWorkTmpPath = shift;
|
||||||
my $strTestResultCoveragePath = shift . '/coverage';
|
my $strTestResultCoveragePath = shift . '/coverage';
|
||||||
my $strTestResultSummaryPath = shift;
|
my $strTestResultSummaryPath = shift;
|
||||||
@ -285,7 +286,7 @@ sub coverageValidateAndGenerate
|
|||||||
{
|
{
|
||||||
executeTest(
|
executeTest(
|
||||||
"genhtml ${strLCovFile} --config-file=${strTestResultCoveragePath}/raw/lcov.conf" .
|
"genhtml ${strLCovFile} --config-file=${strTestResultCoveragePath}/raw/lcov.conf" .
|
||||||
" --prefix=${strWorkTmpPath}/repo" .
|
" --prefix=${strWorkPath}/repo" .
|
||||||
" --output-directory=${strTestResultCoveragePath}/lcov");
|
" --output-directory=${strTestResultCoveragePath}/lcov");
|
||||||
|
|
||||||
foreach my $strCodeModule (sort(keys(%{$hCoverageActual})))
|
foreach my $strCodeModule (sort(keys(%{$hCoverageActual})))
|
||||||
@ -335,7 +336,7 @@ sub coverageValidateAndGenerate
|
|||||||
}
|
}
|
||||||
|
|
||||||
coverageGenerate(
|
coverageGenerate(
|
||||||
$oStorage, "${strWorkTmpPath}/repo", "${strTestResultCoveragePath}/raw", "${strTestResultCoveragePath}/coverage.html");
|
$oStorage, "${strWorkPath}/repo", "${strTestResultCoveragePath}/raw", "${strTestResultCoveragePath}/coverage.html");
|
||||||
|
|
||||||
if ($bCoverageSummary)
|
if ($bCoverageSummary)
|
||||||
{
|
{
|
||||||
@ -358,6 +359,29 @@ push @EXPORT, qw(coverageValidateAndGenerate);
|
|||||||
####################################################################################################################################
|
####################################################################################################################################
|
||||||
# Generate a C coverage report
|
# 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
|
sub coverageGenerate
|
||||||
{
|
{
|
||||||
my $oStorage = shift;
|
my $oStorage = shift;
|
||||||
@ -397,6 +421,12 @@ sub coverageGenerate
|
|||||||
# to create valid, disambiguos links.
|
# to create valid, disambiguos links.
|
||||||
$rhCoverage->{$strFile}{anchor} = sha1_hex(rand(16));
|
$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
|
# Check branch coverage
|
||||||
elsif ($strLine =~ /^BRDA\:/)
|
elsif ($strLine =~ /^BRDA\:/)
|
||||||
{
|
{
|
||||||
@ -408,7 +438,6 @@ sub coverageGenerate
|
|||||||
}
|
}
|
||||||
|
|
||||||
my $strBranchLine = sprintf("%09d", $stryData[0]);
|
my $strBranchLine = sprintf("%09d", $stryData[0]);
|
||||||
$rhCoverage->{$strFile}{line}{$strBranchLine}{branch}{covered} = true;
|
|
||||||
|
|
||||||
if ($iBranchLine != $stryData[0])
|
if ($iBranchLine != $stryData[0])
|
||||||
{
|
{
|
||||||
@ -436,9 +465,10 @@ sub coverageGenerate
|
|||||||
$rhCoverage->{$strFile}{line}{$strBranchLine}{branch}{$iBranchIdx}{$iBranchPart} =
|
$rhCoverage->{$strFile}{line}{$strBranchLine}{branch}{$iBranchIdx}{$iBranchPart} =
|
||||||
$stryData[3] eq '-' || $stryData[3] eq '0' ? false : true;
|
$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})
|
if (!$rhCoverage->{$strFile}{line}{$strBranchLine}{branch}{$iBranchIdx}{$iBranchPart})
|
||||||
{
|
{
|
||||||
$rhCoverage->{$strFile}{line}{$strBranchLine}{branch}{covered} = false;
|
coverageGenerateFunction($rhCoverage->{$strFile}, $strBranchLine)->{covered} = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,69 +482,76 @@ sub coverageGenerate
|
|||||||
confess &log(ERROR, "'${strLine}' should have two fields");
|
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')
|
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})))
|
foreach my $strFile (sort(keys(%{$rhCoverage})))
|
||||||
{
|
{
|
||||||
my $bCovered = true;
|
my $bFileCovered = true;
|
||||||
|
|
||||||
foreach my $iLine (sort(keys(%{$rhCoverage->{$strFile}{line}})))
|
# Proceed if there is some coverage data
|
||||||
{
|
|
||||||
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})))
|
|
||||||
{
|
|
||||||
if (defined($rhCoverage->{$strFile}{line}))
|
if (defined($rhCoverage->{$strFile}{line}))
|
||||||
{
|
{
|
||||||
my $strC = ${$oStorage->get($strFile)};
|
my @stryC = split("\n", ${$oStorage->get($strFile)});
|
||||||
my @stryC = split("\n", $strC);
|
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
|
my $iLine = sprintf("%09d", $iLineIdx + 1);
|
||||||
for (my $iLineIdx = $iLine; $iLineIdx >= 0; $iLineIdx--)
|
|
||||||
|
# If not in an uncovered function see if this line is the start of an uncovered function
|
||||||
|
if (!$bInUncoveredFunction)
|
||||||
{
|
{
|
||||||
if (!defined($rhCoverage->{$strFile}{line}{sprintf("%09d", $iLineIdx)}))
|
$bInUncoveredFunction =
|
||||||
|
defined($rhCoverage->{$strFile}{function}{$iLine}) && !$rhCoverage->{$strFile}{function}{$iLine}{covered};
|
||||||
|
|
||||||
|
# If any function is uncovered then the file is uncovered
|
||||||
|
if ($bInUncoveredFunction)
|
||||||
{
|
{
|
||||||
$rhCoverage->{$strFile}{line}{sprintf("%09d", $iLineIdx)} = undef;
|
$bFileCovered = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
last if ($stryC[$iLineIdx - 1] =~ '^\/\*');
|
# If not in an uncovered function remove coverage
|
||||||
|
if (!$bInUncoveredFunction)
|
||||||
|
{
|
||||||
|
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}{$iLine} = undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run forward to the end of the function
|
# Stop processing at the function end brace. This depends on the file being formated correctly, but worst case
|
||||||
for (my $iLineIdx = $iLine; $iLineIdx < @stryC; $iLineIdx++)
|
# is we run on a display the entire file rather than just uncovered functions.
|
||||||
|
if ($stryC[$iLineIdx] =~ '^\}')
|
||||||
{
|
{
|
||||||
if (!defined($rhCoverage->{$strFile}{line}{sprintf("%09d", $iLineIdx)}))
|
$bInUncoveredFunction = false;
|
||||||
{
|
}
|
||||||
$rhCoverage->{$strFile}{line}{sprintf("%09d", $iLineIdx)} = undef;
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
last if ($stryC[$iLineIdx - 1] eq '}');
|
# Remove coverage info when file is fully covered
|
||||||
}
|
if ($bFileCovered)
|
||||||
}
|
{
|
||||||
|
delete($rhCoverage->{$strFile}{line});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -709,31 +746,33 @@ sub coverageGenerate
|
|||||||
foreach my $strFile (sort(keys(%{$rhCoverage})))
|
foreach my $strFile (sort(keys(%{$rhCoverage})))
|
||||||
{
|
{
|
||||||
my $oRow = $oTable->addNew(HTML_TR, 'list-table-row-' . (defined($rhCoverage->{$strFile}{line}) ? 'uncovered' : 'covered'));
|
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
|
# Link only created when file is uncovered
|
||||||
if (defined($rhCoverage->{$strFile}{line}))
|
if (defined($rhCoverage->{$strFile}{line}))
|
||||||
{
|
{
|
||||||
$oRow->addNew(HTML_TD, 'list-table-row-file')->addNew(
|
$oRow->addNew(HTML_TD, 'list-table-row-file')->addNew(
|
||||||
HTML_A, undef,
|
HTML_A, undef, {strContent => $strFileDisplay, strRef => '#' . $rhCoverage->{$strFile}{anchor}});
|
||||||
{strContent => substr($strFile, length($strBasePath) + 1), strRef => '#' . $rhCoverage->{$strFile}{anchor}});
|
|
||||||
}
|
}
|
||||||
# Else just show the file name
|
# Else just show the file name
|
||||||
else
|
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
|
# Report on files that are missing coverage
|
||||||
foreach my $strFile (sort(keys(%{$rhCoverage})))
|
foreach my $strFile (sort(keys(%{$rhCoverage})))
|
||||||
{
|
{
|
||||||
|
my $strFileDisplay = substr($strFile, length($strBasePath) + 1);
|
||||||
|
|
||||||
if (defined($rhCoverage->{$strFile}{line}))
|
if (defined($rhCoverage->{$strFile}{line}))
|
||||||
{
|
{
|
||||||
# Anchor only created when file is uncovered
|
# Anchor only created when file is uncovered
|
||||||
$oHtml->bodyGet()->addNew(HTML_A, undef, {strId => $rhCoverage->{$strFile}{anchor}});
|
$oHtml->bodyGet()->addNew(HTML_A, undef, {strId => $rhCoverage->{$strFile}{anchor}});
|
||||||
|
|
||||||
# Report table caption, i.e. the uncovered file name
|
# 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
|
# Build the file report table
|
||||||
$oTable = $oHtml->bodyGet()->addNew(HTML_TABLE, 'report-table');
|
$oTable = $oHtml->bodyGet()->addNew(HTML_TABLE, 'report-table');
|
||||||
@ -763,12 +802,12 @@ sub coverageGenerate
|
|||||||
my $strBranch;
|
my $strBranch;
|
||||||
|
|
||||||
# Show missing branch coverage
|
# Show missing branch coverage
|
||||||
|
my $bBranchCovered = true;
|
||||||
|
|
||||||
if (defined($rhCoverage->{$strFile}{line}{$strLine}{branch}))
|
if (defined($rhCoverage->{$strFile}{line}{$strLine}{branch}))
|
||||||
{
|
{
|
||||||
foreach my $iBranch (sort(keys(%{$rhCoverage->{$strFile}{line}{$strLine}{branch}})))
|
foreach my $iBranch (sort(keys(%{$rhCoverage->{$strFile}{line}{$strLine}{branch}})))
|
||||||
{
|
{
|
||||||
next if $iBranch eq 'covered';
|
|
||||||
|
|
||||||
$strBranch .= '[';
|
$strBranch .= '[';
|
||||||
|
|
||||||
my $bBranchPartFirst = true;
|
my $bBranchPartFirst = true;
|
||||||
@ -780,7 +819,15 @@ sub coverageGenerate
|
|||||||
$strBranch .= ' ';
|
$strBranch .= ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
$strBranch .= $rhCoverage->{$strFile}{line}{$strLine}{branch}{$iBranch}{$iBranchPart} ? '+' : '-';
|
if ($rhCoverage->{$strFile}{line}{$strLine}{branch}{$iBranch}{$iBranchPart})
|
||||||
|
{
|
||||||
|
$strBranch .= '+';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$strBranch .= '-';
|
||||||
|
$bBranchCovered = false;
|
||||||
|
}
|
||||||
|
|
||||||
$bBranchPartFirst = false;
|
$bBranchPartFirst = false;
|
||||||
}
|
}
|
||||||
@ -790,19 +837,12 @@ sub coverageGenerate
|
|||||||
}
|
}
|
||||||
|
|
||||||
$oRow->addNew(
|
$oRow->addNew(
|
||||||
HTML_TD,
|
HTML_TD, 'report-table-row-branch' . (!$bBranchCovered ? '-uncovered' : ''), {strContent => $strBranch});
|
||||||
'report-table-row-branch' .
|
|
||||||
(defined($strBranch) && !$rhCoverage->{$strFile}{line}{$strLine}{branch}{covered} ? '-uncovered' : ''),
|
|
||||||
{strContent => $strBranch});
|
|
||||||
|
|
||||||
# Color code based on coverage
|
# 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(
|
$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]});
|
{bPre => true, strContent => $stryC[$strLine - 1]});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1149,8 +1149,8 @@ eval
|
|||||||
if (vmCoverageC($strVm) && !$bNoCoverage && !$bDryRun && $iTestFail == 0)
|
if (vmCoverageC($strVm) && !$bNoCoverage && !$bDryRun && $iTestFail == 0)
|
||||||
{
|
{
|
||||||
$iUncoveredCodeModuleTotal = coverageValidateAndGenerate(
|
$iUncoveredCodeModuleTotal = coverageValidateAndGenerate(
|
||||||
$oyTestRun, $oStorageBackRest, $bCoverageSummary, "${strTestPath}/temp", "${strBackRestBase}/test/result",
|
$oyTestRun, $oStorageBackRest, $bCoverageSummary, $strTestPath, "${strTestPath}/temp",
|
||||||
"${strBackRestBase}/doc/xml/auto");
|
"${strBackRestBase}/test/result", "${strBackRestBase}/doc/xml/auto");
|
||||||
}
|
}
|
||||||
|
|
||||||
# Print test info and exit
|
# Print test info and exit
|
||||||
|
Reference in New Issue
Block a user