Skip to content

Commit

Permalink
fix: add in-memory source code caching to support detail reports on c…
Browse files Browse the repository at this point in the history
…ompiled CoffeeScript and other files with intermediate pre-processing
  • Loading branch information
unframework authored and Marsup committed Nov 26, 2014
1 parent c5d0b4c commit c1e542a
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 53 deletions.
7 changes: 6 additions & 1 deletion lib/preprocessor.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
var istanbul = require('istanbul'),
ibrik = require('ibrik'),
minimatch = require('minimatch');
minimatch = require('minimatch'),
globalSourceCache = require('./sourceCache');

var createCoveragePreprocessor = function(logger, basePath, reporters, coverageReporter) {
var log = logger.create('preprocessor.coverage');
var instrumenterOverrides = (coverageReporter && coverageReporter.instrumenter) || {};
var instrumenters = {istanbul: istanbul, ibrik: ibrik};
var sourceCache = globalSourceCache.getByBasePath(basePath);

// if coverage reporter is not used, do not preprocess the files
if (reporters.indexOf('coverage') === -1) {
Expand Down Expand Up @@ -55,6 +57,9 @@ var createCoveragePreprocessor = function(logger, basePath, reporters, coverageR
file.path = file.path.replace(/\.coffee$/, '.js');
}

// remember the actual immediate instrumented JS for given original path
sourceCache[jsPath] = content;

done(instrumentedCode);
});
};
Expand Down
29 changes: 13 additions & 16 deletions lib/reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,31 @@ var fs = require('fs');
var util = require('util');
var istanbul = require('istanbul');
var dateformat = require('dateformat');
var globalSourceCache = require('./sourceCache');


var Store = istanbul.Store;

var BasePathStore = function(opts) {
var SourceCacheStore = function(opts) {
Store.call(this, opts);
opts = opts || {};
this.basePath = opts.basePath;
this.delegate = Store.create('fslookup');
this.sourceCache = opts.sourceCache;
};
BasePathStore.TYPE = 'basePathlookup';
util.inherits(BasePathStore, Store);
SourceCacheStore.TYPE = 'sourceCacheLookup';
util.inherits(SourceCacheStore, Store);

Store.mix(BasePathStore, {
Store.mix(SourceCacheStore, {
keys : function() {
return this.delegate.keys();
},
toKey : function(key) {
if (key.indexOf('./') === 0) { return path.join(this.basePath, key); }
return key;
throw 'not implemented';
},
get : function(key) {
return this.delegate.get(this.toKey(key));
return this.sourceCache[key];
},
hasKey : function(key) {
return this.delegate.hasKey(this.toKey(key));
return this.sourceCache.hasOwnProperty(key);
},
set : function(key, contents) {
return this.delegate.set(this.toKey(key), contents);
throw 'not applicable';
}
});

Expand All @@ -42,6 +38,7 @@ var CoverageReporter = function(rootConfig, helper, logger) {
var config = rootConfig.coverageReporter || {};
var basePath = rootConfig.basePath;
var reporters = config.reporters;
var sourceCache = globalSourceCache.getByBasePath(basePath);

if (!helper.isDefined(reporters)) {
reporters = [config];
Expand Down Expand Up @@ -132,8 +129,8 @@ var CoverageReporter = function(rootConfig, helper, logger) {
log.debug('Writing coverage to %s', outputDir);
var options = helper.merge({}, reporterConfig, {
dir : outputDir,
sourceStore : new BasePathStore({
basePath : basePath
sourceStore : new SourceCacheStore({
sourceCache: sourceCache
})
});
var reporter = istanbul.Report.create(reporterConfig.type || 'html', options);
Expand Down
6 changes: 6 additions & 0 deletions lib/sourceCache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

var cacheByBasePath = {};

exports.getByBasePath = function (basePath) {
return cacheByBasePath[basePath] ? cacheByBasePath[basePath] : (cacheByBasePath[basePath] = {});
};
52 changes: 16 additions & 36 deletions test/reporter.spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,6 @@ describe 'reporter', ->
mockStore = sinon.spy()
mockStore.mix = (fn, obj) ->
istanbul.Store.mix fn, obj
mockFslookup = sinon.stub
keys: ->
get: ->
hasKey: ->
set: ->
mockStore.create = sinon.stub().returns mockFslookup

mockAdd = sinon.spy()
mockDispose = sinon.spy()
Expand Down Expand Up @@ -56,40 +50,26 @@ describe 'reporter', ->
beforeEach ->
m = loadFile __dirname + '/../lib/reporter.js', mocks

describe 'BasePathStore', ->
describe 'SourceCacheStore', ->
options = store = null

beforeEach ->
options =
basePath: 'path/to/coverage/'
store = new m.BasePathStore options

describe 'toKey', ->
it 'should concat relative path and basePath', ->
expect(store.toKey './foo').to.deep.equal path.join(options.basePath, 'foo')

it 'should does not concat absolute path and basePath', ->
expect(store.toKey '/foo').to.deep.equal '/foo'

it 'should call keys and delegate to inline store', ->
store.keys()
expect(mockFslookup.keys).to.have.been.called

it 'should call get and delegate to inline store', ->
key = './path/to/js'
store.get(key)
expect(mockFslookup.get).to.have.been.calledWith path.join(options.basePath, key)

it 'should call hasKey and delegate to inline store', ->
key = './path/to/js'
store.hasKey(key)
expect(mockFslookup.hasKey).to.have.been.calledWith path.join(options.basePath, key)

it 'should call set and delegate to inline store', ->
key = './path/to/js'
content = 'any content'
store.set key, content
expect(mockFslookup.set).to.have.been.calledWith path.join(options.basePath, key), content
sourceCache: { './foo': 'TEST_SRC_DATA' }
store = new m.SourceCacheStore options

it 'should fail on call to keys', ->
expect(-> store.keys()).to.throw()

it 'should call get and check cache data', ->
expect(store.get('./foo')).to.equal 'TEST_SRC_DATA'

it 'should call hasKey and check cache data', ->
expect(store.hasKey('./foo')).to.be.true
expect(store.hasKey('./bar')).to.be.false

it 'should fail on call to set', ->
expect(-> store.set()).to.throw()

describe 'CoverageReporter', ->
rootConfig = emitter = reporter = null
Expand Down

0 comments on commit c1e542a

Please sign in to comment.