Files
shoes/scripts/coverage-lines.test.mjs
T
cfal 38523d65ed Add performance reports, Linux coverage, and rule property tests (#228)
Add advisory selector/reload performance reports, optional Linux unit/process coverage with changed-line summaries, and bounded rule-parser property tests to make regressions and verification gaps easier to spot. Reports reject invalid measurements and account for timing noise without introducing hard performance gates; existing tests and production behavior remain unchanged.
2026-10-26 16:50:44 +08:00

91 lines
4.0 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import { changedCoverage, changedLines, coverageLines } from './coverage-lines.mjs';
const diff = 'diff --git a/src/a.rs b/src/a.rs\n+++ b/src/a.rs\n@@ -1 +1,3 @@\n+a\n+b\n+c\n';
const lcov = 'SF:src/a.rs\nDA:1,3\nDA:2,0\nend_of_record\n';
test('changed lines distinguish covered, uncovered and unknown/non-executable', () => {
const report = changedCoverage(diff, lcov);
assert.equal(report.percent, 50);
assert.deepEqual(report.files[0].covered, [1]);
assert.deepEqual(report.files[0].uncovered, [2]);
assert.deepEqual(report.files[0].not_in_report, [3]);
});
test('empty diff and deletion-only hunks are N/A, not 100 percent', () => {
assert.equal(changedCoverage('', lcov).percent, null);
assert.deepEqual([...changedLines(diff.replace('+1,3', '+1,0')).get('src/a.rs')], []);
});
test('deleted files contribute no candidate lines, including rename-as-delete/add diffs', () => {
const deletion = 'diff --git a/src/old.rs b/src/old.rs\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-a\n-b\n';
assert.equal(changedCoverage(deletion, lcov).percent, null);
assert.equal(changedCoverage(deletion + diff, lcov).percent, 50);
});
test('added source text resembling a file header cannot change the current file', () => {
const report = changedCoverage(diff + '+++ b/wrong.rs\n@@ -9 +9 @@\n+text\n', lcov);
assert.equal(report.files.length, 1);
assert.equal(report.files[0].file, 'src/a.rs');
assert.deepEqual(report.files[0].not_in_report, [3, 9]);
});
test('missing file coverage remains unknown', () => {
const report = changedCoverage(diff.replaceAll('a.rs', 'b.rs'), lcov);
assert.equal(report.percent, null);
assert.equal(report.files[0].instrumented, false);
assert.deepEqual(report.files[0].not_in_report, [1, 2, 3]);
});
test('LCOV merges duplicate file instantiations without double counting lines', () => {
const report = changedCoverage(diff, lcov + lcov.replace('DA:2,0', 'DA:2,4'));
assert.equal(report.percent, 100);
assert.equal(report.changed_executable_lines, 2);
});
test('zero-line, malformed, missing and incomplete coverage fails closed', () => {
for (const input of ['', 'SF:src/a.rs\nend_of_record', lcov.replace('DA:1,3', 'DA:1,-1'),
'DA:1,2\n', lcov.replace('end_of_record', ''), lcov.replace('DA:1,3', 'DA:0,2')]) {
assert.throws(() => coverageLines(input));
}
});
test('quoted paths and malformed hunk context fail explicitly', () => {
assert.throws(() => changedLines('+++ "b/weird path"\n@@ -1 +1 @@'));
assert.throws(() => changedLines('@@ -1 +1 @@'));
});
test('classification preserves hunk order, deduplicates lines and retains empty file records', () => {
const changes = [
'diff --git a/src/a.rs b/src/a.rs',
'+++ b/src/a.rs',
'@@ -9 +9,2 @@',
'+late',
'+unknown',
'@@ -1 +1,2 @@',
'+covered',
'+uncovered',
'@@ -9 +9 @@',
'+duplicate',
'diff --git a/src/b.rs b/src/b.rs',
'+++ b/src/b.rs',
'@@ -1 +1 @@',
'+uninstrumented',
].join('\n');
const counts = 'SF:src/a.rs\nDA:1,3\nDA:2,0\nDA:9,1\nend_of_record\nSF:src/b.rs\nend_of_record\n';
const report = changedCoverage(changes, counts);
assert.deepEqual(report.files, [
{ file: 'src/a.rs', changed_lines: 4, instrumented: true, covered: [9, 1], uncovered: [2], not_in_report: [10] },
{ file: 'src/b.rs', changed_lines: 1, instrumented: true, covered: [], uncovered: [], not_in_report: [1] },
]);
assert.equal(report.changed_executable_lines, 3);
assert.equal(report.percent, 200 / 3);
});
test('malformed LCOV retains specific validation diagnostics', () => {
const cases = [
['SF:src/a.rs\nSF:src/b.rs\n', 'unterminated LCOV record'],
['DA:1,1\n', 'invalid LCOV line count'],
['SF:src/a.rs\nDA:1,9007199254740992\n', 'invalid LCOV line count'],
['end_of_record\n', 'LCOV terminator without file'],
['SF:src/a.rs\nend_of_record\n', 'empty or incomplete coverage'],
];
for (const [input, message] of cases) {
assert.throws(() => coverageLines(input), { message });
}
});