Skip to content

Commit

Permalink
fix(instrumenter): corect mutant location in .vue and .html files
Browse files Browse the repository at this point in the history
Report the correct locations of mutants by offsetting location in mutant collector instead of html parser (#2790)
  • Loading branch information
Djaler committed Mar 11, 2021
1 parent 86f644a commit 547a25c
Show file tree
Hide file tree
Showing 9 changed files with 402 additions and 359 deletions.
22 changes: 14 additions & 8 deletions packages/instrumenter/src/mutant.ts
Expand Up @@ -18,7 +18,13 @@ export class Mutant {
public readonly mutatorName: string;
public readonly ignoreReason: string | undefined;

constructor(public readonly id: number, public readonly fileName: string, specs: NamedNodeMutation) {
constructor(
public readonly id: number,
public readonly fileName: string,
specs: NamedNodeMutation,
public readonly positionOffset: number = 0,
public readonly lineOffset: number = 0
) {
this.original = specs.original;
this.replacement = specs.replacement;
this.mutatorName = specs.mutatorName;
Expand All @@ -30,25 +36,25 @@ export class Mutant {
return {
fileName: this.fileName,
id: this.id,
location: toApiLocation(this.original.loc!),
location: toApiLocation(this.original.loc!, this.lineOffset),
mutatorName: this.mutatorName,
range: [this.original.start!, this.original.end!],
range: [this.original.start! + this.positionOffset, this.original.end! + this.positionOffset],
replacement: this.replacementCode,
ignoreReason: this.ignoreReason,
};
}
}

function toApiLocation(source: types.SourceLocation): Location {
function toApiLocation(source: types.SourceLocation, lineOffset: number): Location {
return {
start: toPosition(source.start),
end: toPosition(source.end),
start: toPosition(source.start, lineOffset),
end: toPosition(source.end, lineOffset),
};
}

function toPosition(source: Position): Position {
function toPosition(source: Position, lineOffset: number): Position {
return {
column: source.column,
line: source.line - 1, // Stryker works 0-based internally
line: source.line + lineOffset - 1, // Stryker works 0-based internally
};
}
15 changes: 9 additions & 6 deletions packages/instrumenter/src/parsers/html-parser.ts
@@ -1,7 +1,6 @@
import type { Element } from 'angular-html-parser/lib/compiler/src/ml_parser/ast';
import type { ParseLocation } from 'angular-html-parser/lib/compiler/src/parse_util';

import { offsetLocations } from '../util/syntax-helpers';
import { HtmlAst, AstFormat, HtmlRootNode, TSAst, JSAst, ScriptFormat, AstByFormat } from '../syntax';

import { ParserContext } from './parser-context';
Expand Down Expand Up @@ -64,11 +63,15 @@ async function ngHtmlParser(text: string, fileName: string, parserContext: Parse
const ast = await parserContext.parse(content, fileName, scriptFormat);
if (ast) {
const offset = el.startSourceSpan!.end;
offsetLocations(ast.root, {
position: offset.offset,
column: offset.col,
line: offset.line + 1, // need to add 1, since ngHtmlParser lines start with 0
});
ast.root.start! += offset.offset;
ast.root.end! += offset.offset;
return {
...ast,
offset: {
position: offset.offset,
line: offset.line,
},
};
}
return ast;
}
Expand Down
4 changes: 4 additions & 0 deletions packages/instrumenter/src/syntax/index.ts
Expand Up @@ -18,6 +18,10 @@ export interface BaseAst {
originFileName: string;
rawContent: string;
root: Ast['root'];
offset?: {
position: number;
line: number;
};
}

/**
Expand Down
8 changes: 6 additions & 2 deletions packages/instrumenter/src/transformers/babel-transformer.ts
Expand Up @@ -12,7 +12,11 @@ import { AstFormat } from '../syntax';

import { AstTransformer } from '.';

export const transformBabel: AstTransformer<AstFormat.JS | AstFormat.TS> = ({ root, originFileName, rawContent }, mutantCollector, { options }) => {
export const transformBabel: AstTransformer<AstFormat.JS | AstFormat.TS> = (
{ root, originFileName, rawContent, offset },
mutantCollector,
{ options }
) => {
// Wrap the AST in a `new File`, so `nodePath.buildCodeFrameError` works
// https://github.com/babel/babel/issues/11889
const file = new File({ filename: originFileName }, { code: rawContent, ast: root });
Expand All @@ -23,7 +27,7 @@ export const transformBabel: AstTransformer<AstFormat.JS | AstFormat.TS> = ({ ro
path.skip();
} else {
mutate(path, options).forEach((mutant) => {
mutantCollector.add(originFileName, mutant);
mutantCollector.add(originFileName, mutant, offset?.position, offset?.line);
});
}
},
Expand Down
6 changes: 4 additions & 2 deletions packages/instrumenter/src/transformers/mutant-collector.ts
Expand Up @@ -14,13 +14,15 @@ export class MutantCollector {
* Adds a mutant to the internal mutant list.
* @param fileName file name that houses the mutant
* @param mutationSpecs the named node mutation to be added
* @param positionOffset position offset of mutant node
* @param lineOffset line offset of mutant node
* @returns The mutant (for testability)
*/
public add(fileName: string, mutationSpecs: NamedNodeMutation): Mutant {
public add(fileName: string, mutationSpecs: NamedNodeMutation, positionOffset = 0, lineOffset = 0): Mutant {
mutationSpecs.replacement.end = mutationSpecs.original.end;
mutationSpecs.replacement.start = mutationSpecs.original.start;
mutationSpecs.replacement.loc = mutationSpecs.original.loc;
const mutant = new Mutant(this._mutants.length, fileName, mutationSpecs);
const mutant = new Mutant(this._mutants.length, fileName, mutationSpecs, positionOffset, lineOffset);
this._mutants.push(mutant);
if (mutant.ignoreReason === undefined) {
// Only place mutants that are not ignored
Expand Down

0 comments on commit 547a25c

Please sign in to comment.