1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2024-12-12 10:04:14 +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;
# Use the new C test harness?
$self->{bTestC} =
$self->{oTest}->{&TEST_C} && !$self->{bCoverageUnit} && !$self->{bProfile} &&
$self->{oTest}->{&TEST_TYPE} ne TESTDEF_PERFORMANCE;
$self->{bTestC} = $self->{oTest}->{&TEST_C} && !$self->{bProfile} && $self->{oTest}->{&TEST_TYPE} ne TESTDEF_PERFORMANCE;
# Setup the path where unit test will be built
$self->{strUnitPath} =
@ -249,13 +247,17 @@ sub run
# Build filename for valgrind suppressions
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 =
($self->{oTest}->{&TEST_VM} ne VM_NONE ? "docker exec -i -u ${\TEST_USER} ${strImage} bash -l -c '" : '') .
" \\\n" .
$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} .
' --vm=' . $self->{oTest}->{&TEST_VM} . ' --vm-id=' . $self->{iVmIdx} . ' test ' .
$self->{oTest}->{&TEST_MODULE} . '/' . $self->{oTest}->{&TEST_NAME} . " && \\\n" .
' --vm=' . $self->{oTest}->{&TEST_VM} . ' --vm-id=' . $self->{iVmIdx} .
($bCoverage ? '' : ' --no-coverage') . ' test ' . $self->{oTest}->{&TEST_MODULE} . '/' .
$self->{oTest}->{&TEST_NAME} . " && \\\n" .
# Allow stderr to be copied to stderr and stdout
"exec 3>&1 && \\\n" .
# Test with valgrind when requested
@ -865,7 +867,9 @@ sub end
$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",
$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

View File

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

View File

@ -33,6 +33,16 @@
<example>1MiB</example>
</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">
<summary>Level for harness logging.</summary>

View File

@ -31,7 +31,7 @@ TestBuild *
testBldNew(
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 String *const timeZone)
const String *const timeZone, const bool coverage)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING, pathRepo);
@ -44,6 +44,7 @@ testBldNew(
FUNCTION_LOG_PARAM(ENUM, logLevel);
FUNCTION_LOG_PARAM(BOOL, logTime);
FUNCTION_LOG_PARAM(STRING, timeZone);
FUNCTION_LOG_PARAM(BOOL, coverage);
FUNCTION_LOG_END();
ASSERT(pathRepo != NULL);
@ -73,6 +74,7 @@ testBldNew(
.logLevel = logLevel,
.logTime = logTime,
.timeZone = strDup(timeZone),
.coverage = coverage,
},
};
@ -363,6 +365,14 @@ testBldUnit(TestBuild *const this)
if (module->flag != NULL)
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
strCatZ(
mesonBuild,
@ -536,7 +546,7 @@ testBldUnit(TestBuild *const this)
strReplace(testC, STRDEF("{[C_TEST_SCALE]}"), strNewFmt("%" PRIu64, testBldScale(this)));
// 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
strReplace(testC, STRDEF("{[C_TEST_GROUP]}"), groupName());

View File

@ -20,7 +20,7 @@ Constructors
***********************************************************************************************************************************/
TestBuild *testBldNew(
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
@ -39,6 +39,7 @@ typedef struct TestBuildPub
bool logTime; // Log times/timestamps
uint64_t scale; // Scale performance test
const String *timeZone; // Test in timezone
bool coverage; // Generate coverage?
TestDef tstDef; // Test definitions
} TestBuildPub;
@ -120,6 +121,13 @@ testBldTimeZone(const TestBuild *const this)
return THIS_PUB(TestBuild)->timeZone;
}
// Generate coverage?
__attribute__((always_inline)) static inline bool
testBldCoverage(const TestBuild *const this)
{
return THIS_PUB(TestBuild)->coverage;
}
// Scale
__attribute__((always_inline)) static inline uint64_t
testBldScale(const TestBuild *const this)

View File

@ -74,7 +74,7 @@ void
cmdTest(
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 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_PARAM(STRING, pathRepo);
@ -89,6 +89,7 @@ cmdTest(
FUNCTION_LOG_PARAM(STRING, timeZone);
FUNCTION_LOG_PARAM(BOOL, repoCopy);
FUNCTION_LOG_PARAM(BOOL, valgrind);
FUNCTION_LOG_PARAM(BOOL, coverage);
FUNCTION_LOG_PARAM(BOOL, run);
FUNCTION_LOG_END();
@ -188,7 +189,7 @@ cmdTest(
// Process test list
unsigned int errorTotal = 0;
bool buildRetry = false;
const char *buildTypeLast = NULL;
String *const mesonSetupLast = strNew();
for (unsigned int moduleIdx = 0; moduleIdx < strLstSize(moduleList); moduleIdx++)
{
@ -201,6 +202,7 @@ cmdTest(
// Build unit test
const String *const pathUnit = strNewFmt("%s/unit-%u/%s", strZ(pathTest), vmId, strZ(vm));
const String *const pathUnitBuild = strNewFmt("%s/build", strZ(pathUnit));
const Storage *const storageUnitBuild = storagePosixNewP(pathUnitBuild, .write = true);
const TimeMSec buildTimeBegin = timeMSec();
TimeMSec buildTimeEnd;
TestBuild *testBld;
@ -209,35 +211,52 @@ cmdTest(
{
// Build unit
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);
// Meson setup
const char *buildType = "debug";
String *const mesonSetup = strCatZ(strNew(), "-Dbuildtype=");
if (module->flag != NULL)
{
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))))
{
LOG_DETAIL("meson setup");
cmdTestExec(
strNewFmt(
"meson setup -Dwerror=true -Dfatal-errors=true -Dbuildtype=%s %s %s", buildType,
strZ(pathUnitBuild), strZ(pathUnit)));
buildTypeLast = buildType;
"meson setup -Dwerror=true -Dfatal-errors=true %s %s %s", strZ(mesonSetup), strZ(pathUnitBuild),
strZ(pathUnit)));
}
// Reconfigure as needed
if (buildType != buildTypeLast)
// Else reconfigure as needed
else if (!strEq(mesonSetup, mesonSetupLast))
{
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

View File

@ -15,6 +15,6 @@ Functions
void cmdTest(
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,
bool run);
bool coverage, bool run);
#endif

View File

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