@code-pushup/coverage-plugin
Version:
Code PushUp plugin for tracking code coverage ☂
86 lines • 3.53 kB
JavaScript
import path from 'node:path';
import { exists, readTextFile, toUnixNewlines, ui } from '@code-pushup/utils';
import { mergeLcovResults } from './merge-lcov.js';
import { parseLcov } from './parse-lcov.js';
import { lcovCoverageToAuditOutput, recordToStatFunctionMapper, } from './transform.js';
// Note: condition or statement coverage is not supported in LCOV
// https://stackoverflow.com/questions/48260434/is-it-possible-to-check-condition-coverage-with-gcov
/**
*
* @param results Paths to LCOV results
* @param coverageTypes types of coverage to be considered
* @returns Audit outputs with complete coverage data.
*/
export async function lcovResultsToAuditOutputs(results, coverageTypes) {
// Parse lcov files
const lcovResults = await parseLcovFiles(results);
// Merge multiple coverage reports for the same file
const mergedResults = mergeLcovResults(lcovResults);
// Calculate code coverage from all coverage results
const totalCoverageStats = getTotalCoverageFromLcovRecords(mergedResults, coverageTypes);
return coverageTypes
.map(coverageType => {
const stats = totalCoverageStats[coverageType];
if (!stats) {
return null;
}
return lcovCoverageToAuditOutput(stats, coverageType);
})
.filter(exists);
}
/**
*
* @param results Paths to LCOV results
* @returns Array of parsed LCOVRecords.
*/
export async function parseLcovFiles(results) {
const parsedResults = (await Promise.all(results.map(async (result) => {
const resultsPath = typeof result === 'string' ? result : result.resultsPath;
const lcovFileContent = await readTextFile(resultsPath);
if (lcovFileContent.trim() === '') {
ui().logger.warning(`Coverage plugin: Empty lcov report file detected at ${resultsPath}.`);
}
const parsedRecords = parseLcov(toUnixNewlines(lcovFileContent));
return parsedRecords.map(record => ({
...record,
file: typeof result === 'string' || result.pathToProject == null
? record.file
: path.join(result.pathToProject, record.file),
}));
}))).flat();
if (parsedResults.length === 0) {
throw new Error('All provided coverage results are empty.');
}
return parsedResults;
}
/**
*
* @param records This function aggregates coverage stats from all coverage files
* @param coverageTypes Types of coverage to be gathered
* @returns Complete coverage stats for all defined types of coverage.
*/
function getTotalCoverageFromLcovRecords(records, coverageTypes) {
return records.reduce((acc, report) => Object.fromEntries([
...Object.entries(acc),
...Object.entries(getCoverageStatsFromLcovRecord(report, coverageTypes)).map(([type, stats]) => [
type,
{
totalFound: (acc[type]?.totalFound ?? 0) + stats.totalFound,
totalHit: (acc[type]?.totalHit ?? 0) + stats.totalHit,
issues: [...(acc[type]?.issues ?? []), ...stats.issues],
},
]),
]), {});
}
/**
* @param record record file data
* @param coverageTypes types of coverage to be gathered
* @returns Relevant coverage data from one lcov record file.
*/
function getCoverageStatsFromLcovRecord(record, coverageTypes) {
return Object.fromEntries(coverageTypes.map((coverageType) => [
coverageType,
recordToStatFunctionMapper[coverageType](record),
]));
}
//# sourceMappingURL=lcov-runner.js.map