Skip to content

Commit

Permalink
Enforce full branch coverage in C code.
Browse files Browse the repository at this point in the history
  • Loading branch information
dwsteele committed May 5, 2018
1 parent 0a860e0 commit 790f7c7
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 33 deletions.
2 changes: 1 addition & 1 deletion doc/xml/release.xml
Expand Up @@ -233,7 +233,7 @@
<release-test-list>
<release-feature-list>
<release-item>
<p>Use <proper>lcov</proper> for C unit test coverage reporting. Switch from <proper>Devel::Cover</proper> because it would not report on branch coverage for reports converted from <proper>gcov</proper>. 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.</p>
<p>Use <proper>lcov</proper> for C unit test coverage reporting. Switch from <proper>Devel::Cover</proper> because it would not report on branch coverage for reports converted from <proper>gcov</proper>. Incomplete branch coverage for a module now generates an error. Coverage of unit tests is not displayed in the report unless they are incomplete for either statement or branch coverage.</p>
</release-item>
</release-feature-list>

Expand Down
18 changes: 11 additions & 7 deletions test/lib/pgBackRestTest/Common/JobTest.pm
Expand Up @@ -437,12 +437,13 @@ sub end

foreach my $strModule (sort(keys(%{$hTestCoverage})))
{
# Skip modules that have no code
next if ($hTestCoverage->{$strModule} eq TESTDEF_COVERAGE_NOCODE);

push (@stryCoveredModule, testRunName(basename($strModule), false) . ".c.gcov");
push (@stryCoveredModule, $strModule);
}

push(
@stryCoveredModule,
"module/$self->{oTest}->{&TEST_MODULE}/" . testRunName($self->{oTest}->{&TEST_NAME}, false) . 'Test');

# Generate coverage reports for the modules
my $strLCovExe = "lcov --config-file=$self->{strBackRestBase}/test/src/lcov.conf";
my $strLCovOut = $self->{strGCovPath} . '/test.lcov';
Expand All @@ -452,7 +453,7 @@ sub end
"${strLCovExe} --capture --directory=$self->{strGCovPath} --o=${strLCovOut}");

# Generate coverage report for each module
foreach my $strModule (sort(keys(%{$hTestCoverage})))
foreach my $strModule (@stryCoveredModule)
{
my $strModuleName = testRunName($strModule, false);
my $strModuleOutName = $strModuleName;
Expand All @@ -478,7 +479,7 @@ sub end

if (defined($strCoverage))
{
if ($hTestCoverage->{$strModule} eq TESTDEF_COVERAGE_NOCODE)
if (!$bTest && $hTestCoverage->{$strModule} eq TESTDEF_COVERAGE_NOCODE)
{
confess &log(ERROR, "module '${strModule}' is marked 'no code' but has code");
}
Expand All @@ -490,8 +491,11 @@ sub end
my $iTotalBranches = 0;
my $iCoveredBranches = 0;

if ($strCoverage =~ /^BRF\:$/mg && $strCoverage =~ /^BRH\:$/mg)
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;
}
Expand Down
10 changes: 6 additions & 4 deletions test/src/module/archive/getTest.c
Expand Up @@ -135,8 +135,9 @@ testRun()
if (waitpid(processId, &processStatus, 0) != processId) // {uncoverable - fork() does not fail}
THROW_SYS_ERROR(AssertError, "unable to find child process"); // {uncoverable+}

if (WEXITSTATUS(processStatus) != 0)
THROW_FMT(AssertError, "perl exited with error %d", WEXITSTATUS(processStatus));
if (WEXITSTATUS(processStatus) != 0) // {uncoverable - correct error code}
THROW_FMT( // {uncoverable+}
AssertError, "perl exited with error %d", WEXITSTATUS(processStatus));
}

// -------------------------------------------------------------------------------------------------------------------------
Expand All @@ -161,8 +162,9 @@ testRun()
if (waitpid(processId, &processStatus, 0) != processId) // {uncoverable - fork() does not fail}
THROW_SYS_ERROR(AssertError, "unable to find child process"); // {uncoverable+}

if (WEXITSTATUS(processStatus) != 0)
THROW_FMT(AssertError, "perl exited with error %d", WEXITSTATUS(processStatus));
if (WEXITSTATUS(processStatus) != 0) // {uncoverable - correct error code}
THROW_FMT( // {uncoverable+}
AssertError, "perl exited with error %d", WEXITSTATUS(processStatus));
}

// Make sure the process times out when there is nothing to get
Expand Down
50 changes: 29 additions & 21 deletions test/test.pl
Expand Up @@ -1109,17 +1109,11 @@ =head1 SYNOPSIS
}
}

if (keys(%{$hCoverageActual}) > 0)
{
&log(INFO, 'test coverage for: ' . join(', ', sort(keys(%{$hCoverageActual}))));
}
else
if (keys(%{$hCoverageActual}) == 0)
{
&log(INFO, 'no code modules had all tests run required for coverage');
}

my $strPartialCoverage;

foreach my $strCodeModule (sort(keys(%{$hCoverageActual})))
{
# If the first char of the module is lower case then it's a c module
Expand Down Expand Up @@ -1191,20 +1185,9 @@ =head1 SYNOPSIS
&log(ERROR, "perl module ${strCodeModule} has 100% coverage but is not marked fully covered");
$iUncoveredCodeModuleTotal++;
}
else
{
$strPartialCoverage .=
(defined($strPartialCoverage) ? ', ' : '') . "${strCodeModule} (${iCoveragePercent}%)";
}
}
}

# If any modules had partial coverage then display them
if (defined($strPartialCoverage))
{
&log(INFO, "perl module (expected) partial coverage: ${strPartialCoverage}");
}

# Generate C coverage with lcov
#---------------------------------------------------------------------------------------------------------------------------
my $strLCovFile = "${strBackRestBase}/test/.vagrant/code/all.lcov";
Expand Down Expand Up @@ -1233,12 +1216,37 @@ =head1 SYNOPSIS

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 $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)
{
&log(ERROR, "c module ${strCodeModule} is not fully covered ($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)");
$iUncoveredCodeModuleTotal++;
}
}
Expand Down

0 comments on commit 790f7c7

Please sign in to comment.