diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fd74cf06a..40936a7cd 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -30,7 +30,7 @@ This example is based on Ubuntu 20.04, but it should work on many versions of De
pgbackrest-dev => Install development tools
```
-sudo apt-get install rsync git devscripts build-essential valgrind lcov autoconf \
+sudo apt-get install rsync git devscripts build-essential valgrind autoconf \
autoconf-archive libssl-dev zlib1g-dev libxml2-dev libpq-dev pkg-config \
libxml-checker-perl libyaml-perl libdbd-pg-perl liblz4-dev liblz4-tool \
zstd libzstd-dev bzip2 libbz2-dev libyaml-dev ccache python3-distutils meson
@@ -532,7 +532,7 @@ pgbackrest/test/test.pl --vm-out --module=command --test=check --vm=u20
```
> **NOTE:** Not all systems perform at the same speed, so if a test is timing out, try rerunning with another vm.
-Because a test run has not been specified, a coverage report will be generated and written to the local file system under the pgBackRest directory `test/result/coverage/lcov/index.html` and a file with only the highlighted code that has not been covered will be written to `test/result/coverage/coverage.html`.
+A coverage report will be generated and written to the local file system under the pgBackRest repository in `test/result/coverage.html`.
If 100 percent code coverage has not been achieved, an error message will be displayed, for example: `ERROR: [125]: c module command/check/check is not fully covered`
diff --git a/doc/src/meson.build b/doc/src/meson.build
index bcddc6427..5ad5c9f87 100644
--- a/doc/src/meson.build
+++ b/doc/src/meson.build
@@ -19,6 +19,7 @@ subdir('command/help')
# test target
####################################################################################################################################
src_doc = [
+ '../../src/build/common/json.c',
'../../src/build/common/render.c',
'../../src/build/common/string.c',
'../../src/build/common/xml.c',
@@ -38,7 +39,6 @@ src_doc = [
'../../src/common/io/fdWrite.c',
'../../src/common/lock.c',
'../../src/common/stat.c',
- '../../src/common/type/json.c',
'../../src/config/config.c',
'../../src/config/parse.c',
'command/build/build.c',
diff --git a/doc/xml/auto/metric-coverage-report.auto.xml b/doc/xml/auto/metric-coverage-report.auto.xml
index 158184c30..692eba546 100644
--- a/doc/xml/auto/metric-coverage-report.auto.xml
+++ b/doc/xml/auto/metric-coverage-report.auto.xml
@@ -71,7 +71,7 @@
command/backup50/50 (100.0%)
- 790/790 (100.0%)
+ 792/792 (100.0%)1896/1896 (100.0%)
@@ -161,9 +161,9 @@
common
- 149/149 (100.0%)
- 634/634 (100.0%)
- 1853/1853 (100.0%)
+ 147/147 (100.0%)
+ 630/630 (100.0%)
+ 1829/1829 (100.0%)
@@ -357,7 +357,7 @@
TOTAL
- 1651/1651 (100.0%)
- 10409/10410 (99.99%)
- 31143/31143 (100.0%)
-
\ No newline at end of file
+ 1649/1649 (100.0%)
+ 10407/10408 (99.99%)
+ 31119/31119 (100.0%)
+
diff --git a/doc/xml/contributing.xml b/doc/xml/contributing.xml
index 4a7aa685b..f33e86ae3 100644
--- a/doc/xml/contributing.xml
+++ b/doc/xml/contributing.xml
@@ -91,7 +91,7 @@
- apt-get install rsync git devscripts build-essential valgrind lcov autoconf
+ apt-get install rsync git devscripts build-essential valgrind autoconf
autoconf-archive libssl-dev zlib1g-dev libxml2-dev libpq-dev pkg-config
libxml-checker-perl libyaml-perl libdbd-pg-perl liblz4-dev liblz4-tool
zstd libzstd-dev bzip2 libbz2-dev libyaml-dev ccache python3-distutils meson
@@ -597,7 +597,7 @@ pgbackrest/test/test.pl --vm-out --module=command --test=check --vm=u20
Not all systems perform at the same speed, so if a test is timing out, try rerunning with another vm.
-
Because a test run has not been specified, a coverage report will be generated and written to the local file system under the directory test/result/coverage/lcov/index.html and a file with only the highlighted code that has not been covered will be written to test/result/coverage/coverage.html.
+
A coverage report will be generated and written to the local file system under the repository in test/result/coverage.html.
If 100 percent code coverage has not been achieved, an error message will be displayed, for example: ERROR: [125]: c module command/check/check is not fully covered
diff --git a/src/build/common/json.c b/src/build/common/json.c
new file mode 100644
index 000000000..eaeed52ff
--- /dev/null
+++ b/src/build/common/json.c
@@ -0,0 +1,21 @@
+/***********************************************************************************************************************************
+JSON Extensions
+***********************************************************************************************************************************/
+// Include core module
+#include "common/type/json.c"
+
+#include "build/common/json.h"
+
+/**********************************************************************************************************************************/
+bool
+jsonReadUntil(JsonRead *const this, const JsonType type)
+{
+ FUNCTION_TEST_BEGIN();
+ FUNCTION_TEST_PARAM(JSON_READ, this);
+ FUNCTION_TEST_PARAM(ENUM, type);
+ FUNCTION_TEST_END();
+
+ ASSERT(this != NULL);
+
+ FUNCTION_TEST_RETURN(BOOL, jsonReadTypeNextIgnoreComma(this) != type);
+}
diff --git a/src/build/common/json.h b/src/build/common/json.h
new file mode 100644
index 000000000..0a02733a8
--- /dev/null
+++ b/src/build/common/json.h
@@ -0,0 +1,15 @@
+/***********************************************************************************************************************************
+JSON Extensions
+***********************************************************************************************************************************/
+#ifndef BUILD_COMMON_JSON_H
+#define BUILD_COMMON_JSON_H
+
+#include "common/type/json.h"
+
+/***********************************************************************************************************************************
+Functions
+***********************************************************************************************************************************/
+// Return true unless specified type found
+bool jsonReadUntil(JsonRead *this, JsonType type);
+
+#endif
diff --git a/test/Dockerfile b/test/Dockerfile
index e88871a78..37414a408 100644
--- a/test/Dockerfile
+++ b/test/Dockerfile
@@ -12,7 +12,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
sudo vim htop jq rsync sysstat curl \
libdbd-pg-perl libxml-checker-perl libyaml-perl \
devscripts build-essential lintian git cloc txt2man debhelper libssl-dev zlib1g-dev libperl-dev libxml2-dev liblz4-dev \
- liblz4-tool libpq-dev lcov autoconf-archive zstd libzstd-dev bzip2 libbz2-dev pkg-config libyaml-dev libc6-dbg wget meson \
+ liblz4-tool libpq-dev autoconf-archive zstd libzstd-dev bzip2 libbz2-dev pkg-config libyaml-dev libc6-dbg wget meson \
ccache valgrind tzdata uncrustify libssh2-1-dev
# Install Docker
diff --git a/test/Vagrantfile b/test/Vagrantfile
index a5568426a..ada11b1c7 100644
--- a/test/Vagrantfile
+++ b/test/Vagrantfile
@@ -75,7 +75,7 @@ Vagrant.configure(2) do |config|
#-----------------------------------------------------------------------------------------------------------------------
echo 'Install Build Tools' && date
apt-get install -y devscripts build-essential lintian git cloc txt2man debhelper libssl-dev zlib1g-dev libperl-dev \
- libxml2-dev liblz4-dev liblz4-tool libpq-dev lcov autoconf-archive zstd libzstd-dev bzip2 libbz2-dev pkg-config \
+ libxml2-dev liblz4-dev liblz4-tool libpq-dev autoconf-archive zstd libzstd-dev bzip2 libbz2-dev pkg-config \
libyaml-dev libc6-dbg valgrind meson ccache uncrustify libssh2-1-dev
#-----------------------------------------------------------------------------------------------------------------------
@@ -144,7 +144,7 @@ Vagrant.configure(2) do |config|
# Basic environment to build/test pgBackRest using homebrew installed in the local user account.
#-------------------------------------------------------------------------------------------------------------------------------
# git clone --depth=1 https://github.com/Homebrew/brew ~/homebrew
- # ~/homebrew/bin/brew install -q pkg-config openssl@1.1 libpq libxml2 libyaml cpanm lcov meson
+ # ~/homebrew/bin/brew install -q pkg-config openssl@1.1 libpq libxml2 libyaml cpanm meson
# ~/homebrew/bin/cpanm --force --local-lib=~/homebrew/perl5 install YAML::XS XML::Checker::Parser
#
# export PATH="${HOME?}/homebrew/bin:$PATH"
diff --git a/test/ci.pl b/test/ci.pl
index 491b91c3f..e302848f4 100755
--- a/test/ci.pl
+++ b/test/ci.pl
@@ -185,12 +185,6 @@ eval
"gcc ccache python3-distutils git rsync zlib1g-dev libssl-dev libxml2-dev libpq-dev libyaml-dev pkg-config uncrustify" .
" libssh2-1-dev valgrind";
- # Add lcov when testing coverage
- if (vmCoverageC($strVm))
- {
- $strPackage .= " lcov";
- }
-
# Extra packages required when testing without containers
if ($strVm eq VM_NONE)
{
diff --git a/test/code-count/file-type.yaml b/test/code-count/file-type.yaml
index ec40d7d3a..14df7934b 100644
--- a/test/code-count/file-type.yaml
+++ b/test/code-count/file-type.yaml
@@ -679,6 +679,14 @@ src/build/common/exec.h:
class: build
type: c/h
+src/build/common/json.c:
+ class: build
+ type: c
+
+src/build/common/json.h:
+ class: build
+ type: c/h
+
src/build/common/regExp.c:
class: build
type: c
@@ -2507,10 +2515,6 @@ test/lib/pgBackRestTest/Common/ContainerTest.pm:
class: test/harness
type: perl
-test/lib/pgBackRestTest/Common/CoverageTest.pm:
- class: test/harness
- type: perl
-
test/lib/pgBackRestTest/Common/DbVersion.pm:
class: test/harness
type: perl
@@ -2599,6 +2603,14 @@ test/src/command/test/build.h:
class: test/harness
type: c/h
+test/src/command/test/coverage.c:
+ class: test/harness
+ type: c
+
+test/src/command/test/coverage.h:
+ class: test/harness
+ type: c/h
+
test/src/command/test/define.c:
class: test/harness
type: c
@@ -3207,6 +3219,10 @@ test/src/module/storage/sftpTest.c:
class: test/module
type: c
+test/src/module/test/coverageTest.c:
+ class: test/module
+ type: c
+
test/src/module/test/testTest.c:
class: test/module
type: c
diff --git a/test/container.yaml b/test/container.yaml
index 677b17ade..9a0e968d1 100644
--- a/test/container.yaml
+++ b/test/container.yaml
@@ -12,19 +12,10 @@
# - docker login -u pgbackrest
# - VM=XXX;DATE=YYYYMMDDX;BASE=pgbackrest/test:${VM?}-base;docker tag ${BASE?} ${BASE?}-${DATE?} && docker push ${BASE?}-${DATE?}
# **********************************************************************************************************************************
-20240524A:
+20240530A:
x86_64:
- u22: eab8001bbbe7c610453ce06adb31ebd971c61592
-
-20240518A:
- x86_64:
- f40: 5173d773cfff925d4d41bd34029e55775be23c51
-
-20240425A:
- x86_64:
- d10: fb03907abefd68fe16557b759e7e110e99eda748
- u20: c3fc7cc1956c5eb10995119deed7a21b92dd07a7
-
-20240423A:
- x86_64:
- rh7: 3ba01dc5bbc96eed48287b8e4f52054d4d7030a5
+ d10: 890b52c1c95ba3dc2d6a6535b8d9e8b273db275d
+ f40: c52b4f20349d8037d74a4a4543d2e33cd52bb3fa
+ u20: 9ac8fc879f2a7934b31d9e9df56a6a617be6cbed
+ u22: 3dc2ae79283af572ebe9d79c8c6a1086f9537233
+ rh7: bf93465740557a07914137ddc8a2156efde2646b
diff --git a/test/define.yaml b/test/define.yaml
index 365e3918a..ad7e488e5 100644
--- a/test/define.yaml
+++ b/test/define.yaml
@@ -254,7 +254,8 @@ unit:
total: 2
coverage:
- - common/type/json
+ - build/common/json
+ - common/type/json: included
# ----------------------------------------------------------------------------------------------------------------------------
- name: type-key-value
@@ -715,6 +716,13 @@ unit:
- test/command/test/define
- test/command/test/test
+ # ----------------------------------------------------------------------------------------------------------------------------
+ - name: coverage
+ total: 1
+
+ coverage:
+ - test/command/test/coverage
+
# ********************************************************************************************************************************
- name: info
diff --git a/test/lib/pgBackRestTest/Common/CodeCountTest.pm b/test/lib/pgBackRestTest/Common/CodeCountTest.pm
index e3f5b0f50..df1b14827 100644
--- a/test/lib/pgBackRestTest/Common/CodeCountTest.pm
+++ b/test/lib/pgBackRestTest/Common/CodeCountTest.pm
@@ -66,7 +66,6 @@ sub codeCountScan
$strFile =~ '^test/result/' ||
$strFile =~ '^test/scratch' ||
$strFile =~ '^test/src/valgrind\.suppress\.' ||
- $strFile eq 'test/src/lcov.conf' ||
$strFile eq 'test/uncrustify.cfg');
# Classify the source file
diff --git a/test/lib/pgBackRestTest/Common/ContainerTest.pm b/test/lib/pgBackRestTest/Common/ContainerTest.pm
index 08d7c57a0..6a5ecdf9e 100644
--- a/test/lib/pgBackRestTest/Common/ContainerTest.pm
+++ b/test/lib/pgBackRestTest/Common/ContainerTest.pm
@@ -439,12 +439,6 @@ sub containerBuild
}
}
- # If no specific version of lcov is requested then install the default package
- if (!defined($oVm->{$strOS}{&VMDEF_LCOV_VERSION}))
- {
- $strScript .= ' lcov';
- }
-
#---------------------------------------------------------------------------------------------------------------------------
$strScript .= sectionHeader() .
"# Regenerate SSH keys\n" .
@@ -476,20 +470,6 @@ sub containerBuild
" rm -rf /root/${strValgrind}";
}
- #---------------------------------------------------------------------------------------------------------------------------
- if (defined($oVm->{$strOS}{&VMDEF_LCOV_VERSION}))
- {
- my $strLCovVersion = $oVm->{$strOS}{&VMDEF_LCOV_VERSION};
- my $strLCovPath = "/root/lcov-${strLCovVersion}";
-
- $strScript .= sectionHeader() .
- "# Build lcov ${strLCovVersion}\n" .
- " wget -q -O - https://github.com/linux-test-project/lcov/releases/download/v${strLCovVersion}/" .
- "lcov-${strLCovVersion}.tar.gz | tar zx -C /root && \\\n" .
- " make -C ${strLCovPath} install && \\\n" .
- " rm -rf ${strLCovPath}";
- }
-
#---------------------------------------------------------------------------------------------------------------------------
if (!$bDeprecated)
{
diff --git a/test/lib/pgBackRestTest/Common/CoverageTest.pm b/test/lib/pgBackRestTest/Common/CoverageTest.pm
deleted file mode 100644
index 2f4aa93aa..000000000
--- a/test/lib/pgBackRestTest/Common/CoverageTest.pm
+++ /dev/null
@@ -1,1070 +0,0 @@
-####################################################################################################################################
-# Generate C Coverage Report
-####################################################################################################################################
-package pgBackRestTest::Common::CoverageTest;
-
-####################################################################################################################################
-# Perl includes
-####################################################################################################################################
-use strict;
-use warnings FATAL => qw(all);
-use Carp qw(confess);
-use English '-no_match_vars';
-
-use Digest::SHA qw(sha1_hex);
-use Exporter qw(import);
- our @EXPORT = qw();
-use File::Basename qw(dirname);
-
-use pgBackRestDoc::Common::Log;
-use pgBackRestDoc::Common::String;
-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;
-
-####################################################################################################################################
-# testRunName
-#
-# Create module/test names by upper-casing the first letter and then inserting capitals after each -.
-####################################################################################################################################
-sub testRunName
-{
- my $strName = shift;
- my $bInitCapFirst = shift;
-
- $bInitCapFirst = defined($bInitCapFirst) ? $bInitCapFirst : true;
- my $bFirst = true;
-
- my @stryName = split('\-', $strName);
- $strName = undef;
-
- foreach my $strPart (@stryName)
- {
- $strName .= ($bFirst && $bInitCapFirst) || !$bFirst ? ucfirst($strPart) : $strPart;
- $bFirst = false;
- }
-
- return $strName;
-}
-
-####################################################################################################################################
-# Generate an lcov configuration file
-####################################################################################################################################
-sub coverageLCovConfigGenerate
-{
- my $oStorage = shift;
- my $strOutFile = shift;
- my $bContainer = shift;
- my $bCoverageSummary = shift;
-
- my $strBranchFilter =
- 'OBJECT_DEFINE_[A-Z0-9_]+\(|\s{4}[A-Z][A-Z0-9_]+\([^\?]*\)|\s{4}(ASSERT|CHECK|CHECK_FMT|assert|switch\s)\(|\{\+{0,1}' .
- ($bCoverageSummary ? 'uncoverable_branch' : 'uncover(ed|able)_branch');
- my $strLineFilter =
- '\{\+{0,1}' . ($bCoverageSummary ? 'uncoverable' : '(uncover(ed|able)' . ($bContainer ? '' : '|vm_covered') . ')') . '[^_]';
-
- my $strConfig =
- "# LCOV Settings\n" .
- "\n" .
- "# Specify if branch coverage data should be collected and processed\n" .
- "lcov_branch_coverage=1\n" .
- "\n" .
- "# Specify the regular expression of lines to exclude from branch coverage\n" .
- "#\n" .
- '# OBJECT_DEFINE_[A-Z0-9_]+\( - exclude object definitions' . "\n" .
- '# \s{4}[A-Z][A-Z0-9_]+\([^\?]*\) - exclude macros that do not take a conditional parameter and are not themselves a parameter' . "\n" .
- '# ASSERT/(|CHECK/(|assert\( - exclude asserts/checks since it usually not possible to trigger both branches' . "\n" .
- '# switch \( - lcov requires default: to show complete coverage but --Wswitch-enum enforces all enum values be present' . "\n" .
- "lcov_excl_br_line=${strBranchFilter}\n" .
- "\n" .
- "# Specify the regular expression of lines to exclude\n" .
- "lcov_excl_line=${strLineFilter}\n" .
- "\n" .
- "# Coverage rate limits\n" .
- "genhtml_hi_limit = 100\n" .
- "genhtml_med_limit = 90\n" .
- "\n" .
- "# Width of line coverage field in source code view\n" .
- "genhtml_line_field_width = 9\n";
-
- # Write configuration file
- $oStorage->put($strOutFile, $strConfig);
-}
-
-push @EXPORT, qw(coverageLCovConfigGenerate);
-
-####################################################################################################################################
-# Extract coverage using gcov
-####################################################################################################################################
-sub coverageExtract
-{
- my $oStorage = shift;
- my $strModule = shift;
- my $strTest = shift;
- my $bContainer = shift;
- my $bSummary = shift;
- my $strContainerImage = shift;
- my $strWorkPath = shift;
- my $strWorkTmpPath = shift;
- my $strWorkUnitPath = shift;
- my $strTestResultCoveragePath = shift . '/coverage';
-
- # Coverage summary must be run in a container
- if ($bSummary && !$bContainer)
- {
- confess &log(ERROR, "coverage summary must be run on containers for full 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, $bContainer, $bSummary);
-
- my $strLCovExe = "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} " : '') .
- "${strLCovExe} --capture --directory=${strWorkUnitPath} --o=${strLCovOut} 2>&1");
-
- # Generate coverage report for each module
- foreach my $strCoveredModule (@stryCoveredModule)
- {
- my $strModuleName = testRunName($strCoveredModule, false);
-
- if ($strModuleName =~ /^test/mg)
- {
- $strModuleName =~ s/^test/src/mg;
- }
- elsif ($strModuleName =~ /^doc/mg)
- {
- $strModuleName =~ s/^doc/doc\/src/mg;
- }
-
- my $strModuleOutName = $strModuleName;
- my $bTest = false;
-
- if ($strModuleOutName =~ /^module/mg)
- {
- $strModuleOutName =~ s/^module/test/mg;
- $bTest = true;
- }
-
- # Generate lcov reports
- my $strModulePath = "${strWorkPath}/repo/";
-
- if (${strModuleOutName} =~ /^src\//)
- {
- $strModulePath .= 'test/src/' . substr(${strModuleOutName}, 4);
- }
- elsif (${strModuleOutName} =~ /^test\//)
- {
- $strModulePath .= 'test/src/module/' . substr(${strModuleOutName}, 5);
- }
- elsif (${strModuleOutName} =~ /^doc\//)
- {
- $strModulePath .= "${strModuleOutName}";
- }
- else
- {
- $strModulePath .= "src/${strModuleOutName}";
- }
-
- my $strLCovFile = "${strTestResultCoveragePath}/raw/${strModuleOutName}.lcov";
- my $strLCovTotal = "${strWorkTmpPath}/all.lcov";
- my $bInc = $strModuleName =~ '\.vendor$' || $strModuleName =~ '\.auto$';
- my $strModuleSourceFile = $strModulePath . '.c' . ($bInc ? '.inc' : '');
-
- executeTest(
- "${strLCovExe}" . ($bTest ? ' --rc lcov_branch_coverage=0' : '') . " --extract=${strLCovOut} *${strModuleName}.c" .
- ($bInc ? '.inc' : '') . " --o=${strLCovOutTmp}");
-
- # Combine with prior run if there was one
- if ($oStorage->exists($strLCovFile))
- {
- my $strCoverage = ${$oStorage->get($strLCovOutTmp)};
- $strCoverage =~ s/^SF\:.*$/SF:$strModuleSourceFile/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:$strModuleSourceFile/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 $bCoverageReport = shift;
- my $bCoverageSummary = shift;
- my $strWorkPath = 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
- #---------------------------------------------------------------------------------------------------------------------------
- my $strLCovFile = "${strWorkTmpPath}/all.lcov";
-
- if ($oStorage->exists($strLCovFile))
- {
- foreach my $strCodeModule (sort(keys(%{$hCoverageActual})))
- {
- my $strCoverageFile = $strCodeModule;
- $strCoverageFile =~ s/^test/src/mg;
- $strCoverageFile =~ s/^doc/doc\/src/mg;
- $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++;
- }
- }
- }
-
- if ($result == 0)
- {
- &log(INFO, "tested modules have full coverage");
- }
-
- # Always generate unified coverage report if there was missing coverage. This is useful for CI.
- if ($bCoverageReport || $result != 0)
- {
- &log(INFO, 'writing C coverage report');
-
- if ($bCoverageReport)
- {
- executeTest(
- "genhtml ${strLCovFile} --config-file=${strTestResultCoveragePath}/raw/lcov.conf" .
- " --prefix=${strWorkPath}/repo" .
- " --output-directory=${strTestResultCoveragePath}/lcov");
- }
-
- coverageGenerate(
- $oStorage, "${strWorkPath}/repo", "${strTestResultCoveragePath}/raw", "${strTestResultCoveragePath}/coverage.html",
- $bCoverageReport);
- }
- # Else output report status in the HTML
- else
- {
- $oStorage->put(
- "${strTestResultCoveragePath}/coverage.html",
- "