cbfl
Version:
library that can be used to automatically find points of failure in TypeScript Modules that are tested with Mocha
151 lines (150 loc) • 6.51 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FaultLocalizations = void 0;
const fs = __importStar(require("fs"));
class FaultLocalizations {
constructor() {
this.faultyFiles = new Map();
this.failedTests = new Map();
}
getOrCreateFaultyFile(coverage, changedLineCoveragePath) {
return (this.faultyFiles.get(changedLineCoveragePath) ||
this.createFaultyFile(coverage, changedLineCoveragePath));
}
createFaultyFile(coverage, changedLineCoveragePath) {
const functionMap = this.createFunctionMap(coverage);
const faultyFile = {
path: coverage.path,
lines: new Map(Object.keys(coverage.s).map((statementNumber) => [
parseInt(statementNumber) + 1,
new Set(),
]) // the line numbers that are put out are just the statements
),
statements: new Map(Object.keys(coverage.s).map((statementNumber) => [
parseInt(statementNumber),
new Set(),
])),
functions: new Map(Object.keys(coverage.f).map((functionID) => [
parseInt(functionID),
new Set(),
])),
functionMap,
};
this.faultyFiles.set(changedLineCoveragePath, faultyFile);
return faultyFile;
}
createFunctionMap(coverage) {
return new Map(Object.keys(coverage.fnMap).map((functionID) => {
const info = coverage.fnMap[functionID];
return [
parseInt(functionID),
{ start: info.decl.start, end: info.decl.end, name: info.name },
];
}));
}
addFailedTest(path, name) {
this.failedTests.set(path, { name });
}
addFailedLine(coverage, changedLineCoveragePath, lineNumber, failedTestPath) {
var _a, _b, _c;
const faultyFile = this.getOrCreateFaultyFile(coverage, changedLineCoveragePath);
(_a = faultyFile.lines.get(lineNumber)) === null || _a === void 0 ? void 0 : _a.add(failedTestPath);
(_b = faultyFile.statements.get(lineNumber - 1)) === null || _b === void 0 ? void 0 : _b.add(failedTestPath);
(_c = faultyFile.functions
.get(this.getFunctionIDForLineNumber(lineNumber, faultyFile))) === null || _c === void 0 ? void 0 : _c.add(failedTestPath);
}
getFunctionIDForLineNumber(lineNumber, faultyFile) {
let functionID = -1;
faultyFile.functionMap.forEach((info, id) => (functionID =
lineNumber >= info.start.line && lineNumber <= info.end.line
? id
: functionID));
return functionID;
}
generateComment() {
var _a;
let comment = "";
for (const [faultyFilePath, faultyFile] of this.faultyFiles) {
for (const [lineNumber, failedTests] of faultyFile.lines) {
if (failedTests.size === 1) {
const failedTestPath = failedTests.values().next().value;
comment += `\nThe failed Test ${(_a = this.failedTests.get(failedTestPath)) === null || _a === void 0 ? void 0 : _a.name} from the file "${failedTestPath}" ran through the line ${lineNumber} of the file ${faultyFilePath} which was changed recently changed.`;
}
else if (failedTests.size > 1) {
const failedTestsPaths = [...failedTests];
const failedTestsDescriptions = failedTestsPaths.map((failedTestPath) => {
var _a;
return `<li>**${((_a = this.failedTests.get(failedTestPath)) === null || _a === void 0 ? void 0 : _a.name) || ""}** (${failedTestPath})</li>`;
});
const failedTestsBlock = failedTestsDescriptions.reduce((prev, curr) => prev + curr, "");
comment += `The failed Tests <ul> ${failedTestsBlock} </ul> ran through the **line ${lineNumber}** of the file ${faultyFilePath} which was recently changed.`;
console.log(failedTestsPaths);
}
}
}
return comment;
}
async saveToFile(commitID) {
const data = {
faultyFiles: this.faultyFiles,
failedTests: this.failedTests,
};
const serialized = JSON.stringify(data, FaultLocalizations.replacer);
const path = "./faultLocalizations";
const filePath = `${path}/${commitID}.json`;
fs.mkdir(path, { recursive: true }, function (err) {
if (err)
return console.log(err);
return fs.promises.writeFile(filePath, serialized, "utf8");
});
return Promise.resolve();
}
static replacer(key, value) {
if (value instanceof Map) {
return {
dataType: "Map",
value: Array.from(value.entries()), // or with spread: value: [...value]
};
}
else if (value instanceof Set) {
return {
dataType: "Set",
value: Array.from(value.values()), // or with spread: value: [...value]
};
}
else {
return value;
}
}
static reviver(key, value) {
if (typeof value === "object" && value !== null) {
if (value.dataType === "Map") {
return new Map(value.value);
}
if (value.dataType === "Set") {
return new Set(value.value);
}
}
return value;
}
}
exports.FaultLocalizations = FaultLocalizations;