1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-01-30 05:39:12 +02:00

Allow C or Perl coverage to run on more than one VM.

C or Perl coverage tests can now be run on any VM provided a recent enough version of Devel::Cover or lcov is available.

For now, leave u18 as the only VM to run coverage tests due to some issues with older versions of lcov.
This commit is contained in:
David Steele 2018-09-15 13:27:06 -04:00
parent 31cdd9d20b
commit f0ed89f21f
6 changed files with 153 additions and 92 deletions

View File

@ -105,6 +105,10 @@
<p>Make Valgrind return an error even when a non-fatal issue is detected. Update some minor issues discovered in the tests as a result.</p>
</release-item>
<release-item>
<p>Allow C or Perl coverage to run on more than one VM.</p>
</release-item>
<release-item>
<p>Don't perform valgrind when requested.</p>
</release-item>

View File

@ -421,6 +421,11 @@ sub containerBuild
{
$strScript .= ' perl-JSON-PP';
}
if (vmCoverageC($strOS))
{
$strScript .= ' lcov';
}
}
else
{
@ -439,9 +444,15 @@ sub containerBuild
{
$strScript .= ' libperl5.14';
}
elsif ($strOS eq VM_U18)
if (vmLintC($strOS))
{
$strScript .= ' clang-6.0 clang-tools-6.0 lcov';
$strScript .= ' clang-6.0 clang-tools-6.0';
}
if (vmCoverageC($strOS))
{
$strScript .= ' lcov';
}
}
@ -580,7 +591,7 @@ sub containerBuild
$strCopy = undef;
my $strPkgDevelCover = packageDevelCover($oVm->{$strOS}{&VM_ARCH});
my $bPkgDevelCoverBuild = vmCoverage($strOS) && !$oStorageDocker->exists("test/package/${strOS}-${strPkgDevelCover}");
my $bPkgDevelCoverBuild = vmCoveragePerl($strOS) && !$oStorageDocker->exists("test/package/${strOS}-${strPkgDevelCover}");
$strScript = sectionHeader() .
"# Create test user\n" .
@ -599,7 +610,7 @@ sub containerBuild
{
$strScript .= sectionHeader() .
"# Install Devel::Cover package source & build\n" .
" git clone https://anonscm.debian.org/git/pkg-perl/packages/libdevel-cover-perl.git" .
" git clone https://salsa.debian.org/perl-team/modules/packages/libdevel-cover-perl.git" .
" /root/libdevel-cover-perl && \\\n" .
" cd /root/libdevel-cover-perl && \\\n" .
" git checkout debian/" . LIB_COVER_VERSION . " && \\\n" .
@ -669,7 +680,7 @@ sub containerBuild
$strImageParent = containerRepo() . ":${strOS}-base";
$strImage = "${strOS}-test";
if (vmCoverage($strOS))
if (vmCoveragePerl($strOS))
{
$oStorageDocker->copy(
"test/package/${strOS}-${strPkgDevelCover}", "test/.vagrant/docker/${strOS}-${strPkgDevelCover}");

View File

@ -247,7 +247,7 @@ sub run
$strCommand =
($self->{oTest}->{&TEST_CONTAINER} ? 'docker exec -i -u ' . TEST_USER . " ${strImage} " : '') .
testRunExe(
vmCoverage($self->{oTest}->{&TEST_VM}), undef, abs_path($0), dirname($self->{strCoveragePath}),
vmCoverageC($self->{oTest}->{&TEST_VM}), undef, abs_path($0), dirname($self->{strCoveragePath}),
$self->{strBackRestBase}, $self->{oTest}->{&TEST_MODULE}, $self->{oTest}->{&TEST_NAME}) .
" --test-path=${strVmTestPath}" .
" --vm=$self->{oTest}->{&TEST_VM}" .
@ -375,7 +375,7 @@ sub run
# " -Wpedantic \\\n" : '') .
" -Wformat=2 -Wformat-nonliteral -Wstrict-prototypes -Wpointer-arith -Wvla \\\n" .
" `perl -MExtUtils::Embed -e ccopts`\n" .
"LDFLAGS=-lcrypto -lz" . (vmCoverage($self->{oTest}->{&TEST_VM}) && $self->{bCoverageUnit} ? " -lgcov" : '') .
"LDFLAGS=-lcrypto -lz" . (vmCoverageC($self->{oTest}->{&TEST_VM}) && $self->{bCoverageUnit} ? " -lgcov" : '') .
(vmWithBackTrace($self->{oTest}->{&TEST_VM}) && $self->{bBackTrace} ? ' -lbacktrace' : '') .
" `perl -MExtUtils::Embed -e ldopts`\n" .
'TESTFLAGS=' . ($self->{oTest}->{&TEST_DEBUG_UNIT_SUPPRESS} ? '' : "-DDEBUG_UNIT") .
@ -391,7 +391,7 @@ sub run
"\n" .
"test.o: test.c\n" .
"\t\$(CC) \$(CFLAGS) \$(TESTFLAGS) -O0" .
(vmCoverage($self->{oTest}->{&TEST_VM}) && $self->{bCoverageUnit} ?
(vmCoverageC($self->{oTest}->{&TEST_VM}) && $self->{bCoverageUnit} ?
' -fprofile-arcs -ftest-coverage' : '') .
" -c test.c\n" .
"\n" .
@ -467,7 +467,7 @@ sub end
}
# If C code generate coverage info
if ($iExitStatus == 0 && $self->{oTest}->{&TEST_C} && vmCoverage($self->{oTest}->{&TEST_VM}) && $self->{bCoverageUnit})
if ($iExitStatus == 0 && $self->{oTest}->{&TEST_C} && vmCoverageC($self->{oTest}->{&TEST_VM}) && $self->{bCoverageUnit})
{
# Generate a list of files to cover
my $hTestCoverage =
@ -512,7 +512,6 @@ sub end
my $strLCovTotal = $self->{strBackRestBase} . "/test/.vagrant/code/all.lcov";
executeTest(
'docker exec -i -u ' . TEST_USER . " ${strImage} " .
"${strLCovExe} --extract=${strLCovOut} */${strModuleName}.c --o=${strLCovOutTmp}");
# Combine with prior run if there was one
@ -523,7 +522,6 @@ sub end
$self->{oStorageTest}->put($strLCovOutTmp, $strCoverage);
executeTest(
'docker exec -i -u ' . TEST_USER . " ${strImage} " .
"${strLCovExe} --add-tracefile=${strLCovOutTmp} --add-tracefile=${strLCovFile} --o=${strLCovOutTmp}");
}
@ -564,7 +562,6 @@ sub end
if ($self->{oStorageTest}->exists($strLCovTotal))
{
executeTest(
'docker exec -i -u ' . TEST_USER . " ${strImage} " .
"${strLCovExe} --add-tracefile=${strLCovFile} --add-tracefile=${strLCovTotal} --o=${strLCovTotal}");
}
else

View File

@ -606,7 +606,7 @@ sub archBits {return vmArchBits(shift->{strVm})}
sub backrestExe {return shift->{strBackRestExe}}
sub backrestUser {return shift->{strBackRestUser}}
sub basePath {return shift->{strBasePath}}
sub coverage {vmCoverage(shift->{strVm})}
sub coverage {vmCoveragePerl(shift->{strVm})}
sub dataPath {return shift->basePath() . '/test/data'}
sub doCleanup {return shift->{bCleanup}}
sub doExpect {return shift->{bExpect}}

View File

@ -29,10 +29,16 @@ use constant VMDEF_DEBUG_INTEGRATION => 'debug-in
push @EXPORT, qw(VMDEF_DEBUG_INTEGRATION);
use constant VM_CONTROL_MASTER => 'control-master';
push @EXPORT, qw(VM_CONTROL_MASTER);
# Will coverage testing be run for C?
use constant VMDEF_COVERAGE_C => 'coverage-c';
# Will coverage testing be run for Perl?
use constant VMDEF_COVERAGE_PERL => 'coverage-perl';
use constant VM_DEPRECATED => 'deprecated';
push @EXPORT, qw(VM_DEPRECATED);
use constant VM_IMAGE => 'image';
push @EXPORT, qw(VM_IMAGE);
# Will static code analysis be run for C?
use constant VMDEF_LINT_C => 'lint-c';
use constant VM_OS => 'os';
push @EXPORT, qw(VM_OS);
use constant VM_OS_BASE => 'os-base';
@ -103,9 +109,6 @@ use constant VM_EXPECT => VM_CO7;
use constant VM_HOST_DEFAULT => VM_U18;
push @EXPORT, qw(VM_HOST_DEFAULT);
# Defines the VM that will do coverage testing
use constant VM_COVERAGE => VM_U18;
# Lists valid VMs
use constant VM_LIST => (VM_U18, VM_CO6, VM_CO7, VM_U12);
push @EXPORT, qw(VM_LIST);
@ -334,6 +337,9 @@ my $oyVm =
&VM_OS_REPO => 'bionic',
&VM_IMAGE => 'ubuntu:18.04',
&VM_ARCH => VM_ARCH_AMD64,
&VMDEF_COVERAGE_C => true,
&VMDEF_COVERAGE_PERL => true,
&VMDEF_LINT_C => true,
&VMDEF_PGSQL_BIN => '/usr/lib/postgresql/{[version]}/bin',
&VMDEF_PERL_ARCH_PATH => '/usr/local/lib/x86_64-linux-gnu/perl/5.26.1',
@ -374,13 +380,19 @@ foreach my $strVm (sort(keys(%{$oyVm})))
foreach my $strPgVersion (versionSupport())
{
my $strVmPgVersionRun;
my $strVmCoverage;
my $bVmCoveragePerl = false;
my $bVmCoverageC = false;
foreach my $strVm (VM_LIST)
{
if ($strVm eq VM_COVERAGE)
if (vmCoverageC($strVm))
{
$strVmCoverage = $strVm;
$bVmCoverageC = true;
}
if (vmCoveragePerl($strVm))
{
$bVmCoveragePerl = true;
}
foreach my $strVmPgVersion (@{$oyVm->{$strVm}{&VM_DB_TEST}})
@ -399,9 +411,14 @@ foreach my $strPgVersion (versionSupport())
my $strErrorSuffix = 'is not configured to run on a default vm';
if (!defined($strVmCoverage))
if (!$bVmCoverageC)
{
confess &log(ASSERT, 'vm designated for coverage testing (' . VM_COVERAGE . ") ${strErrorSuffix}");
confess &log(ASSERT, "C coverage ${strErrorSuffix}");
}
if (!$bVmCoveragePerl)
{
confess &log(ASSERT, "Perl coverage ${strErrorSuffix}");
}
if (!defined($strVmPgVersionRun))
@ -434,16 +451,40 @@ sub vmBaseTest
push @EXPORT, qw(vmBaseTest);
####################################################################################################################################
# vmCoverage
# vmCoverageC
####################################################################################################################################
sub vmCoverage
sub vmCoverageC
{
my $strVm = shift;
return ($strVm eq VM_COVERAGE ? true : false)
return $oyVm->{$strVm}{&VMDEF_COVERAGE_C} ? true : false;
}
push @EXPORT, qw(vmCoverage);
push @EXPORT, qw(vmCoverageC);
####################################################################################################################################
# vmCoveragePerl
####################################################################################################################################
sub vmCoveragePerl
{
my $strVm = shift;
return $oyVm->{$strVm}{&VMDEF_COVERAGE_PERL} ? true : false;
}
push @EXPORT, qw(vmCoveragePerl);
####################################################################################################################################
# vmLintC
####################################################################################################################################
sub vmLintC
{
my $strVm = shift;
return $oyVm->{$strVm}{&VMDEF_LINT_C} ? true : false;
}
push @EXPORT, qw(vmLintC);
####################################################################################################################################
# Get vm architecture bits

View File

@ -321,7 +321,7 @@ eval
{
confess &log(ERROR, "select a single Debian-based VM for coverage testing");
}
elsif (!vmCoverage($strVm))
elsif (!vmCoveragePerl($strVm))
{
confess &log(ERROR, "only Debian-based VMs can be used for coverage testing");
}
@ -587,7 +587,7 @@ eval
executeTest("rm -rf ${strBackRestBase}/test/coverage/perl/*");
# Copy C code for coverage tests
if (vmCoverage($strVm) && !$bDryRun)
if (vmCoverageC($strVm) && !$bDryRun)
{
$oStorageTest->pathCreate("${strCodePath}/test", {strMode => '0770', bIgnoreExists => true, bCreateParent => true});
@ -721,7 +721,7 @@ eval
" --include=" . join('/*** --include=', @stryBinSrcPath) . '/*** --exclude=*' .
" ${strBackRestBase}/ ${strBinPath}/${strBuildVM}");
if (vmCoverage($strVm) && !$bNoLint)
if (vmLintC($strVm) && !$bNoLint)
{
&log(INFO, " clang static analyzer ${strBuildVM} (${strBuildPath})");
}
@ -734,7 +734,7 @@ eval
executeTest(
'docker exec -i test-build' .
(vmCoverage($strVm) && !$bNoLint ? ' scan-build-6.0' : '') .
(vmLintC($strVm) && !$bNoLint ? ' scan-build-6.0' : '') .
" make --silent --directory ${strBuildPath} CEXTRA=${strCExtra} LDEXTRA=${strLdExtra} ${strCDebug}",
{bShowOutputAsync => $bLogDetail});
@ -1158,83 +1158,86 @@ eval
#---------------------------------------------------------------------------------------------------------------------------
my $iUncoveredCodeModuleTotal = 0;
if (vmCoverage($strVm) && !$bNoCoverage && !$bDryRun)
if ((vmCoverageC($strVm) || vmCoveragePerl($strVm)) && !$bNoCoverage && !$bDryRun && $iTestFail == 0)
{
&log(INFO, 'writing coverage report');
executeTest("cp -rp ${strCoveragePath} ${strCoveragePath}_temp");
executeTest(
"cd ${strCoveragePath}_temp && " .
LIB_COVER_EXE . " -report json -outputdir ${strBackRestBase}/test/coverage/perl ${strCoveragePath}_temp",
{bSuppressStdErr => true});
executeTest("sudo rm -rf ${strCoveragePath}_temp");
executeTest("sudo cp -rp ${strCoveragePath} ${strCoveragePath}_temp");
executeTest(
"cd ${strCoveragePath}_temp && " .
LIB_COVER_EXE . " -outputdir ${strBackRestBase}/test/coverage/perl ${strCoveragePath}_temp",
{bSuppressStdErr => true});
executeTest("sudo rm -rf ${strCoveragePath}_temp");
# Determine which modules were covered (only check coverage if all tests were successful)
#-----------------------------------------------------------------------------------------------------------------------
if ($iTestFail == 0)
my $hModuleTest; # Everything that was run
# Build a hash of all modules, tests, and runs that were executed
foreach my $hTestRun (@{$oyTestRun})
{
my $hModuleTest; # Everything that was run
# Get coverage for the module
my $strModule = $hTestRun->{&TEST_MODULE};
my $hModule = testDefModule($strModule);
# Build a hash of all modules, tests, and runs that were executed
foreach my $hTestRun (@{$oyTestRun})
# 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)
{
# Get coverage for the module
my $strModule = $hTestRun->{&TEST_MODULE};
my $hModule = testDefModule($strModule);
$hModuleTest->{$strModule}{$strTest} = true;
}
}
# Get coverage for the test
my $strTest = $hTestRun->{&TEST_NAME};
my $hTest = testDefModuleTest($strModule, $strTest);
# Now compare against code modules that should have full coverage
my $hCoverageList = testDefCoverageList();
my $hCoverageType = testDefCoverageType();
my $hCoverageActual;
# If no tests are listed it means all of them were run
if (@{$hTestRun->{&TEST_RUN}} == 0)
foreach my $strCodeModule (sort(keys(%{$hCoverageList})))
{
if (@{$hCoverageList->{$strCodeModule}} > 0)
{
my $iCoverageTotal = 0;
foreach my $hTest (@{$hCoverageList->{$strCodeModule}})
{
$hModuleTest->{$strModule}{$strTest} = true;
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 Perl coverage report
#-----------------------------------------------------------------------------------------------------------------------
if (vmCoveragePerl($strVm))
{
&log(INFO, 'writing Perl coverage report');
executeTest("cp -rp ${strCoveragePath} ${strCoveragePath}_temp");
executeTest(
"cd ${strCoveragePath}_temp && " .
LIB_COVER_EXE . " -report json -outputdir ${strBackRestBase}/test/coverage/perl ${strCoveragePath}_temp",
{bSuppressStdErr => true});
executeTest("sudo rm -rf ${strCoveragePath}_temp");
executeTest("sudo cp -rp ${strCoveragePath} ${strCoveragePath}_temp");
executeTest(
"cd ${strCoveragePath}_temp && " .
LIB_COVER_EXE . " -outputdir ${strBackRestBase}/test/coverage/perl ${strCoveragePath}_temp",
{bSuppressStdErr => true});
executeTest("sudo rm -rf ${strCoveragePath}_temp");
# Load the results of coverage testing from JSON
my $oJSON = JSON::PP->new()->allow_nonref();
my $hCoverageResult = $oJSON->decode(${$oStorageBackRest->get('test/coverage/perl/cover.json')});
# 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');
}
foreach my $strCodeModule (sort(keys(%{$hCoverageActual})))
{
# If the first char of the module is lower case then it's a c module
@ -1308,9 +1311,14 @@ eval
}
}
}
}
# Generate C coverage report
#---------------------------------------------------------------------------------------------------------------------------
if (vmCoverageC($strVm))
{
&log(INFO, 'writing C coverage report');
# Generate C coverage with lcov
#---------------------------------------------------------------------------------------------------------------------------
my $strLCovFile = "${strBackRestBase}/test/.vagrant/code/all.lcov";
if ($oStorageBackRest->exists($strLCovFile))