stryker
Version:
The extendable JavaScript mutation testing framework
201 lines • 9.62 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var _ = require("lodash");
var TestableMutant_1 = require("../TestableMutant");
var objectUtils_1 = require("../utils/objectUtils");
var SourceFile_1 = require("../SourceFile");
var LocationHelper_1 = require("../utils/LocationHelper");
var plugin_1 = require("stryker-api/plugin");
var di_1 = require("../di");
var StatementIndexKind;
(function (StatementIndexKind) {
StatementIndexKind[StatementIndexKind["function"] = 0] = "function";
StatementIndexKind[StatementIndexKind["statement"] = 1] = "statement";
})(StatementIndexKind || (StatementIndexKind = {}));
var MutantTestMatcher = /** @class */ (function () {
function MutantTestMatcher(log, options, reporter, input, initialRunResult) {
this.log = log;
this.options = options;
this.reporter = reporter;
this.input = input;
this.initialRunResult = initialRunResult;
}
Object.defineProperty(MutantTestMatcher.prototype, "baseline", {
get: function () {
if (this.isCoveragePerTestResult(this.initialRunResult.runResult.coverage)) {
return this.initialRunResult.runResult.coverage.baseline;
}
else {
return null;
}
},
enumerable: true,
configurable: true
});
MutantTestMatcher.prototype.matchWithMutants = function (mutants) {
var _this = this;
var testableMutants = this.createTestableMutants(mutants);
if (this.options.coverageAnalysis === 'off') {
testableMutants.forEach(function (mutant) { return mutant.selectAllTests(_this.initialRunResult.runResult, TestableMutant_1.TestSelectionResult.Success); });
}
else if (!this.initialRunResult.runResult.coverage) {
this.log.warn('No coverage result found, even though coverageAnalysis is "%s". Assuming that all tests cover each mutant. This might have a big impact on the performance.', this.options.coverageAnalysis);
testableMutants.forEach(function (mutant) { return mutant.selectAllTests(_this.initialRunResult.runResult, TestableMutant_1.TestSelectionResult.FailedButAlreadyReported); });
}
else {
testableMutants.forEach(function (testableMutant) { return _this.enrichWithCoveredTests(testableMutant); });
}
this.reporter.onAllMutantsMatchedWithTests(Object.freeze(testableMutants.map(this.mapMutantOnMatchedMutant)));
return testableMutants;
};
MutantTestMatcher.prototype.enrichWithCoveredTests = function (testableMutant) {
var _this = this;
var transpiledLocation = this.initialRunResult.sourceMapper.transpiledLocationFor({
fileName: testableMutant.mutant.fileName,
location: testableMutant.location
});
var fileCoverage = this.initialRunResult.coverageMaps[transpiledLocation.fileName];
var statementIndex = this.findMatchingStatement(new LocationHelper_1.default(transpiledLocation.location), fileCoverage);
if (statementIndex) {
if (this.isCoveredByBaseline(transpiledLocation.fileName, statementIndex)) {
testableMutant.selectAllTests(this.initialRunResult.runResult, TestableMutant_1.TestSelectionResult.Success);
}
else {
this.initialRunResult.runResult.tests.forEach(function (testResult, id) {
if (_this.isCoveredByTest(id, transpiledLocation.fileName, statementIndex)) {
testableMutant.selectTest(testResult, id);
}
});
}
}
else {
// Could not find a statement corresponding to this mutant
// This can happen when for example mutating a TypeScript interface
// It should result in an early result during mutation testing
// Lets delay error reporting for now
testableMutant.selectAllTests(this.initialRunResult.runResult, TestableMutant_1.TestSelectionResult.Failed);
}
};
MutantTestMatcher.prototype.isCoveredByBaseline = function (fileName, statementIndex) {
if (this.baseline) {
var coverageResult = this.baseline[fileName];
return this.isCoveredByCoverageCollection(coverageResult, statementIndex);
}
else {
return false;
}
};
MutantTestMatcher.prototype.isCoveredByTest = function (testId, fileName, statementIndex) {
var coverageCollection = this.findCoverageCollectionForTest(testId);
var coveredFile = coverageCollection && coverageCollection[fileName];
return this.isCoveredByCoverageCollection(coveredFile, statementIndex);
};
MutantTestMatcher.prototype.isCoveredByCoverageCollection = function (coveredFile, statementIndex) {
if (coveredFile) {
if (statementIndex.kind === StatementIndexKind.statement) {
return coveredFile.s[statementIndex.index] > 0;
}
else {
return coveredFile.f[statementIndex.index] > 0;
}
}
else {
return false;
}
};
MutantTestMatcher.prototype.createTestableMutants = function (mutants) {
var _this = this;
var sourceFiles = this.input.filesToMutate.map(function (file) { return new SourceFile_1.default(file); });
return objectUtils_1.filterEmpty(mutants.map(function (mutant, index) {
var sourceFile = sourceFiles.find(function (file) { return file.name === mutant.fileName; });
if (sourceFile) {
return new TestableMutant_1.default(index.toString(), mutant, sourceFile);
}
else {
_this.log.error("Mutant \"" + mutant.mutatorName + mutant.replacement + "\" is corrupt, because cannot find a text file with name " + mutant.fileName + ". List of source files: \n\t" + sourceFiles.map(function (s) { return s.name; }).join('\n\t'));
return null;
}
}));
};
/**
* Map the Mutant object on the MatchMutant Object.
* @param testableMutant The mutant.
* @returns The MatchedMutant
*/
MutantTestMatcher.prototype.mapMutantOnMatchedMutant = function (testableMutant) {
var matchedMutant = _.cloneDeep({
fileName: testableMutant.mutant.fileName,
id: testableMutant.id,
mutatorName: testableMutant.mutant.mutatorName,
replacement: testableMutant.mutant.replacement,
scopedTestIds: testableMutant.selectedTests.map(function (testSelection) { return testSelection.id; }),
timeSpentScopedTests: testableMutant.timeSpentScopedTests,
});
return Object.freeze(matchedMutant);
};
MutantTestMatcher.prototype.findMatchingStatement = function (location, fileCoverage) {
var statementIndex = this.findMatchingStatementInMap(location, fileCoverage.statementMap);
if (statementIndex) {
return {
index: statementIndex,
kind: StatementIndexKind.statement
};
}
else {
var functionIndex = this.findMatchingStatementInMap(location, fileCoverage.fnMap);
if (functionIndex) {
return {
index: functionIndex,
kind: StatementIndexKind.function
};
}
else {
return null;
}
}
};
/**
* Finds the smallest statement that covers a location
* @param needle The location to find.
* @param haystack the statement map or function map to search in.
* @returns The index of the smallest statement surrounding the location, or null if not found.
*/
MutantTestMatcher.prototype.findMatchingStatementInMap = function (needle, haystack) {
var smallestStatement = {
index: null,
location: LocationHelper_1.default.MAX_VALUE
};
if (haystack) {
Object.keys(haystack).forEach(function (statementId) {
var statementLocation = haystack[statementId];
if (needle.isCoveredBy(statementLocation) && smallestStatement.location.isSmallerArea(statementLocation)) {
smallestStatement = {
index: statementId,
location: new LocationHelper_1.default(statementLocation)
};
}
});
}
return smallestStatement.index;
};
MutantTestMatcher.prototype.findCoverageCollectionForTest = function (testId) {
if (this.initialRunResult.runResult.coverage) {
if (this.isCoveragePerTestResult(this.initialRunResult.runResult.coverage)) {
return this.initialRunResult.runResult.coverage.deviations[testId];
}
else {
return this.initialRunResult.runResult.coverage;
}
}
else {
return null;
}
};
MutantTestMatcher.prototype.isCoveragePerTestResult = function (_coverage) {
return this.options.coverageAnalysis === 'perTest';
};
MutantTestMatcher.inject = plugin_1.tokens(plugin_1.commonTokens.logger, plugin_1.commonTokens.options, di_1.coreTokens.reporter, di_1.coreTokens.inputFiles, di_1.coreTokens.initialRunResult);
return MutantTestMatcher;
}());
exports.MutantTestMatcher = MutantTestMatcher;
//# sourceMappingURL=MutantTestMatcher.js.map