Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / istanbul / lib / command / common / run-with-cover.js
1 /*
2  Copyright (c) 2012, Yahoo! Inc.  All rights reserved.
3  Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms.
4  */
5 var Module = require('module'),
6     path = require('path'),
7     fs = require('fs'),
8     nopt = require('nopt'),
9     which = require('which'),
10     mkdirp = require('mkdirp'),
11     existsSync = fs.existsSync || path.existsSync,
12     inputError = require('../../util/input-error'),
13     matcherFor = require('../../util/file-matcher').matcherFor,
14     Instrumenter = require('../../instrumenter'),
15     Collector = require('../../collector'),
16     formatOption = require('../../util/help-formatter').formatOption,
17     hook = require('../../hook'),
18     Reporter = require('../../reporter'),
19     resolve = require('resolve'),
20     configuration = require('../../config');
21
22 function usage(arg0, command) {
23
24     console.error('\nUsage: ' + arg0 + ' ' + command + ' [<options>] <executable-js-file-or-command> [-- <arguments-to-jsfile>]\n\nOptions are:\n\n'
25         + [
26             formatOption('--config <path-to-config>', 'the configuration file to use, defaults to .istanbul.yml'),
27             formatOption('--root <path> ', 'the root path to look for files to instrument, defaults to .'),
28             formatOption('-x <exclude-pattern> [-x <exclude-pattern>]', 'one or more fileset patterns e.g. "**/vendor/**"'),
29             formatOption('-i <include-pattern> [-i <include-pattern>]', 'one or more fileset patterns e.g. "**/*.js"'),
30             formatOption('--[no-]default-excludes', 'apply default excludes [ **/node_modules/**, **/test/**, **/tests/** ], defaults to true'),
31             formatOption('--hook-run-in-context', 'hook vm.runInThisContext in addition to require (supports RequireJS), defaults to false'),
32             formatOption('--post-require-hook <file> | <module>', 'JS module that exports a function for post-require processing'),
33             formatOption('--report <format> [--report <format>] ', 'report format, defaults to lcov (= lcov.info + HTML)'),
34             formatOption('--dir <report-dir>', 'report directory, defaults to ./coverage'),
35             formatOption('--print <type>', 'type of report to print to console, one of summary (default), detail, both or none'),
36             formatOption('--verbose, -v', 'verbose mode'),
37             formatOption('--[no-]preserve-comments', 'remove / preserve comments in the output, defaults to false'),
38             formatOption('--include-all-sources', 'instrument all unused sources after running tests, defaults to false'),
39             formatOption('--[no-]include-pid', 'include PID in output coverage filename')
40         ].join('\n\n') + '\n');
41     console.error('\n');
42 }
43
44 function run(args, commandName, enableHooks, callback) {
45
46     var template = {
47             config: path,
48             root: path,
49             x: [ Array, String ],
50             report: [Array, String ],
51             dir: path,
52             verbose: Boolean,
53             yui: Boolean,
54             'default-excludes': Boolean,
55             print: String,
56             'self-test': Boolean,
57             'hook-run-in-context': Boolean,
58             'post-require-hook': String,
59             'preserve-comments': Boolean,
60             'include-all-sources': Boolean,
61             'preload-sources': Boolean,
62             i: [ Array, String ],
63             'include-pid': Boolean
64         },
65         opts = nopt(template, { v : '--verbose' }, args, 0),
66         overrides = {
67             verbose: opts.verbose,
68             instrumentation: {
69                 root: opts.root,
70                 'default-excludes': opts['default-excludes'],
71                 excludes: opts.x,
72                 'include-all-sources': opts['include-all-sources'],
73                 'preload-sources': opts['preload-sources'],
74                 'include-pid': opts['include-pid']
75             },
76             reporting: {
77                 reports: opts.report,
78                 print: opts.print,
79                 dir: opts.dir
80             },
81             hooks: {
82                 'hook-run-in-context': opts['hook-run-in-context'],
83                 'post-require-hook': opts['post-require-hook'],
84                 'handle-sigint': opts['handle-sigint']
85             }
86         },
87         config = configuration.loadFile(opts.config, overrides),
88         verbose = config.verbose,
89         cmdAndArgs = opts.argv.remain,
90         preserveComments = opts['preserve-comments'],
91         includePid = opts['include-pid'],
92         cmd,
93         cmdArgs,
94         reportingDir,
95         reporter = new Reporter(config),
96         runFn,
97         excludes;
98
99     if (cmdAndArgs.length === 0) {
100         return callback(inputError.create('Need a filename argument for the ' + commandName + ' command!'));
101     }
102
103     cmd = cmdAndArgs.shift();
104     cmdArgs = cmdAndArgs;
105
106     if (!existsSync(cmd)) {
107         try {
108             cmd = which.sync(cmd);
109         } catch (ex) {
110             return callback(inputError.create('Unable to resolve file [' + cmd + ']'));
111         }
112     } else {
113         cmd = path.resolve(cmd);
114     }
115
116     runFn = function () {
117         process.argv = ["node", cmd].concat(cmdArgs);
118         if (verbose) {
119             console.log('Running: ' + process.argv.join(' '));
120         }
121         process.env.running_under_istanbul=1;
122         Module.runMain(cmd, null, true);
123     };
124
125     excludes = config.instrumentation.excludes(true);
126
127     if (enableHooks) {
128         reportingDir = path.resolve(config.reporting.dir());
129         mkdirp.sync(reportingDir); //ensure we fail early if we cannot do this
130         reporter.dir = reportingDir;
131         reporter.addAll(config.reporting.reports());
132         if (config.reporting.print() !== 'none') {
133             switch (config.reporting.print()) {
134             case 'detail':
135                 reporter.add('text');
136                 break;
137             case 'both':
138                 reporter.add('text');
139                 reporter.add('text-summary');
140                 break;
141             default:
142                 reporter.add('text-summary');
143                 break;
144             }
145         }
146
147         excludes.push(path.relative(process.cwd(), path.join(reportingDir, '**', '*')));
148         matcherFor({
149             root: config.instrumentation.root() || process.cwd(),
150             includes: opts.i || config.instrumentation.extensions().map(function (ext) {
151                 return '**/*' + ext;
152             }),
153             excludes: excludes
154         },
155             function (err, matchFn) {
156                 if (err) { return callback(err); }
157
158                 var coverageVar = '$$cov_' + new Date().getTime() + '$$',
159                     instrumenter = new Instrumenter({ coverageVariable: coverageVar , preserveComments: preserveComments}),
160                     transformer = instrumenter.instrumentSync.bind(instrumenter),
161                     hookOpts = { verbose: verbose, extensions: config.instrumentation.extensions() },
162                     postRequireHook = config.hooks.postRequireHook(),
163                     postLoadHookFile;
164
165                 if (postRequireHook) {
166                     postLoadHookFile = path.resolve(postRequireHook);
167                 } else if (opts.yui) { //EXPERIMENTAL code: do not rely on this in anyway until the docs say it is allowed
168                     postLoadHookFile = path.resolve(__dirname, '../../util/yui-load-hook');
169                 }
170
171                 if (postRequireHook) {
172                     if (!existsSync(postLoadHookFile)) { //assume it is a module name and resolve it
173                         try {
174                             postLoadHookFile = resolve.sync(postRequireHook, { basedir: process.cwd() });
175                         } catch (ex) {
176                             if (verbose) { console.error('Unable to resolve [' + postRequireHook + '] as a node module'); }
177                             callback(ex);
178                             return;
179                         }
180                     }
181                 }
182                 if (postLoadHookFile) {
183                     if (verbose) { console.error('Use post-load-hook: ' + postLoadHookFile); }
184                     hookOpts.postLoadHook = require(postLoadHookFile)(matchFn, transformer, verbose);
185                 }
186
187                 if (opts['self-test']) {
188                     hook.unloadRequireCache(matchFn);
189                 }
190                 // runInThisContext is used by RequireJS [issue #23]
191                 if (config.hooks.hookRunInContext()) {
192                     hook.hookRunInThisContext(matchFn, transformer, hookOpts);
193                 }
194                 hook.hookRequire(matchFn, transformer, hookOpts);
195
196                 //initialize the global variable to stop mocha from complaining about leaks
197                 global[coverageVar] = {};
198
199                 // enable passing --handle-sigint to write reports on SIGINT.
200                 // This allows a user to manually kill a process while
201                 // still getting the istanbul report.
202                 if (config.hooks.handleSigint()) {
203                     process.once('SIGINT', process.exit);
204                 }
205
206                 process.once('exit', function () {
207                     var pidExt = includePid ? ('-' + process.pid) : '',
208                         file = path.resolve(reportingDir, 'coverage' + pidExt + '.json'),
209                         collector,
210                         cov;
211                     if (typeof global[coverageVar] === 'undefined' || Object.keys(global[coverageVar]).length === 0) {
212                         console.error('No coverage information was collected, exit without writing coverage information');
213                         return;
214                     } else {
215                         cov = global[coverageVar];
216                     }
217                     //important: there is no event loop at this point
218                     //everything that happens in this exit handler MUST be synchronous
219                     if (config.instrumentation.includeAllSources()) {
220                         // Files that are not touched by code ran by the test runner is manually instrumented, to
221                         // illustrate the missing coverage.
222                         matchFn.files.forEach(function (file) {
223                             if (!cov[file]) {
224                                 transformer(fs.readFileSync(file, 'utf-8'), file);
225
226                                 // When instrumenting the code, istanbul will give each FunctionDeclaration a value of 1 in coverState.s,
227                                 // presumably to compensate for function hoisting. We need to reset this, as the function was not hoisted,
228                                 // as it was never loaded.
229                                 Object.keys(instrumenter.coverState.s).forEach(function (key) {
230                                     instrumenter.coverState.s[key] = 0;
231                                 });
232
233                                 cov[file] = instrumenter.coverState;
234                             }
235                         });
236                     }
237                     mkdirp.sync(reportingDir); //yes, do this again since some test runners could clean the dir initially created
238                     if (config.reporting.print() !== 'none') {
239                         console.error('=============================================================================');
240                         console.error('Writing coverage object [' + file + ']');
241                     }
242                     fs.writeFileSync(file, JSON.stringify(cov), 'utf8');
243                     collector = new Collector();
244                     collector.add(cov);
245                     if (config.reporting.print() !== 'none') {
246                         console.error('Writing coverage reports at [' + reportingDir + ']');
247                         console.error('=============================================================================');
248                     }
249                     reporter.write(collector, true, callback);
250                 });
251                 runFn();
252             });
253     } else {
254         runFn();
255     }
256 }
257
258 module.exports = {
259     run: run,
260     usage: usage
261 };