Skip to content

Commit 93c65c9

Browse files
authored
Merge pull request #28 from feddi-dev/chore/test-baseline-reporting
Add test baseline reporting
2 parents 5a5e20c + f4b5c8c commit 93c65c9

10 files changed

Lines changed: 945 additions & 2 deletions

File tree

.github/scripts/parse-jacoco.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
const fs = require('fs');
2+
3+
const zeroCov = { covered: 0, missed: 0 };
4+
5+
function parseJacocoXml(jacocoFile) {
6+
const result = { overall: {}, classes: {} };
7+
8+
if (!fs.existsSync(jacocoFile)) {
9+
return null;
10+
}
11+
12+
const xml = fs.readFileSync(jacocoFile, 'utf8');
13+
14+
const stripped = xml.replace(/<package[\s\S]*?<\/package>/g, '');
15+
const counterRe = /<counter type="(\w+)" missed="(\d+)" covered="(\d+)"\/>/g;
16+
let counterMatch;
17+
while ((counterMatch = counterRe.exec(stripped)) !== null) {
18+
const entry = {
19+
covered: parseInt(counterMatch[3], 10),
20+
missed: parseInt(counterMatch[2], 10),
21+
};
22+
if (counterMatch[1] === 'LINE') result.overall.line = entry;
23+
else if (counterMatch[1] === 'BRANCH') result.overall.branch = entry;
24+
else if (counterMatch[1] === 'METHOD') result.overall.method = entry;
25+
}
26+
27+
const packageRe = /<package\s+name="([^"]+)">([\s\S]*?)<\/package>/g;
28+
let packageMatch;
29+
while ((packageMatch = packageRe.exec(xml)) !== null) {
30+
const classRe = /<class\s+name="([^"]+)"[^>]*(?<!\/)>([\s\S]*?)<\/class>/g;
31+
let classMatch;
32+
while ((classMatch = classRe.exec(packageMatch[2])) !== null) {
33+
const className = classMatch[1].replace(/\//g, '.');
34+
const classBody = classMatch[2];
35+
const counters = {
36+
line: { ...zeroCov },
37+
branch: { ...zeroCov },
38+
method: { ...zeroCov },
39+
};
40+
41+
const classCounterRe = /<counter type="(\w+)" missed="(\d+)" covered="(\d+)"\/>/g;
42+
let classCounterMatch;
43+
while ((classCounterMatch = classCounterRe.exec(classBody)) !== null) {
44+
const entry = {
45+
covered: parseInt(classCounterMatch[3], 10),
46+
missed: parseInt(classCounterMatch[2], 10),
47+
};
48+
if (classCounterMatch[1] === 'LINE') counters.line = entry;
49+
else if (classCounterMatch[1] === 'BRANCH') counters.branch = entry;
50+
else if (classCounterMatch[1] === 'METHOD') counters.method = entry;
51+
}
52+
53+
const methods = [];
54+
const methodRe = /<method\s+name="([^"]+)"\s+desc="([^"]+)"(?:\s+line="(\d+)")?[^>]*>([\s\S]*?)<\/method>/g;
55+
let methodMatch;
56+
while ((methodMatch = methodRe.exec(classBody)) !== null) {
57+
const methodCounters = {
58+
line: { ...zeroCov },
59+
branch: { ...zeroCov },
60+
method: { ...zeroCov },
61+
};
62+
const methodCounterRe = /<counter type="(\w+)" missed="(\d+)" covered="(\d+)"\/>/g;
63+
let methodCounterMatch;
64+
while ((methodCounterMatch = methodCounterRe.exec(methodMatch[4])) !== null) {
65+
const entry = {
66+
covered: parseInt(methodCounterMatch[3], 10),
67+
missed: parseInt(methodCounterMatch[2], 10),
68+
};
69+
if (methodCounterMatch[1] === 'LINE') methodCounters.line = entry;
70+
else if (methodCounterMatch[1] === 'BRANCH') methodCounters.branch = entry;
71+
else if (methodCounterMatch[1] === 'METHOD') methodCounters.method = entry;
72+
}
73+
74+
if (methodCounters.line.covered + methodCounters.line.missed > 0) {
75+
methods.push({
76+
name: methodMatch[1],
77+
desc: methodMatch[2],
78+
line: methodMatch[3] ? parseInt(methodMatch[3], 10) : null,
79+
counters: methodCounters,
80+
});
81+
}
82+
}
83+
84+
if (counters.line.covered + counters.line.missed > 0) {
85+
result.classes[className] = counters;
86+
if (methods.length > 0) {
87+
result.classes[className].methods = methods;
88+
}
89+
}
90+
}
91+
}
92+
93+
return result;
94+
}
95+
96+
function pct(covered, missed) {
97+
const total = covered + missed;
98+
return total === 0 ? 0 : (covered / total) * 100;
99+
}
100+
101+
function counterPct(counter) {
102+
return pct(counter?.covered || 0, counter?.missed || 0);
103+
}
104+
105+
function isRegression(currPct, basePct, currMissed, baseMissed, tolerance = 0.05) {
106+
return currPct < basePct - tolerance && currMissed > baseMissed;
107+
}
108+
109+
module.exports = {
110+
parseJacocoXml,
111+
pct,
112+
counterPct,
113+
zeroCov,
114+
isRegression,
115+
};

0 commit comments

Comments
 (0)