You've already forked pgbackrest
							
							
				mirror of
				https://github.com/pgbackrest/pgbackrest.git
				synced 2025-10-30 23:37:45 +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:
		| @@ -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 | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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> | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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()); | ||||||
|   | |||||||
| @@ -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) | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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; | ||||||
|                 } |                 } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user