action-surefire-report/utils.js

159 lines
6.1 KiB
JavaScript
Raw Permalink Normal View History

2020-05-19 21:00:21 +08:00
const glob = require('@actions/glob');
const core = require('@actions/core');
const fs = require('fs');
const parser = require('xml-js');
2020-05-19 21:00:21 +08:00
2022-12-28 14:58:33 +08:00
const resolveFileAndLine = (file, classname, output, isFilenameInOutput) => {
// extract filename from classname and remove suffix
2022-12-28 14:58:33 +08:00
let filename;
let filenameWithPackage;
2022-12-28 14:58:33 +08:00
if (isFilenameInOutput) {
filename = output.split(':')[0].trim();
filenameWithPackage = filename;
2022-12-28 14:58:33 +08:00
} else {
filename = file ? file : classname.split('.').slice(-1)[0].split('(')[0];
filenameWithPackage = classname.replace(/\./g, '/');
2022-12-28 14:58:33 +08:00
}
const escapedFilename = filename.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
const matches = output.match(new RegExp(`${escapedFilename}.*?:\\d+`, 'g'));
if (!matches) return {filename: filename, filenameWithPackage: filenameWithPackage, line: 1};
2020-05-19 21:00:21 +08:00
const [lastItem] = matches.slice(-1);
2020-07-10 04:27:15 +08:00
const [, line] = lastItem.split(':');
core.debug(`Resolved file ${filenameWithPackage} with name ${filename} and line ${line}`);
2020-05-19 21:00:21 +08:00
return {filename, filenameWithPackage, line: parseInt(line)};
2020-05-19 21:00:21 +08:00
};
const resolvePath = async filenameWithPackage => {
core.debug(`Resolving path for ${filenameWithPackage}`);
const globber = await glob.create([`**/${filenameWithPackage}.*`, `**/${filenameWithPackage}`].join('\n'), {followSymbolicLinks: false});
2020-05-19 21:00:21 +08:00
const results = await globber.glob();
core.debug(`Matched files: ${results}`);
const searchPath = globber.getSearchPaths()[0];
2020-10-15 19:38:01 +08:00
let path = '';
if (results.length) {
// skip various temp folders
const found = results.find(r => !r.includes('__pycache__') && !r.endsWith('.class'));
2020-10-15 19:38:01 +08:00
if (found) path = found.slice(searchPath.length + 1);
else path = filenameWithPackage;
2020-10-15 19:38:01 +08:00
} else {
path = filenameWithPackage;
2020-10-15 19:38:01 +08:00
}
2020-05-19 21:00:21 +08:00
core.debug(`Resolved path: ${path}`);
// canonicalize to make windows paths use forward slashes
const canonicalPath = path.replace(/\\/g, '/');
core.debug(`Canonical path: ${canonicalPath}`);
return canonicalPath;
2020-05-19 21:00:21 +08:00
};
function getTestsuites(report) {
if (report.testsuite) {
return [report.testsuite];
}
if (!report.testsuites || !report.testsuites.testsuite) {
return [];
}
if (Array.isArray(report.testsuites.testsuite)) {
return report.testsuites.testsuite;
}
return [report.testsuites.testsuite];
}
async function parseFile(file, isFilenameInStackTrace, ignoreFlakyTests) {
2020-05-19 21:00:21 +08:00
core.debug(`Parsing file ${file}`);
let count = 0;
let skipped = 0;
let annotations = [];
const data = await fs.promises.readFile(file);
const report = JSON.parse(parser.xml2json(data, {compact: true}));
core.debug(`parsed report: ${JSON.stringify(report)}`);
const testsuites = getTestsuites(report);
core.debug(`test suites: ${JSON.stringify(testsuites)}`);
2020-05-19 21:00:21 +08:00
2020-07-10 04:27:15 +08:00
for (const testsuite of testsuites) {
const testcases = Array.isArray(testsuite.testcase)
? testsuite.testcase
: testsuite.testcase
? [testsuite.testcase]
: [];
2020-07-10 04:27:15 +08:00
for (const testcase of testcases) {
count++;
if (testcase.skipped) skipped++;
if (testcase.failure || (testcase.flakyFailure && !ignoreFlakyTests) || testcase.error) {
let testcaseData =
(testcase.failure && testcase.failure._cdata) ||
(testcase.failure && testcase.failure._text) ||
(testcase.flakyFailure && testcase.flakyFailure._cdata) ||
(testcase.flakyFailure && testcase.flakyFailure._text) ||
(testcase.error && testcase.error._cdata) ||
(testcase.error && testcase.error._text) ||
'';
testcaseData = Array.isArray(testcaseData) ? testcaseData : [testcaseData];
const stackTrace = (testcaseData.length ? testcaseData.join('') : '').trim();
const message = (
(testcase.failure &&
testcase.failure._attributes &&
testcase.failure._attributes.message) ||
(testcase.flakyFailure &&
testcase.flakyFailure._attributes &&
testcase.flakyFailure._attributes.message) ||
(testcase.error &&
testcase.error._attributes &&
testcase.error._attributes.message) ||
stackTrace.split('\n').slice(0, 2).join('\n') ||
testcase._attributes.name
).trim();
const {filename, filenameWithPackage, line} = resolveFileAndLine(
testcase._attributes.file,
testcase._attributes.classname,
stackTrace,
isFilenameInStackTrace
);
const path = await resolvePath(filenameWithPackage);
const title = `${filename}.${testcase._attributes.name}`;
core.info(`${path}:${line} | ${message.replace(/\n/g, ' ')}`);
annotations.push({
path,
start_line: line,
end_line: line,
start_column: 0,
end_column: 0,
annotation_level: 'failure',
title,
message,
raw_details: stackTrace
});
2020-07-10 04:27:15 +08:00
}
2020-05-19 21:00:21 +08:00
}
}
return {count, skipped, annotations};
2020-05-19 21:00:21 +08:00
}
const parseTestReports = async (reportPaths, isFilenameInStackTrace, ignoreFlakyTests) => {
const globber = await glob.create(reportPaths, {followSymbolicLinks: false});
2020-05-19 21:00:21 +08:00
let annotations = [];
let count = 0;
let skipped = 0;
for await (const file of globber.globGenerator()) {
const {count: c, skipped: s, annotations: a} = await parseFile(file, isFilenameInStackTrace, ignoreFlakyTests);
if (c === 0) continue;
2020-05-19 21:00:21 +08:00
count += c;
skipped += s;
annotations = annotations.concat(a);
}
return {count, skipped, annotations};
2020-05-19 21:00:21 +08:00
};
module.exports = { resolveFileAndLine, resolvePath, parseFile, parseTestReports };