123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210 |
- /*
- Copyright 2012-2015, Yahoo Inc.
- Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
- */
- var path = require('path'),
- fs = require('fs'),
- mkdirp = require('mkdirp'),
- matcherFor = require('./file-matcher').matcherFor,
- libInstrument = require('istanbul-lib-instrument'),
- libCoverage = require('istanbul-lib-coverage'),
- libSourceMaps = require('istanbul-lib-source-maps'),
- hook = require('istanbul-lib-hook'),
- Reporter = require('./reporter');
- function getCoverFunctions(config, includes, callback) {
- if (!callback && typeof includes === 'function') {
- callback = includes;
- includes = null;
- }
- var includePid = config.instrumentation.includePid(),
- reportingDir = path.resolve(config.reporting.dir()),
- reporter = new Reporter(config),
- excludes = config.instrumentation.excludes(true),
- // The coverage variable below should have different value than
- // that of the coverage variable actually used by the instrumenter (in this case: __coverage__).
- // Otherwise if you run nyc to provide coverage on these files,
- // both the actual instrumenter and this file will write to the global coverage variable,
- // and provide unexpected coverage result.
- coverageVar = '$$coverage$$',
- instOpts = config.instrumentation.getInstrumenterOpts(),
- sourceMapStore = libSourceMaps.createSourceMapStore({}),
- instrumenter,
- transformer,
- fakeRequire,
- requireTransformer,
- reportInitFn,
- hookFn,
- unhookFn,
- coverageFinderFn,
- coverageSetterFn,
- beforeReportFn,
- exitFn;
- instOpts.coverageVariable = coverageVar;
- instOpts.sourceMapUrlCallback = function (file, url) {
- sourceMapStore.registerURL(file, url);
- };
- coverageFinderFn = function () {
- return global[coverageVar];
- };
- instrumenter = libInstrument.createInstrumenter(instOpts);
- transformer = function (code, file) {
- return instrumenter.instrumentSync(code, file);
- };
- requireTransformer = function (code, file) {
- var cov,
- ret = transformer(code, file);
- if (fakeRequire) {
- cov = coverageFinderFn();
- cov[file] = instrumenter.lastFileCoverage();
- return 'function x() {}';
- }
- return ret;
- };
- coverageSetterFn = function (cov) {
- global[coverageVar] = cov;
- };
- reportInitFn = function () {
- // set up reporter
- mkdirp.sync(reportingDir); //ensure we fail early if we cannot do this
- reporter.addAll(config.reporting.reports());
- if (config.reporting.print() !== 'none') {
- switch (config.reporting.print()) {
- case 'detail':
- reporter.add('text');
- break;
- case 'both':
- reporter.add('text');
- reporter.add('text-summary');
- break;
- default:
- reporter.add('text-summary');
- break;
- }
- }
- };
- var disabler;
- hookFn = function (matchFn) {
- var hookOpts = {
- verbose: config.verbose,
- extensions: config.instrumentation.extensions(),
- coverageVariable: coverageVar
- };
- //initialize the global variable
- coverageSetterFn({});
- reportInitFn();
- if (config.hooks.hookRunInContext()) {
- hook.hookRunInContext(matchFn, transformer, hookOpts);
- }
- if (config.hooks.hookRunInThisContext()) {
- hook.hookRunInThisContext(matchFn, transformer, hookOpts);
- }
- disabler = hook.hookRequire(matchFn, requireTransformer, hookOpts);
- };
- unhookFn = function (matchFn) {
- if (disabler) {
- disabler();
- }
- hook.unhookRunInThisContext();
- hook.unhookRunInContext();
- hook.unloadRequireCache(matchFn);
- };
- beforeReportFn = function (matchFn, cov) {
- var pidExt = includePid ? ('-' + process.pid) : '',
- file = path.resolve(reportingDir, 'coverage' + pidExt + '.raw.json'),
- missingFiles,
- finalCoverage = cov;
- if (config.instrumentation.includeAllSources()) {
- if (config.verbose) {
- console.error("Including all sources not require'd by tests");
- }
- missingFiles = [];
- // Files that are not touched by code ran by the test runner is manually instrumented, to
- // illustrate the missing coverage.
- matchFn.files.forEach(function (file) {
- if (!cov[file]) {
- missingFiles.push(file);
- }
- });
- fakeRequire = true;
- missingFiles.forEach(function (file) {
- try {
- require(file);
- } catch (ex) {
- console.error('Unable to post-instrument: ' + file);
- }
- });
- }
- if (Object.keys(finalCoverage).length >0) {
- if (config.verbose) {
- console.error('=============================================================================');
- console.error('Writing coverage object [' + file + ']');
- console.error('Writing coverage reports at [' + reportingDir + ']');
- console.error('=============================================================================');
- }
- fs.writeFileSync(file, JSON.stringify(finalCoverage), 'utf8');
- }
- return finalCoverage;
- };
- exitFn = function (matchFn, reporterOpts) {
- var cov,
- coverageMap,
- transformed;
- cov = coverageFinderFn() || {};
- cov = beforeReportFn(matchFn, cov);
- coverageSetterFn(cov);
- if (!(cov && typeof cov === 'object') || Object.keys(cov).length === 0) {
- console.error('No coverage information was collected, exit without writing coverage information');
- return;
- }
- coverageMap = libCoverage.createCoverageMap(cov);
- transformed = sourceMapStore.transformCoverage(coverageMap);
- reporterOpts.sourceFinder = transformed.sourceFinder;
- reporter.write(transformed.map, reporterOpts);
- sourceMapStore.dispose();
- };
- excludes.push(path.relative(process.cwd(), path.join(reportingDir, '**', '*')));
- includes = includes || config.instrumentation.extensions().map(function (ext) {
- return '**/*' + ext;
- });
- var matchConfig = {
- root: config.instrumentation.root() || /* istanbul ignore next: untestable */ process.cwd(),
- includes: includes,
- excludes: excludes
- };
- matcherFor(matchConfig, function (err, matchFn) {
- /* istanbul ignore if: untestable */
- if (err) {
- return callback(err);
- }
- return callback(null, {
- coverageFn: coverageFinderFn,
- hookFn: hookFn.bind(null, matchFn),
- exitFn: exitFn.bind(null, matchFn, {}), // XXX: reporter opts
- unhookFn: unhookFn.bind(null, matchFn)
- });
- });
- }
- module.exports = {
- getCoverFunctions: getCoverFunctions
- };
|