1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-01-18 04:58:51 +02:00

Add coverage to C test harness.

Coverage reports are still generated in Perl, but all the settings have been added to the C harness to generate raw coverage data.
This commit is contained in:
David Steele 2022-07-28 14:53:48 -04:00
parent c99ea54f17
commit 1e83f2a022
8 changed files with 83 additions and 25 deletions

View File

@ -115,9 +115,7 @@ sub new
$self->{iTry} = 0; $self->{iTry} = 0;
# Use the new C test harness? # Use the new C test harness?
$self->{bTestC} = $self->{bTestC} = $self->{oTest}->{&TEST_C} && !$self->{bProfile} && $self->{oTest}->{&TEST_TYPE} ne TESTDEF_PERFORMANCE;
$self->{oTest}->{&TEST_C} && !$self->{bCoverageUnit} && !$self->{bProfile} &&
$self->{oTest}->{&TEST_TYPE} ne TESTDEF_PERFORMANCE;
# Setup the path where unit test will be built # Setup the path where unit test will be built
$self->{strUnitPath} = $self->{strUnitPath} =
@ -249,13 +247,17 @@ sub run
# Build filename for valgrind suppressions # Build filename for valgrind suppressions
my $strValgrindSuppress = $self->{strRepoPath} . '/test/src/valgrind.suppress.' . $self->{oTest}->{&TEST_VM}; my $strValgrindSuppress = $self->{strRepoPath} . '/test/src/valgrind.suppress.' . $self->{oTest}->{&TEST_VM};
# Is coverage enabled?
my $bCoverage = vmCoverageC($self->{oTest}->{&TEST_VM}) && $self->{bCoverageUnit};
$strCommand = $strCommand =
($self->{oTest}->{&TEST_VM} ne VM_NONE ? "docker exec -i -u ${\TEST_USER} ${strImage} bash -l -c '" : '') . ($self->{oTest}->{&TEST_VM} ne VM_NONE ? "docker exec -i -u ${\TEST_USER} ${strImage} bash -l -c '" : '') .
" \\\n" . " \\\n" .
$self->{strTestPath} . '/build/' . $self->{oTest}->{&TEST_VM} . '/test/src/test-pgbackrest --no-run' . $self->{strTestPath} . '/build/' . $self->{oTest}->{&TEST_VM} . '/test/src/test-pgbackrest --no-run' .
' --no-repo-copy --repo-path=' . $self->{strTestPath} . '/repo' . ' --test-path=' . $self->{strTestPath} . ' --no-repo-copy --repo-path=' . $self->{strTestPath} . '/repo' . ' --test-path=' . $self->{strTestPath} .
' --vm=' . $self->{oTest}->{&TEST_VM} . ' --vm-id=' . $self->{iVmIdx} . ' test ' . ' --vm=' . $self->{oTest}->{&TEST_VM} . ' --vm-id=' . $self->{iVmIdx} .
$self->{oTest}->{&TEST_MODULE} . '/' . $self->{oTest}->{&TEST_NAME} . " && \\\n" . ($bCoverage ? '' : ' --no-coverage') . ' test ' . $self->{oTest}->{&TEST_MODULE} . '/' .
$self->{oTest}->{&TEST_NAME} . " && \\\n" .
# Allow stderr to be copied to stderr and stdout # Allow stderr to be copied to stderr and stdout
"exec 3>&1 && \\\n" . "exec 3>&1 && \\\n" .
# Test with valgrind when requested # Test with valgrind when requested
@ -865,7 +867,9 @@ sub end
$self->{oStorageTest}, $self->{oTest}->{&TEST_MODULE}, $self->{oTest}->{&TEST_NAME}, $self->{oStorageTest}, $self->{oTest}->{&TEST_MODULE}, $self->{oTest}->{&TEST_NAME},
$self->{oTest}->{&TEST_VM} ne VM_NONE, $self->{bCoverageSummary}, $self->{oTest}->{&TEST_VM} ne VM_NONE, $self->{bCoverageSummary},
$self->{oTest}->{&TEST_VM} eq VM_NONE ? undef : $strImage, $self->{strTestPath}, "$self->{strTestPath}/temp", $self->{oTest}->{&TEST_VM} eq VM_NONE ? undef : $strImage, $self->{strTestPath}, "$self->{strTestPath}/temp",
$self->{strUnitPath}, $self->{strBackRestBase} . '/test/result'); -e "$self->{strUnitPath}/build/test-unit.p" ?
"$self->{strUnitPath}/build/test-unit.p" : "$self->{strUnitPath}/build/test-unit\@exe",
$self->{strBackRestBase} . '/test/result');
} }
# Record elapsed time # Record elapsed time

View File

@ -45,6 +45,13 @@ option:
- 512KiB - 512KiB
- 1MiB - 1MiB
coverage:
type: boolean
default: true
negate: true
command:
test: {}
neutral-umask: neutral-umask:
type: boolean type: boolean
internal: true internal: true

View File

@ -33,6 +33,16 @@
<example>1MiB</example> <example>1MiB</example>
</option> </option>
<option id="coverage">
<summary>Generate code coverage.</summary>
<text>
<p>Coverage helps determine that tests adequately test the code.</p>
</text>
<example>n</example>
</option>
<option id="log-level"> <option id="log-level">
<summary>Level for harness logging.</summary> <summary>Level for harness logging.</summary>

View File

@ -31,7 +31,7 @@ TestBuild *
testBldNew( testBldNew(
const String *const pathRepo, const String *const pathTest, const String *const vm, const unsigned int vmId, const String *const pathRepo, const String *const pathTest, const String *const vm, const unsigned int vmId,
const TestDefModule *const module, const unsigned int test, const uint64_t scale, const LogLevel logLevel, const bool logTime, const TestDefModule *const module, const unsigned int test, const uint64_t scale, const LogLevel logLevel, const bool logTime,
const String *const timeZone) const String *const timeZone, const bool coverage)
{ {
FUNCTION_LOG_BEGIN(logLevelDebug); FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING, pathRepo); FUNCTION_LOG_PARAM(STRING, pathRepo);
@ -44,6 +44,7 @@ testBldNew(
FUNCTION_LOG_PARAM(ENUM, logLevel); FUNCTION_LOG_PARAM(ENUM, logLevel);
FUNCTION_LOG_PARAM(BOOL, logTime); FUNCTION_LOG_PARAM(BOOL, logTime);
FUNCTION_LOG_PARAM(STRING, timeZone); FUNCTION_LOG_PARAM(STRING, timeZone);
FUNCTION_LOG_PARAM(BOOL, coverage);
FUNCTION_LOG_END(); FUNCTION_LOG_END();
ASSERT(pathRepo != NULL); ASSERT(pathRepo != NULL);
@ -73,6 +74,7 @@ testBldNew(
.logLevel = logLevel, .logLevel = logLevel,
.logTime = logTime, .logTime = logTime,
.timeZone = strDup(timeZone), .timeZone = strDup(timeZone),
.coverage = coverage,
}, },
}; };
@ -363,6 +365,14 @@ testBldUnit(TestBuild *const this)
if (module->flag != NULL) if (module->flag != NULL)
strCatFmt(mesonBuild, "add_global_arguments('%s', language : 'c')\n", strZ(module->flag)); strCatFmt(mesonBuild, "add_global_arguments('%s', language : 'c')\n", strZ(module->flag));
// Add coverage
if (testBldCoverage(this))
strCatZ(mesonBuild, "add_global_arguments('-DDEBUG_COVERAGE', language : 'c')\n");
// Add container flag
if (!strEqZ(testBldVm(this), "none"))
strCatZ(mesonBuild, "add_global_arguments('-DTEST_CONTAINER_REQUIRED', language : 'c')\n");
// Build unit test // Build unit test
strCatZ( strCatZ(
mesonBuild, mesonBuild,
@ -536,7 +546,7 @@ testBldUnit(TestBuild *const this)
strReplace(testC, STRDEF("{[C_TEST_SCALE]}"), strNewFmt("%" PRIu64, testBldScale(this))); strReplace(testC, STRDEF("{[C_TEST_SCALE]}"), strNewFmt("%" PRIu64, testBldScale(this)));
// Does this test run in a container? // Does this test run in a container?
strReplace(testC, STRDEF("{[C_TEST_CONTAINER]}"), STR(cvtBoolToConstZ(module->containerRequired))); strReplace(testC, STRDEF("{[C_TEST_CONTAINER]}"), STR(cvtBoolToConstZ(!strEqZ(testBldVm(this), "none"))));
// User/group info // User/group info
strReplace(testC, STRDEF("{[C_TEST_GROUP]}"), groupName()); strReplace(testC, STRDEF("{[C_TEST_GROUP]}"), groupName());

View File

@ -20,7 +20,7 @@ Constructors
***********************************************************************************************************************************/ ***********************************************************************************************************************************/
TestBuild *testBldNew( TestBuild *testBldNew(
const String *pathRepo, const String *pathTest, const String *const vm, unsigned int vmId, const TestDefModule *module, const String *pathRepo, const String *pathTest, const String *const vm, unsigned int vmId, const TestDefModule *module,
unsigned int test, uint64_t scale, LogLevel logLevel, bool logTime, const String *timeZone); unsigned int test, uint64_t scale, LogLevel logLevel, bool logTime, const String *timeZone, bool coverage);
/*********************************************************************************************************************************** /***********************************************************************************************************************************
Getters/Setters Getters/Setters
@ -39,6 +39,7 @@ typedef struct TestBuildPub
bool logTime; // Log times/timestamps bool logTime; // Log times/timestamps
uint64_t scale; // Scale performance test uint64_t scale; // Scale performance test
const String *timeZone; // Test in timezone const String *timeZone; // Test in timezone
bool coverage; // Generate coverage?
TestDef tstDef; // Test definitions TestDef tstDef; // Test definitions
} TestBuildPub; } TestBuildPub;
@ -120,6 +121,13 @@ testBldTimeZone(const TestBuild *const this)
return THIS_PUB(TestBuild)->timeZone; return THIS_PUB(TestBuild)->timeZone;
} }
// Generate coverage?
__attribute__((always_inline)) static inline bool
testBldCoverage(const TestBuild *const this)
{
return THIS_PUB(TestBuild)->coverage;
}
// Scale // Scale
__attribute__((always_inline)) static inline uint64_t __attribute__((always_inline)) static inline uint64_t
testBldScale(const TestBuild *const this) testBldScale(const TestBuild *const this)

View File

@ -74,7 +74,7 @@ void
cmdTest( cmdTest(
const String *const pathRepo, const String *const pathTest, const String *const vm, const unsigned int vmId, const String *const pathRepo, const String *const pathTest, const String *const vm, const unsigned int vmId,
const StringList *moduleFilterList, const unsigned int test, const uint64_t scale, const LogLevel logLevel, const StringList *moduleFilterList, const unsigned int test, const uint64_t scale, const LogLevel logLevel,
const bool logTime, const String *const timeZone, const bool repoCopy, const bool valgrind, const bool run) const bool logTime, const String *const timeZone, const bool repoCopy, const bool valgrind, const bool coverage, const bool run)
{ {
FUNCTION_LOG_BEGIN(logLevelDebug); FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING, pathRepo); FUNCTION_LOG_PARAM(STRING, pathRepo);
@ -89,6 +89,7 @@ cmdTest(
FUNCTION_LOG_PARAM(STRING, timeZone); FUNCTION_LOG_PARAM(STRING, timeZone);
FUNCTION_LOG_PARAM(BOOL, repoCopy); FUNCTION_LOG_PARAM(BOOL, repoCopy);
FUNCTION_LOG_PARAM(BOOL, valgrind); FUNCTION_LOG_PARAM(BOOL, valgrind);
FUNCTION_LOG_PARAM(BOOL, coverage);
FUNCTION_LOG_PARAM(BOOL, run); FUNCTION_LOG_PARAM(BOOL, run);
FUNCTION_LOG_END(); FUNCTION_LOG_END();
@ -188,7 +189,7 @@ cmdTest(
// Process test list // Process test list
unsigned int errorTotal = 0; unsigned int errorTotal = 0;
bool buildRetry = false; bool buildRetry = false;
const char *buildTypeLast = NULL; String *const mesonSetupLast = strNew();
for (unsigned int moduleIdx = 0; moduleIdx < strLstSize(moduleList); moduleIdx++) for (unsigned int moduleIdx = 0; moduleIdx < strLstSize(moduleList); moduleIdx++)
{ {
@ -201,6 +202,7 @@ cmdTest(
// Build unit test // Build unit test
const String *const pathUnit = strNewFmt("%s/unit-%u/%s", strZ(pathTest), vmId, strZ(vm)); const String *const pathUnit = strNewFmt("%s/unit-%u/%s", strZ(pathTest), vmId, strZ(vm));
const String *const pathUnitBuild = strNewFmt("%s/build", strZ(pathUnit)); const String *const pathUnitBuild = strNewFmt("%s/build", strZ(pathUnit));
const Storage *const storageUnitBuild = storagePosixNewP(pathUnitBuild, .write = true);
const TimeMSec buildTimeBegin = timeMSec(); const TimeMSec buildTimeBegin = timeMSec();
TimeMSec buildTimeEnd; TimeMSec buildTimeEnd;
TestBuild *testBld; TestBuild *testBld;
@ -209,35 +211,52 @@ cmdTest(
{ {
// Build unit // Build unit
testBld = testBldNew( testBld = testBldNew(
pathRepoCopy, pathTest, vm, vmId, module, test, scale, logLevel, logTime, timeZone); pathRepoCopy, pathTest, vm, vmId, module, test, scale, logLevel, logTime, timeZone, coverage);
testBldUnit(testBld); testBldUnit(testBld);
// Meson setup // Meson setup
const char *buildType = "debug"; String *const mesonSetup = strCatZ(strNew(), "-Dbuildtype=");
if (module->flag != NULL) if (module->flag != NULL)
{ {
ASSERT(strEqZ(module->flag, "-DNDEBUG")); ASSERT(strEqZ(module->flag, "-DNDEBUG"));
buildType = "release"; strCatZ(mesonSetup, "release");
} }
else
strCatZ(mesonSetup, "debug");
strCatFmt(mesonSetup, " -Db_coverage=%s", cvtBoolToConstZ(coverage));
if (!storageExistsP(testBldStorageTest(testBld), strNewFmt("%s/build.ninja", strZ(pathUnitBuild)))) if (!storageExistsP(testBldStorageTest(testBld), strNewFmt("%s/build.ninja", strZ(pathUnitBuild))))
{ {
LOG_DETAIL("meson setup"); LOG_DETAIL("meson setup");
cmdTestExec( cmdTestExec(
strNewFmt( strNewFmt(
"meson setup -Dwerror=true -Dfatal-errors=true -Dbuildtype=%s %s %s", buildType, "meson setup -Dwerror=true -Dfatal-errors=true %s %s %s", strZ(mesonSetup), strZ(pathUnitBuild),
strZ(pathUnitBuild), strZ(pathUnit))); strZ(pathUnit)));
buildTypeLast = buildType;
} }
// Else reconfigure as needed
// Reconfigure as needed else if (!strEq(mesonSetup, mesonSetupLast))
if (buildType != buildTypeLast)
{ {
LOG_DETAIL("meson configure"); LOG_DETAIL("meson configure");
cmdTestExec(strNewFmt("meson configure -Dbuildtype=%s %s", buildType, strZ(pathUnitBuild)));
buildTypeLast = buildType; cmdTestExec(strNewFmt("meson configure %s %s", strZ(mesonSetup), strZ(pathUnitBuild)));
}
strCat(strTrunc(mesonSetupLast), mesonSetup);
// Remove old coverage data. Note that coverage can be in different paths depending on the meson version.
const String *const pathCoverage = storagePathExistsP(storageUnitBuild, STRDEF("test-unit.p")) ?
STRDEF("test-unit.p") : STRDEF("test-unit@exe");
StorageIterator *const storageItr = storageNewItrP(
storageUnitBuild, pathCoverage, .expression = STRDEF("\\.gcda$"));
while (storageItrMore(storageItr))
{
storageRemoveP(
storageUnitBuild, strNewFmt("%s/%s", strZ(pathCoverage), strZ(storageItrNext(storageItr).name)));
} }
// Ninja build // Ninja build

View File

@ -15,6 +15,6 @@ Functions
void cmdTest( void cmdTest(
const String *pathRepo, const String *pathTest, const String *const vm, unsigned int vmId, const StringList *moduleFilterList, const String *pathRepo, const String *pathTest, const String *const vm, unsigned int vmId, const StringList *moduleFilterList,
unsigned int test, uint64_t scale, LogLevel logLevel, bool logTime, const String *timeZone, bool repoCopy, bool valgrind, unsigned int test, uint64_t scale, LogLevel logLevel, bool logTime, const String *timeZone, bool repoCopy, bool valgrind,
bool run); bool coverage, bool run);
#endif #endif

View File

@ -75,7 +75,7 @@ main(int argListSize, const char *argList[])
cfgOptionUInt(cfgOptVmId), cfgCommandParam(), cfgOptionTest(cfgOptTest) ? cfgOptionUInt(cfgOptTest) : 0, cfgOptionUInt(cfgOptVmId), cfgCommandParam(), cfgOptionTest(cfgOptTest) ? cfgOptionUInt(cfgOptTest) : 0,
cfgOptionUInt64(cfgOptScale), logLevelEnum(cfgOptionStrId(cfgOptLogLevelTest)), cfgOptionUInt64(cfgOptScale), logLevelEnum(cfgOptionStrId(cfgOptLogLevelTest)),
cfgOptionBool(cfgOptLogTimestamp), cfgOptionStrNull(cfgOptTz), cfgOptionBool(cfgOptRepoCopy), cfgOptionBool(cfgOptLogTimestamp), cfgOptionStrNull(cfgOptTz), cfgOptionBool(cfgOptRepoCopy),
cfgOptionBool(cfgOptValgrind), cfgOptionBool(cfgOptRun)); cfgOptionBool(cfgOptValgrind), cfgOptionBool(cfgOptCoverage), cfgOptionBool(cfgOptRun));
break; break;
} }