1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-15 01:04:37 +02:00

Move coverage code to CoverageTest module.

This code needs some work, which will be easier if it is all in one place.
This commit is contained in:
David Steele
2020-03-19 12:07:51 -04:00
parent f538da0571
commit d677b07081
3 changed files with 304 additions and 269 deletions

View File

@ -22,6 +22,12 @@ use pgBackRestDoc::Html::DocHtmlBuilder;
use pgBackRestDoc::Html::DocHtmlElement;
use pgBackRestDoc::ProjectInfo;
use pgBackRestTest::Common::ContainerTest;
use pgBackRestTest::Common::DefineTest;
use pgBackRestTest::Common::ExecuteTest;
use pgBackRestTest::Common::ListTest;
use pgBackRestTest::Common::RunTest;
####################################################################################################################################
# Generate an lcov configuration file
####################################################################################################################################
@ -66,6 +72,297 @@ sub coverageLCovConfigGenerate
push @EXPORT, qw(coverageLCovConfigGenerate);
####################################################################################################################################
# Extract coverage using gcov
####################################################################################################################################
sub coverageExtract
{
my $oStorage = shift;
my $strModule = shift;
my $strTest = shift;
my $bSummary = shift;
my $strContainerImage = shift;
my $strWorkPath = shift;
my $strWorkTmpPath = shift;
my $strWorkUnitPath = shift;
my $strTestResultCoveragePath = shift . '/coverage';
# Generate a list of files to cover
my $hTestCoverage = (testDefModuleTest($strModule, $strTest))->{&TESTDEF_COVERAGE};
my @stryCoveredModule;
foreach my $strModule (sort(keys(%{$hTestCoverage})))
{
push (@stryCoveredModule, $strModule);
}
push(@stryCoveredModule, "module/${strModule}/" . testRunName($strTest, false) . 'Test');
# Generate coverage reports for the modules
my $strLCovConf = "${strTestResultCoveragePath}/raw/lcov.conf";
coverageLCovConfigGenerate($oStorage, $strLCovConf, $bSummary);
my $strLCovExeBase = "lcov --config-file=${strLCovConf}";
my $strLCovOut = "${strWorkUnitPath}/test.lcov";
my $strLCovOutTmp = "${strWorkUnitPath}/test.tmp.lcov";
executeTest(
(defined($strContainerImage) ? 'docker exec -i -u ' . TEST_USER . " ${strContainerImage} " : '') .
"${strLCovExeBase} --capture --directory=${strWorkUnitPath} --o=${strLCovOut}");
# Generate coverage report for each module
foreach my $strCoveredModule (@stryCoveredModule)
{
my $strModuleName = testRunName($strCoveredModule, false);
my $strModuleOutName = $strModuleName;
my $bTest = false;
if ($strModuleOutName =~ /^module/mg)
{
$strModuleOutName =~ s/^module/test/mg;
$bTest = true;
}
# Disable branch coverage for test files
my $strLCovExe = $strLCovExeBase;
if ($bTest)
{
$strLCovExe .= ' --rc lcov_branch_coverage=0';
}
# Generate lcov reports
my $strModulePath =
"${strWorkPath}/repo/" .
(${strModuleOutName} =~ /^test\// ?
'test/src/module/' . substr(${strModuleOutName}, 5) : "src/${strModuleOutName}");
my $strLCovFile = "${strTestResultCoveragePath}/raw/${strModuleOutName}.lcov";
my $strLCovTotal = "${strWorkTmpPath}/all.lcov";
executeTest(
"${strLCovExe} --extract=${strLCovOut} */${strModuleName}.c --o=${strLCovOutTmp}");
# Combine with prior run if there was one
if ($oStorage->exists($strLCovFile))
{
my $strCoverage = ${$oStorage->get($strLCovOutTmp)};
$strCoverage =~ s/^SF\:.*$/SF:$strModulePath\.c/mg;
$oStorage->put($strLCovOutTmp, $strCoverage);
executeTest(
"${strLCovExe} --add-tracefile=${strLCovOutTmp} --add-tracefile=${strLCovFile} --o=${strLCovOutTmp}");
}
# Update source file
my $strCoverage = ${$oStorage->get($strLCovOutTmp)};
if (defined($strCoverage))
{
if (!$bTest && $hTestCoverage->{$strCoveredModule} eq TESTDEF_COVERAGE_NOCODE)
{
confess &log(ERROR, "module '${strCoveredModule}' is marked 'no code' but has code");
}
# Get coverage info
my $iTotalLines = (split(':', ($strCoverage =~ m/^LF:.*$/mg)[0]))[1] + 0;
my $iCoveredLines = (split(':', ($strCoverage =~ m/^LH:.*$/mg)[0]))[1] + 0;
my $iTotalBranches = 0;
my $iCoveredBranches = 0;
if ($strCoverage =~ /^BRF\:/mg && $strCoverage =~ /^BRH\:/mg)
{
# If this isn't here the statements below fail -- huh?
my @match = $strCoverage =~ m/^BRF\:.*$/mg;
$iTotalBranches = (split(':', ($strCoverage =~ m/^BRF:.*$/mg)[0]))[1] + 0;
$iCoveredBranches = (split(':', ($strCoverage =~ m/^BRH:.*$/mg)[0]))[1] + 0;
}
# Report coverage if this is not a test or if the test does not have complete coverage
if (!$bTest || $iTotalLines != $iCoveredLines || $iTotalBranches != $iCoveredBranches)
{
# Fix source file name
$strCoverage =~ s/^SF\:.*$/SF:$strModulePath\.c/mg;
$oStorage->put($oStorage->openWrite($strLCovFile, {bPathCreate => true}), $strCoverage);
if ($oStorage->exists($strLCovTotal))
{
executeTest("${strLCovExe} --add-tracefile=${strLCovFile} --add-tracefile=${strLCovTotal} --o=${strLCovTotal}");
}
else
{
$oStorage->copy($strLCovFile, $strLCovTotal)
}
}
else
{
$oStorage->remove($strLCovFile);
}
}
else
{
if ($hTestCoverage->{$strCoveredModule} ne TESTDEF_COVERAGE_NOCODE)
{
confess &log(ERROR, "module '${strCoveredModule}' is marked 'code' but has no code");
}
}
}
}
push @EXPORT, qw(coverageExtract);
####################################################################################################################################
# Validate converage and generate reports
####################################################################################################################################
sub coverageValidateAndGenerate
{
my $oyTestRun = shift;
my $oStorage = shift;
my $bCoverageSummary = shift;
my $strWorkTmpPath = shift;
my $strTestResultCoveragePath = shift . '/coverage';
my $strTestResultSummaryPath = shift;
my $result = 0;
# Determine which modules were covered (only check coverage if all tests were successful)
#-----------------------------------------------------------------------------------------------------------------------
my $hModuleTest; # Everything that was run
# Build a hash of all modules, tests, and runs that were executed
foreach my $hTestRun (@{$oyTestRun})
{
# Get coverage for the module
my $strModule = $hTestRun->{&TEST_MODULE};
my $hModule = testDefModule($strModule);
# Get coverage for the test
my $strTest = $hTestRun->{&TEST_NAME};
my $hTest = testDefModuleTest($strModule, $strTest);
# If no tests are listed it means all of them were run
if (@{$hTestRun->{&TEST_RUN}} == 0)
{
$hModuleTest->{$strModule}{$strTest} = true;
}
}
# Now compare against code modules that should have full coverage
my $hCoverageList = testDefCoverageList();
my $hCoverageType = testDefCoverageType();
my $hCoverageActual;
foreach my $strCodeModule (sort(keys(%{$hCoverageList})))
{
if (@{$hCoverageList->{$strCodeModule}} > 0)
{
my $iCoverageTotal = 0;
foreach my $hTest (@{$hCoverageList->{$strCodeModule}})
{
if (!defined($hModuleTest->{$hTest->{strModule}}{$hTest->{strTest}}))
{
next;
}
$iCoverageTotal++;
}
if (@{$hCoverageList->{$strCodeModule}} == $iCoverageTotal)
{
$hCoverageActual->{testRunName($strCodeModule, false)} = $hCoverageType->{$strCodeModule};
}
}
}
if (keys(%{$hCoverageActual}) == 0)
{
&log(INFO, 'no code modules had all tests run required for coverage');
}
# Generate C coverage report
#---------------------------------------------------------------------------------------------------------------------------
&log(INFO, 'writing C coverage report');
my $strLCovFile = "${strWorkTmpPath}/all.lcov";
if ($oStorage->exists($strLCovFile))
{
executeTest(
"genhtml ${strLCovFile} --config-file=${strTestResultCoveragePath}/raw/lcov.conf" .
" --prefix=${strWorkTmpPath}/repo" .
" --output-directory=${strTestResultCoveragePath}/lcov");
foreach my $strCodeModule (sort(keys(%{$hCoverageActual})))
{
my $strCoverageFile = $strCodeModule;
$strCoverageFile =~ s/^module/test/mg;
$strCoverageFile = "${strTestResultCoveragePath}/raw/${strCoverageFile}.lcov";
my $strCoverage = $oStorage->get($oStorage->openRead($strCoverageFile, {bIgnoreMissing => true}));
if (defined($strCoverage) && defined($$strCoverage))
{
my $iTotalLines = (split(':', ($$strCoverage =~ m/^LF\:.*$/mg)[0]))[1] + 0;
my $iCoveredLines = (split(':', ($$strCoverage =~ m/^LH\:.*$/mg)[0]))[1] + 0;
my $iTotalBranches = 0;
my $iCoveredBranches = 0;
if ($$strCoverage =~ /^BRF\:/mg && $$strCoverage =~ /^BRH\:/mg)
{
# If this isn't here the statements below fail -- huh?
my @match = $$strCoverage =~ m/^BRF\:.*$/mg;
$iTotalBranches = (split(':', ($$strCoverage =~ m/^BRF\:.*$/mg)[0]))[1] + 0;
$iCoveredBranches = (split(':', ($$strCoverage =~ m/^BRH\:.*$/mg)[0]))[1] + 0;
}
# Generate detail if there is missing coverage
my $strDetail = undef;
if ($iCoveredLines != $iTotalLines)
{
$strDetail .= "$iCoveredLines/$iTotalLines lines";
}
if ($iTotalBranches != $iCoveredBranches)
{
$strDetail .= (defined($strDetail) ? ', ' : '') . "$iCoveredBranches/$iTotalBranches branches";
}
if (defined($strDetail))
{
&log(ERROR, "c module ${strCodeModule} is not fully covered ($strDetail)");
$result++;
}
}
}
coverageGenerate(
$oStorage, "${strWorkTmpPath}/repo", "${strTestResultCoveragePath}/raw", "${strTestResultCoveragePath}/coverage.html");
if ($bCoverageSummary)
{
&log(INFO, 'writing C coverage summary report');
coverageDocSummaryGenerate(
$oStorage, "${strTestResultCoveragePath}/raw", "${strTestResultSummaryPath}/metric-coverage-report.auto.xml");
}
}
else
{
executeTest("rm -rf ${strTestResultCoveragePath}/test/tesult/coverage");
}
return $result;
}
push @EXPORT, qw(coverageValidateAndGenerate);
####################################################################################################################################
# Generate a C coverage report
####################################################################################################################################