-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0a7a5de
commit 4059134
Showing
7 changed files
with
127 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import * as core from '@actions/core'; | ||
import { promises as fs } from 'fs'; | ||
import { join } from 'path'; | ||
import { globNearest } from '../util/index.js'; | ||
|
||
/** | ||
* Load coverage reports using LCOV format. | ||
* @param {string} root Root search directory | ||
* @returns LCOV coverage report | ||
*/ | ||
export async function getReports(root) { | ||
core.info('Load LCOV coverage report'); | ||
const patterns = [ | ||
join(root, '**/lcov.*'), | ||
join(root, '**/*.lcov') | ||
]; | ||
const files = await globNearest(patterns); | ||
/** @type {Omit<CoverageReport, 'format'>[]} */ | ||
const reports = []; | ||
for (const f of files) { | ||
core.info(`Load LCOV report '${f}'`); | ||
const coverage = await getCoverage(f); | ||
if (coverage < 0) { | ||
core.info('Report is not a valid LCOV report'); | ||
continue; // Invalid report file, trying the next one | ||
} | ||
reports.push({ type: 'coverage', data: { coverage } }); | ||
break; // Successfully loaded a report file, can return now | ||
} | ||
core.info(`Loaded ${reports.length} LCOV report(s)`); | ||
return reports; | ||
} | ||
|
||
/** | ||
* Compute the total line coverage rate from the given LCOV report file, | ||
* i.e., the ratio of all hit (LH) to found (LF) lines, deduplicated by source | ||
* file (SF) (only the best coverage for each source file is considered). | ||
* @param {string} path Path to the LCOV coverage report file | ||
* @returns {Promise<number>} The total line coverage rate (%), | ||
* or -1 if no coverage data can be read from this file | ||
*/ | ||
async function getCoverage(path) { | ||
const contents = await fs.readFile(path, { encoding: 'utf8' }); | ||
/** @type {{ [sf: string]: { lh: number, lf: number } }} */ | ||
const sourceFiles = {}; | ||
let from = 0, to = 0; | ||
while ((to = contents.indexOf('end_of_record', from)) > 0) { | ||
const record = contents.slice(from, to); | ||
const sf = record.match(/^SF:(.+)$/m)?.[1] ?? ''; | ||
const lh = parseInt(record.match(/^LH:([0-9]+)$/m)?.[1] ?? '0'); | ||
const lf = parseInt(record.match(/^LF:([0-9]+)$/m)?.[1] ?? '0'); | ||
const value = sourceFiles[sf]; | ||
if (sf && (!value || (lf >= value.lf && lh / lf > value.lh / value.lf))) { | ||
sourceFiles[sf] = { lh, lf }; | ||
} | ||
from = to + 13; | ||
} | ||
let lh = 0, lf = 0; | ||
for (let value of Object.values(sourceFiles)) { | ||
lh += value.lh; | ||
lf += value.lf; | ||
} | ||
return lf > 0 ? (lh / lf) * 100 : -1; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import assert from 'assert/strict'; | ||
import { join } from 'path'; | ||
import { getReports } from './lcov.js'; | ||
|
||
describe('reports/lcov', () => { | ||
describe('#getReports()', () => { | ||
it('should return a coverage report', async () => { | ||
const reports = await getReports(join(process.cwd(), 'test/data/lcov')); | ||
assert.equal(reports.length, 1); | ||
assert.deepEqual(reports, [ | ||
{ type: 'coverage', data: { coverage: 47 } } | ||
]); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
TN: | ||
SF:a.js | ||
LH:20 | ||
LF:40 | ||
end_of_record | ||
SF:b.js | ||
LH:30 | ||
LF:70 | ||
end_of_record | ||
SF:c.js | ||
LH:30 | ||
LF:90 | ||
end_of_record | ||
TN: | ||
SF:a.js | ||
LH:34 | ||
LF:40 | ||
end_of_record | ||
SF:b.js | ||
LH:20 | ||
LF:70 | ||
end_of_record |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters