Skip to content

Commit

Permalink
feat: readInitialCoverage - extracts empty coverage data from an inst…
Browse files Browse the repository at this point in the history
…rumented file

In support of finally fixing istanbuljs/babel-plugin-istanbul#4.
Blocked on a babel-traverse release that includes babel/babel#4746
  • Loading branch information
motiz88 committed Oct 17, 2016
1 parent 2fb9046 commit a725740
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 8 deletions.
11 changes: 11 additions & 0 deletions src/gen-var.js
@@ -0,0 +1,11 @@
import {createHash} from 'crypto';

// function to use for creating hashes
const SHA = 'sha1';

// generate a variable name from hashing the supplied file path
export default function genVar(filename) {
var hash = createHash(SHA);
hash.update(filename);
return 'cov_' + parseInt(hash.digest('hex').substr(0, 12), 16).toString(36);
}
3 changes: 2 additions & 1 deletion src/index.js
@@ -1,5 +1,6 @@
import Instrumenter from './instrumenter';
import programVisitor from './visitor';
import readInitialCoverage from './read-coverage';

/**
* createInstrumenter creates a new instrumenter with the
Expand All @@ -13,4 +14,4 @@ function createInstrumenter(opts) {

export {createInstrumenter};
export {programVisitor};

export {readInitialCoverage};
47 changes: 47 additions & 0 deletions src/read-coverage.js
@@ -0,0 +1,47 @@
import genVar from './gen-var';
import { parse } from 'babylon';
import traverse from 'babel-traverse';
import * as t from 'babel-types';

export default function readInitialCoverage (code, filename) {
if (typeof code !== 'string') {
throw new Error('Code must be a string');
}

// Parse as leniently as possible
const ast = parse(code, {
allowImportExportEverywhere: true,
allowReturnOutsideFunction: true,
allowSuperOutsideMethod: true,
sourceType: "script", // I think ?
plugins: ["*"]
});

const varName = genVar(filename);
let covPath;
traverse(ast, {
VariableDeclarator: function (path) {
if (t.isIdentifier(path.node.id) && path.node.id.name === varName) {
covPath = path.get('init');
path.stop();
}
}
});

if (!covPath) {
return null;
}

const binding = covPath.get('callee.body').scope.getOwnBinding("coverageData");
if (!binding) {
return null;
}

const dataPath = binding.path.get('init');
const coverageData = dataPath.evaluate();
if (!coverageData.confident) {
return null;
}

return coverageData.value;
}
7 changes: 1 addition & 6 deletions src/visitor.js
@@ -1,4 +1,5 @@
import {SourceCoverage} from './source-coverage';
import genVar from './gen-var';
import {createHash} from 'crypto';
import template from 'babel-template';

Expand All @@ -9,12 +10,6 @@ const COMMENT_RE = /^\s*istanbul\s+ignore\s+(if|else|next)(?=\W|$)/;
// source map URL pattern
const SOURCE_MAP_RE = /[#@]\s*sourceMappingURL=(.*)\s*$/m;

// generate a variable name from hashing the supplied file path
function genVar(filename) {
var hash = createHash(SHA);
hash.update(filename);
return 'cov_' + parseInt(hash.digest('hex').substr(0, 12), 16).toString(36);
}

// VisitState holds the state of the visitor, provides helper functions
// and is the `this` for the individual coverage visitors.
Expand Down
8 changes: 7 additions & 1 deletion test/util/verifier.js
Expand Up @@ -2,6 +2,7 @@ import Instrumenter from '../../src/instrumenter';
import {classes} from 'istanbul-lib-coverage';
import {assert} from 'chai';
import clone from 'clone';
import readInitialCoverage from '../../src/read-coverage';

var FileCoverage = classes.FileCoverage;

Expand Down Expand Up @@ -47,6 +48,10 @@ class Verifier {
assert.deepEqual(cov.f, expectedCoverage.functions || {}, 'Function coverage mismatch');
assert.deepEqual(cov.b, expectedCoverage.branches || {}, 'Branch coverage mismatch');
assert.deepEqual(cov.s, expectedCoverage.statements || {}, 'Statement coverage mismatch');

if (this.result.file) {
assert.deepEqual(readInitialCoverage(this.getGeneratedCode(), this.result.file), this.result.emptyCoverage);
}
}

getCoverage() {
Expand Down Expand Up @@ -134,7 +139,8 @@ function create(code, opts, instrumenterOpts) {
code: code,
generatedCode: instrumenterOutput,
coverageVariable: coverageVariable,
baseline: clone(g[coverageVariable])
baseline: clone(g[coverageVariable]),
emptyCoverage: instrumenter.lastFileCoverage()
});
}

Expand Down

0 comments on commit a725740

Please sign in to comment.