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/backup 50/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", - "
[ " . ($result == 0 ? "Coverage Complete" : "No Coverage Report") . " ]
"); - } - - if ($bCoverageSummary) - { - &log(INFO, 'writing C coverage summary report'); - - coverageDocSummaryGenerate( - $oStorage, "${strTestResultCoveragePath}/raw", "${strTestResultSummaryPath}/metric-coverage-report.auto.xml"); - } - } - - return $result; -} - -push @EXPORT, qw(coverageValidateAndGenerate); - -#################################################################################################################################### -# 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 -{ - my $oStorage = shift; - my $strBasePath = shift; - my $strCoveragePath = shift; - my $strOutFile = shift; - my $bCoverageReport = shift; - - # Track missing coverage - my $rhCoverage = {}; - - # Find all lcov files in the coverage path - my $rhManifest = $oStorage->manifest($strCoveragePath); - - foreach my $strFileCov (sort(keys(%{$rhManifest}))) - { - # If a coverage report was not requested then skip coverage of test modules. If we are here it means there was missing - # coverage on CI and we want to keep the report as small as possible. - next if !$bCoverageReport && $strFileCov =~ /Test\.lcov$/; - - if ($strFileCov =~ /\.lcov$/) - { - my $strCoverage = ${$oStorage->get("${strCoveragePath}/${strFileCov}")}; - - # Show that the file is part of the coverage report even if there is no missing coverage - my $strFile; - - my $iBranchLine = -1; - my $iBranch = undef; - my $iBranchIdx = -1; - my $iBranchPart = undef; - - foreach my $strLine (split("\n", $strCoverage)) - { - # Get source file name - if ($strLine =~ /^SF\:/) - { - $strFile = substr($strLine, 3); - $rhCoverage->{$strFile} = undef; - - # Generate a random anchor so new reports will not show links as already followed. This is also an easy way to - # create valid, disambiguos links. - $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 - elsif ($strLine =~ /^BRDA\:/) - { - my @stryData = split("\,", substr($strLine, 5)); - - if (@stryData < 4) - { - confess &log(ERROR, "'${strLine}' should have four fields"); - } - - my $strBranchLine = sprintf("%09d", $stryData[0]); - - if ($iBranchLine != $stryData[0]) - { - $iBranchLine = $stryData[0] + 0; - $iBranch = $stryData[1] + 0; - $iBranchIdx = 0; - $iBranchPart = 0; - } - elsif ($iBranch != $stryData[1]) - { - if ($iBranchPart != 1) - { - confess &log(ERROR, "line ${iBranchLine}, branch ${iBranch} does not have at least two parts"); - } - - $iBranch = $stryData[1] + 0; - $iBranchIdx++; - $iBranchPart = 0; - } - else - { - $iBranchPart++; - } - - $rhCoverage->{$strFile}{line}{$strBranchLine}{branch}{$iBranchIdx}{$iBranchPart} = - $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}) - { - coverageGenerateFunction($rhCoverage->{$strFile}, $strBranchLine)->{covered} = false; - } - } - - # Check line coverage - if ($strLine =~ /^DA\:/) - { - my @stryData = split("\,", substr($strLine, 3)); - - if (@stryData < 2) - { - confess &log(ERROR, "'${strLine}' should have two fields"); - } - - my $strStatementLine = sprintf("%09d", $stryData[0]); - - # If the statement is uncovered then the function is uncovered - if ($stryData[1] eq '0') - { - $rhCoverage->{$strFile}{line}{$strStatementLine}{statement} = 0; - coverageGenerateFunction($rhCoverage->{$strFile}, $strStatementLine)->{covered} = false; - } - } - } - } - } - - # Report on the entire function if any branches/lines in the function are uncovered - foreach my $strFile (sort(keys(%{$rhCoverage}))) - { - my $bFileCovered = true; - - # Proceed if there is some coverage data - if (defined($rhCoverage->{$strFile}{line})) - { - my @stryC = split("\n", ${$oStorage->get($strFile)}); - my $bInUncoveredFunction = false; - - # Iterate every line in the C file - for (my $iLineIdx = 0; $iLineIdx < @stryC; $iLineIdx++) - { - my $iLine = sprintf("%09d", $iLineIdx + 1); - - # If not in an uncovered function see if this line is the start of an uncovered function - if (!$bInUncoveredFunction) - { - $bInUncoveredFunction = - defined($rhCoverage->{$strFile}{function}{$iLine}) && !$rhCoverage->{$strFile}{function}{$iLine}{covered}; - - # If any function is uncovered then the file is uncovered - if ($bInUncoveredFunction) - { - $bFileCovered = false; - } - } - - # 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; - } - - # Stop processing at the function end brace. This depends on the file being formated correctly, but worst case - # is we run on a display the entire file rather than just uncovered functions. - if ($stryC[$iLineIdx] =~ '^\}') - { - $bInUncoveredFunction = false; - } - } - } - } - - # Remove coverage info when file is fully covered - if ($bFileCovered) - { - delete($rhCoverage->{$strFile}{line}); - } - } - - # Build html - my $strTitle = PROJECT_NAME . ' Coverage Report'; - my $strDarkRed = '#580000'; - my $strGray = '#555555'; - my $strDarkGray = '#333333'; - - my $oHtml = new pgBackRestDoc::Html::DocHtmlBuilder( - PROJECT_NAME, $strTitle, - undef, undef, undef, - true, true, - - "html\n" . - "{\n" . - " background-color: ${strGray};\n" . - " font-family: Avenir, Corbel, sans-serif;\n" . - " color: white;\n" . - " font-size: 12pt;\n" . - " margin-top: 8px;\n" . - " margin-left: 1\%;\n" . - " margin-right: 1\%;\n" . - " width: 98\%;\n" . - "}\n" . - "\n" . - "body\n" . - "{\n" . - " margin: 0px auto;\n" . - " padding: 0px;\n" . - " width: 100\%;\n" . - " text-align: justify;\n" . - "}\n" . - - ".title\n" . - "{\n" . - " width: 100\%;\n" . - " text-align: center;\n" . - " font-size: 200\%;\n" . - "}\n" . - - "\n" . - ".list-table\n" . - "{\n" . - " width: 100\%;\n" . - "}\n" . - - "\n" . - ".list-table-caption\n" . - "{\n" . - " margin-top: 1em;\n" . - " font-size: 130\%;\n" . - " margin-bottom: .25em;\n" . - "}\n" . - - "\n" . - ".list-table-caption::after\n" . - "{\n" . - " content: \"Modules Tested for Coverage:\";\n" . - "}\n" . - - "\n" . - ".list-table-header-file\n" . - "{\n" . - " padding-left: .5em;\n" . - " padding-right: .5em;\n" . - " background-color: ${strDarkGray};\n" . - " width: 100\%;\n" . - "}\n" . - - "\n" . - ".list-table-row-uncovered\n" . - "{\n" . - " background-color: ${strDarkRed};\n" . - " color: white;\n" . - " width: 100\%;\n" . - "}\n" . - - "\n" . - ".list-table-row-file\n" . - "{\n" . - " padding-left: .5em;\n" . - " padding-right: .5em;\n" . - "}\n" . - - "\n" . - ".report-table\n" . - "{\n" . - " width: 100\%;\n" . - "}\n" . - - "\n" . - ".report-table-caption\n" . - "{\n" . - " margin-top: 1em;\n" . - " font-size: 130\%;\n" . - " margin-bottom: .25em;\n" . - "}\n" . - - "\n" . - ".report-table-caption::after\n" . - "{\n" . - " content: \" report:\";\n" . - "}\n" . - - "\n" . - ".report-table-header\n" . - "{\n" . - "}\n" . - - "\n" . - ".report-table-header-line, .report-table-header-branch, .report-table-header-code\n" . - "{\n" . - " padding-left: .5em;\n" . - " padding-right: .5em;\n" . - " background-color: ${strDarkGray};\n" . - "}\n" . - - "\n" . - ".report-table-header-code\n" . - "{\n" . - " width: 100\%;\n" . - "}\n" . - - "\n" . - ".report-table-row-dot-tr, .report-table-row\n" . - "{\n" . - " font-family: \"Courier New\", Courier, monospace;\n" . - "}\n" . - - "\n" . - ".report-table-row-dot-skip\n" . - "{\n" . - " height: 1em;\n" . - " padding-top: .25em;\n" . - " padding-bottom: .25em;\n" . - " text-align: center;\n" . - "}\n" . - - "\n" . - ".report-table-row-line, .report-table-row-branch, .report-table-row-branch-uncovered," . - " .report-table-row-code, .report-table-row-code-uncovered\n" . - "{\n" . - " padding-left: .5em;\n" . - " padding-right: .5em;\n" . - "}\n" . - - "\n" . - ".report-table-row-line\n" . - "{\n" . - " text-align: right;\n" . - "}\n" . - - "\n" . - ".report-table-row-branch, .report-table-row-branch-uncovered\n" . - "{\n" . - " text-align: right;\n" . - " white-space: nowrap;\n" . - "}\n" . - - "\n" . - ".report-table-row-branch-uncovered\n" . - "{\n" . - " background-color: ${strDarkRed};\n" . - " color: white;\n" . - "}\n" . - - "\n" . - ".report-table-row-code, .report-table-row-code-uncovered\n" . - "{\n" . - " white-space: pre;\n" . - "}\n" . - - "\n" . - ".report-table-row-code-uncovered\n" . - "{\n" . - " background-color: ${strDarkRed};\n" . - " color: white;\n" . - "}\n"); - - # File list title - $oHtml->bodyGet()->addNew(HTML_DIV, 'title', {strContent => $strTitle}); - - # Build the file list table - $oHtml->bodyGet()->addNew(HTML_DIV, 'list-table-caption'); - - my $oTable = $oHtml->bodyGet()->addNew(HTML_TABLE, 'list-table'); - - my $oHeader = $oTable->addNew(HTML_TR, 'list-table-header'); - $oHeader->addNew(HTML_TH, 'list-table-header-file', {strContent => 'FILE'}); - - foreach my $strFile (sort(keys(%{$rhCoverage}))) - { - 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 - if (defined($rhCoverage->{$strFile}{line})) - { - $oRow->addNew(HTML_TD, 'list-table-row-file')->addNew( - HTML_A, undef, {strContent => $strFileDisplay, strRef => '#' . $rhCoverage->{$strFile}{anchor}}); - } - # Else just show the file name - else - { - $oRow->addNew(HTML_TD, 'list-table-row-file', {strContent => $strFileDisplay}); - } - } - - # Report on files that are missing coverage - foreach my $strFile (sort(keys(%{$rhCoverage}))) - { - my $strFileDisplay = substr($strFile, length($strBasePath) + 1); - - if (defined($rhCoverage->{$strFile}{line})) - { - # Anchor only created when file is uncovered - $oHtml->bodyGet()->addNew(HTML_A, undef, {strId => $rhCoverage->{$strFile}{anchor}}); - - # Report table caption, i.e. the uncovered file name - $oHtml->bodyGet()->addNew(HTML_DIV, 'report-table-caption', {strContent => $strFileDisplay}); - - # Build the file report table - $oTable = $oHtml->bodyGet()->addNew(HTML_TABLE, 'report-table'); - - $oHeader = $oTable->addNew(HTML_TR, 'report-table-header'); - $oHeader->addNew(HTML_TH, 'report-table-header-line', {strContent => 'LINE'}); - $oHeader->addNew(HTML_TH, 'report-table-header-branch', {strContent => 'BRANCH'}); - $oHeader->addNew(HTML_TH, 'report-table-header-code', {strContent => 'CODE'}); - - my $strC = ${$oStorage->get($strFile)}; - my @stryC = split("\n", $strC); - my $iLastLine = undef; - - foreach my $strLine (sort(keys(%{$rhCoverage->{$strFile}{line}}))) - { - if (defined($iLastLine) && $strLine != $iLastLine + 1) - { - my $oRow = $oTable->addNew(HTML_TR, 'report-table-row-dot'); - $oRow->addNew(HTML_TD, 'report-table-row-dot-skip', {strExtra => 'colspan="3"'}); - } - - $iLastLine = $strLine; - my $iLine = int($strLine); - - my $oRow = $oTable->addNew(HTML_TR, 'report-table-row'); - $oRow->addNew(HTML_TD, 'report-table-row-line', {strContent => $iLine}); - my $strBranch; - - # Show missing branch coverage - my $bBranchCovered = true; - - if (defined($rhCoverage->{$strFile}{line}{$strLine}{branch})) - { - foreach my $iBranch (sort(keys(%{$rhCoverage->{$strFile}{line}{$strLine}{branch}}))) - { - $strBranch .= '['; - - my $bBranchPartFirst = true; - - foreach my $iBranchPart (sort(keys(%{$rhCoverage->{$strFile}{line}{$strLine}{branch}{$iBranch}}))) - { - if (!$bBranchPartFirst) - { - $strBranch .= ' '; - } - - if ($rhCoverage->{$strFile}{line}{$strLine}{branch}{$iBranch}{$iBranchPart}) - { - $strBranch .= '+'; - } - else - { - $strBranch .= '-'; - $bBranchCovered = false; - } - - $bBranchPartFirst = false; - } - - $strBranch .= ']'; - } - } - - $oRow->addNew( - HTML_TD, 'report-table-row-branch' . (!$bBranchCovered ? '-uncovered' : ''), {strContent => $strBranch}); - - # Color code based on coverage - $oRow->addNew( - HTML_TD, - 'report-table-row-code' . (defined($rhCoverage->{$strFile}{line}{$strLine}{statement}) ? '-uncovered' : ''), - {bPre => true, strContent => $stryC[$strLine - 1]}); - } - } - } - - # Write coverage report - $oStorage->put($strOutFile, $oHtml->htmlGet()); -} - -push @EXPORT, qw(coverageGenerate); - -#################################################################################################################################### -# Generate a C coverage summary for the documentation -#################################################################################################################################### -sub coverageDocSummaryGenerateValue -{ - my $iHit = shift; - my $iFound = shift; - - if (!defined($iFound) || !defined($iHit) || $iFound == 0) - { - return "---"; - } - - my $fPercent = $iHit * 100 / $iFound; - my $strPercent; - - if ($fPercent == 100) - { - $strPercent = '100.0'; - } - elsif ($fPercent > 99.99) - { - $strPercent = '99.99'; - } - else - { - $strPercent = sprintf("%.2f", $fPercent); - } - - return "${iHit}/${iFound} (${strPercent}%)"; -} - -sub coverageDocSummaryGenerate -{ - my $oStorage = shift; - my $strCoveragePath = shift; - my $strOutFile = shift; - - # Track coverage summary - my $rhSummary; - - # Find all lcov files in the coverage path - my $rhManifest = $oStorage->manifest($strCoveragePath); - - foreach my $strFileCov (sort(keys(%{$rhManifest}))) - { - # Skip doc/test modules (this includes modules that start with src/ since src/ is stripped from core modules) - next if $strFileCov =~ /^doc\// || $strFileCov =~ /^test\// || $strFileCov =~ /^src\//; - - if ($strFileCov =~ /\.lcov$/) - { - my $strCoverage = ${$oStorage->get("${strCoveragePath}/${strFileCov}")}; - my $strModule = dirname($strFileCov); - - foreach my $strLine (split("\n", $strCoverage)) - { - # Get Line Coverage - if ($strLine =~ /^LF\:/) - { - $rhSummary->{$strModule}{line}{found} += substr($strLine, 3) + 0; - $rhSummary->{zzztotal}{line}{found} += substr($strLine, 3) + 0; - } - - if ($strLine =~ /^LH\:/) - { - $rhSummary->{$strModule}{line}{hit} += substr($strLine, 3) + 0; - $rhSummary->{zzztotal}{line}{hit} += substr($strLine, 3) + 0; - } - - # Get Function Coverage - if ($strLine =~ /^FNF\:/) - { - $rhSummary->{$strModule}{function}{found} += substr($strLine, 4) + 0; - $rhSummary->{zzztotal}{function}{found} += substr($strLine, 4) + 0; - } - - if ($strLine =~ /^FNH\:/) - { - $rhSummary->{$strModule}{function}{hit} += substr($strLine, 4) + 0; - $rhSummary->{zzztotal}{function}{hit} += substr($strLine, 4) + 0; - } - - # Get Branch Coverage - if ($strLine =~ /^BRF\:/) - { - $rhSummary->{$strModule}{branch}{found} += substr($strLine, 4) + 0; - $rhSummary->{zzztotal}{branch}{found} += substr($strLine, 4) + 0; - } - - if ($strLine =~ /^BRH\:/) - { - $rhSummary->{$strModule}{branch}{hit} += substr($strLine, 4) + 0; - $rhSummary->{zzztotal}{branch}{hit} += substr($strLine, 4) + 0; - } - } - } - } - - # use Data::Dumper;confess Dumper($rhSummary); - - my $strSummary; - - foreach my $strModule (sort(keys(%{$rhSummary}))) - { - my $rhModuleData = $rhSummary->{$strModule}; - - $strSummary .= - (defined($strSummary) ? "\n\n" : '') . - "\n" . - " " . ($strModule eq 'zzztotal' ? 'TOTAL' : $strModule) . "\n" . - " " . - coverageDocSummaryGenerateValue($rhModuleData->{function}{hit}, $rhModuleData->{function}{found}) . - "\n" . - " " . - coverageDocSummaryGenerateValue($rhModuleData->{branch}{hit}, $rhModuleData->{branch}{found}) . - "\n" . - " " . - coverageDocSummaryGenerateValue($rhModuleData->{line}{hit}, $rhModuleData->{line}{found}) . - "\n" . - ""; - } - - - # Write coverage report - $oStorage->put($strOutFile, $strSummary); -} - -push @EXPORT, qw(coverageDocSummaryGenerate); - -1; diff --git a/test/lib/pgBackRestTest/Common/JobTest.pm b/test/lib/pgBackRestTest/Common/JobTest.pm index 90c27c68f..e98c13d56 100644 --- a/test/lib/pgBackRestTest/Common/JobTest.pm +++ b/test/lib/pgBackRestTest/Common/JobTest.pm @@ -25,7 +25,6 @@ use pgBackRestDoc::ProjectInfo; use pgBackRestTest::Common::BuildTest; use pgBackRestTest::Common::ContainerTest; -use pgBackRestTest::Common::CoverageTest; use pgBackRestTest::Common::DbVersion; use pgBackRestTest::Common::DefineTest; use pgBackRestTest::Common::ExecuteTest; @@ -342,13 +341,13 @@ sub end # If C code generate coverage info if ($iExitStatus == 0 && $self->{oTest}->{&TEST_C} && vmCoverageC($self->{oTest}->{&TEST_VM}) && $self->{bCoverageUnit}) { - coverageExtract( - $self->{oStorageTest}, $self->{oTest}->{&TEST_MODULE}, $self->{oTest}->{&TEST_NAME}, - $self->{oTest}->{&TEST_VM} ne VM_NONE, $self->{bCoverageSummary}, - $self->{oTest}->{&TEST_VM} eq VM_NONE ? undef : $strImage, $self->{strTestPath}, "$self->{strTestPath}/temp", - -e "$self->{strUnitPath}/build/test-unit.p" ? - "$self->{strUnitPath}/build/test-unit.p" : "$self->{strUnitPath}/build/test-unit\@exe", - $self->{strBackRestBase} . '/test/result'); + executeTest( + ($self->{oTest}->{&TEST_VM} ne VM_NONE ? 'docker exec -i -u ' . TEST_USER . " ${strImage} " : '') . + "gcov --json-format --stdout --branch-probabilities " . + (-e "$self->{strUnitPath}/build/test-unit.p" ? + "$self->{strUnitPath}/build/test-unit.p" : "$self->{strUnitPath}/build/test-unit\@exe") . + '/test.c.gcda > ' . $self->{strBackRestBase} . '/test/result/coverage/raw/' . + $self->{oTest}->{&TEST_MODULE} . '-' . $self->{oTest}->{&TEST_NAME} . '.json'); } # Record elapsed time diff --git a/test/lib/pgBackRestTest/Common/VmTest.pm b/test/lib/pgBackRestTest/Common/VmTest.pm index c16b8db59..50cdf7652 100644 --- a/test/lib/pgBackRestTest/Common/VmTest.pm +++ b/test/lib/pgBackRestTest/Common/VmTest.pm @@ -42,8 +42,6 @@ use constant VM_OS_BASE => 'os-base' push @EXPORT, qw(VM_OS_BASE); use constant VMDEF_PGSQL_BIN => 'psql-bin'; push @EXPORT, qw(VMDEF_PGSQL_BIN); -use constant VMDEF_LCOV_VERSION => 'lcov-version'; - push @EXPORT, qw(VMDEF_LCOV_VERSION); use constant VMDEF_WITH_LZ4 => 'with-lz4'; push @EXPORT, qw(VMDEF_WITH_LZ4); use constant VMDEF_WITH_ZST => 'with-zst'; diff --git a/test/src/build/config/config.yaml b/test/src/build/config/config.yaml index 8006823c4..fa1b6f977 100644 --- a/test/src/build/config/config.yaml +++ b/test/src/build/config/config.yaml @@ -59,6 +59,12 @@ option: command: test: {} + coverage-summary: + type: boolean + default: false + command: + test: {} + neutral-umask: type: boolean internal: true @@ -126,8 +132,8 @@ option: vm-id: type: integer + required: false internal: true - default: 0 allow-range: [0, 1024] command: test: {} diff --git a/test/src/build/help/help.xml b/test/src/build/help/help.xml index b9b93b01a..0eba08a14 100644 --- a/test/src/build/help/help.xml +++ b/test/src/build/help/help.xml @@ -54,6 +54,16 @@ n + +