feat(diff-coverage): enhance coverage reporting for multi-line statements and branches (#33516)

Co-authored-by: CodingOnStar <hanxujiang@dify.com>
This commit is contained in:
Coding On Star 2026-03-16 15:09:46 +08:00 committed by GitHub
parent a01c384f5b
commit 7daec9717d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 66 additions and 4 deletions

View File

@ -79,6 +79,23 @@ describe('check-components-diff-coverage helpers', () => {
})
})
it('should report the first changed line inside a multi-line uncovered statement', () => {
const entry = {
s: { 0: 0 },
statementMap: {
0: { start: { line: 10 }, end: { line: 14 } },
},
}
const coverage = getChangedStatementCoverage(entry, new Set([13, 14]))
expect(coverage).toEqual({
covered: 0,
total: 1,
uncoveredLines: [13],
})
})
it('should fail changed lines when a source file has no coverage entry', () => {
const coverage = getChangedStatementCoverage(undefined, new Set([42, 43]))
@ -118,6 +135,36 @@ describe('check-components-diff-coverage helpers', () => {
})
})
it('should report the first changed line inside a multi-line uncovered branch arm', () => {
const entry = {
b: {
0: [0, 0],
},
branchMap: {
0: {
line: 30,
loc: { start: { line: 30 }, end: { line: 35 } },
locations: [
{ start: { line: 31 }, end: { line: 34 } },
{ start: { line: 35 }, end: { line: 38 } },
],
type: 'if',
},
},
}
const coverage = getChangedBranchCoverage(entry, new Set([33]))
expect(coverage).toEqual({
covered: 0,
total: 2,
uncoveredBranches: [
{ armIndex: 0, line: 33 },
{ armIndex: 1, line: 35 },
],
})
})
it('should ignore changed lines with valid pragma reasons and report invalid pragmas', () => {
const sourceCode = [
'const a = 1',

View File

@ -100,7 +100,7 @@ export function getChangedStatementCoverage(entry, changedLines) {
continue
}
uncoveredLines.push(statement.start.line)
uncoveredLines.push(getFirstChangedLineInRange(statement, normalizedChangedLines))
}
return {
@ -111,6 +111,7 @@ export function getChangedStatementCoverage(entry, changedLines) {
}
export function getChangedBranchCoverage(entry, changedLines) {
const normalizedChangedLines = [...(changedLines ?? [])].sort((a, b) => a - b)
if (!entry) {
return {
covered: 0,
@ -141,7 +142,7 @@ export function getChangedBranchCoverage(entry, changedLines) {
const location = locations[armIndex] ?? branch.loc ?? branch
uncoveredBranches.push({
armIndex,
line: getLocationStartLine(location) ?? branch.line ?? 1,
line: getFirstChangedLineInRange(location, normalizedChangedLines, branch.line ?? 1),
})
}
}
@ -247,6 +248,20 @@ function rangeIntersectsChangedLines(location, changedLines) {
return false
}
function getFirstChangedLineInRange(location, changedLines, fallbackLine = 1) {
const startLine = getLocationStartLine(location)
const endLine = getLocationEndLine(location) ?? startLine
if (!startLine || !endLine)
return startLine ?? fallbackLine
for (const lineNumber of changedLines) {
if (lineNumber >= startLine && lineNumber <= endLine)
return lineNumber
}
return startLine ?? fallbackLine
}
function getLocationStartLine(location) {
return location?.start?.line ?? location?.line ?? null
}

View File

@ -338,8 +338,8 @@ function buildSummary({
}
lines.push(`Changed source files checked: ${changedSourceFiles.length}`)
lines.push(`Changed statement coverage: ${percentage(diffTotals.statements.covered, diffTotals.statements.total).toFixed(2)}%`)
lines.push(`Changed branch coverage: ${percentage(diffTotals.branches.covered, diffTotals.branches.total).toFixed(2)}%`)
lines.push(`Changed statement coverage: ${formatDiffPercent(diffTotals.statements)}`)
lines.push(`Changed branch coverage: ${formatDiffPercent(diffTotals.branches)}`)
return lines
}