Bug:Fix file validation issue
[vnfsdk/refrepo.git] / vnfmarket / src / main / webapp / vnfmarket / node_modules / istanbul / lib / command / instrument.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
6 var path = require('path'),
7     mkdirp = require('mkdirp'),
8     once = require('once'),
9     async = require('async'),
10     fs = require('fs'),
11     filesFor = require('../util/file-matcher').filesFor,
12     nopt = require('nopt'),
13     Instrumenter = require('../instrumenter'),
14     inputError = require('../util/input-error'),
15     formatOption = require('../util/help-formatter').formatOption,
16     util = require('util'),
17     Command = require('./index'),
18     Collector = require('../collector'),
19     configuration = require('../config'),
20     verbose;
21
22
23 /*
24  * Chunk file size to use when reading non JavaScript files in memory
25  * and copying them over when using complete-copy flag.
26  */
27 var READ_FILE_CHUNK_SIZE = 64 * 1024;
28
29 function BaselineCollector(instrumenter) {
30     this.instrumenter = instrumenter;
31     this.collector = new Collector();
32     this.instrument = instrumenter.instrument.bind(this.instrumenter);
33
34     var origInstrumentSync = instrumenter.instrumentSync;
35     this.instrumentSync = function () {
36         var args = Array.prototype.slice.call(arguments),
37             ret = origInstrumentSync.apply(this.instrumenter, args),
38             baseline = this.instrumenter.lastFileCoverage(),
39             coverage = {};
40         coverage[baseline.path] = baseline;
41         this.collector.add(coverage);
42         return ret;
43     };
44     //monkey patch the instrumenter to call our version instead
45     instrumenter.instrumentSync = this.instrumentSync.bind(this);
46 }
47
48 BaselineCollector.prototype = {
49     getCoverage: function () {
50         return this.collector.getFinalCoverage();
51     }
52 };
53
54
55 function processFiles(instrumenter, inputDir, outputDir, relativeNames, extensions) {
56     var processor = function (name, callback) {
57             var inputFile = path.resolve(inputDir, name),
58                 outputFile = path.resolve(outputDir, name),
59                 inputFileExtenstion = path.extname(inputFile),
60                 isJavaScriptFile = extensions.indexOf(inputFileExtenstion) > -1,
61                 oDir = path.dirname(outputFile),
62                 readStream, writeStream;
63
64             callback = once(callback);
65             mkdirp.sync(oDir);
66
67             if (fs.statSync(inputFile).isDirectory()) {
68                 return callback(null, name);
69             }
70
71             if (isJavaScriptFile) {
72                 fs.readFile(inputFile, 'utf8', function (err, data) {
73                     if (err) { return callback(err, name); }
74                     instrumenter.instrument(data, inputFile, function (iErr, instrumented) {
75                         if (iErr) { return callback(iErr, name); }
76                         fs.writeFile(outputFile, instrumented, 'utf8', function (err) {
77                             return callback(err, name);
78                         });
79                     });
80                 });
81             }
82             else {
83                 // non JavaScript file, copy it as is
84                 readStream = fs.createReadStream(inputFile, {'bufferSize': READ_FILE_CHUNK_SIZE});
85                 writeStream = fs.createWriteStream(outputFile);
86
87                 readStream.on('error', callback);
88                 writeStream.on('error', callback);
89
90                 readStream.pipe(writeStream);
91                 readStream.on('end', function() {
92                     callback(null, name);
93                 });
94             }
95         },
96         q = async.queue(processor, 10),
97         errors = [],
98         count = 0,
99         startTime = new Date().getTime();
100
101     q.push(relativeNames, function (err, name) {
102         var inputFile, outputFile;
103         if (err) {
104             errors.push({ file: name, error: err.message || err.toString() });
105             inputFile = path.resolve(inputDir, name);
106             outputFile = path.resolve(outputDir, name);
107             fs.writeFileSync(outputFile, fs.readFileSync(inputFile));
108         }
109         if (verbose) {
110             console.log('Processed: ' + name);
111         } else {
112             if (count % 100 === 0) { process.stdout.write('.'); }
113         }
114         count += 1;
115     });
116
117     q.drain = function () {
118         var endTime = new Date().getTime();
119         console.log('\nProcessed [' + count + '] files in ' + Math.floor((endTime - startTime) / 1000) + ' secs');
120         if (errors.length > 0) {
121             console.log('The following ' + errors.length + ' file(s) had errors and were copied as-is');
122             console.log(errors);
123         }
124     };
125 }
126
127
128 function InstrumentCommand() {
129     Command.call(this);
130 }
131
132 InstrumentCommand.TYPE = 'instrument';
133 util.inherits(InstrumentCommand, Command);
134
135 Command.mix(InstrumentCommand, {
136     synopsis: function synopsis() {
137         return "instruments a file or a directory tree and writes the instrumented code to the desired output location";
138     },
139
140     usage: function () {
141         console.error('\nUsage: ' + this.toolName() + ' ' + this.type() + ' <options> <file-or-directory>\n\nOptions are:\n\n' +
142             [
143                 formatOption('--config <path-to-config>', 'the configuration file to use, defaults to .istanbul.yml'),
144                 formatOption('--output <file-or-dir>', 'The output file or directory. This is required when the input is a directory, ' +
145                     'defaults to standard output when input is a file'),
146                 formatOption('-x <exclude-pattern> [-x <exclude-pattern>]', 'one or more fileset patterns (e.g. "**/vendor/**" to ignore all files ' +
147                     'under a vendor directory). Also see the --default-excludes option'),
148                 formatOption('--variable <global-coverage-variable-name>', 'change the variable name of the global coverage variable from the ' +
149                     'default value of `__coverage__` to something else'),
150                 formatOption('--embed-source', 'embed source code into the coverage object, defaults to false'),
151                 formatOption('--[no-]compact', 'produce [non]compact output, defaults to compact'),
152                 formatOption('--[no-]preserve-comments', 'remove / preserve comments in the output, defaults to false'),
153                 formatOption('--[no-]complete-copy', 'also copy non-javascript files to the ouput directory as is, defaults to false'),
154                 formatOption('--save-baseline', 'produce a baseline coverage.json file out of all files instrumented'),
155                 formatOption('--baseline-file <file>', 'filename of baseline file, defaults to coverage/coverage-baseline.json')
156             ].join('\n\n') + '\n');
157         console.error('\n');
158     },
159
160     run: function (args, callback) {
161
162         var template = {
163                 config: path,
164                 output: path,
165                 x: [Array, String],
166                 variable: String,
167                 compact: Boolean,
168                 'complete-copy': Boolean,
169                 verbose: Boolean,
170                 'save-baseline': Boolean,
171                 'baseline-file': path,
172                 'embed-source': Boolean,
173                 'preserve-comments': Boolean
174             },
175             opts = nopt(template, { v : '--verbose' }, args, 0),
176             overrides = {
177                 verbose: opts.verbose,
178                 instrumentation: {
179                     variable: opts.variable,
180                     compact: opts.compact,
181                     'embed-source': opts['embed-source'],
182                     'preserve-comments': opts['preserve-comments'],
183                     excludes: opts.x,
184                     'complete-copy': opts['complete-copy'],
185                     'save-baseline': opts['save-baseline'],
186                     'baseline-file': opts['baseline-file']
187                 }
188             },
189             config = configuration.loadFile(opts.config, overrides),
190             iOpts = config.instrumentation,
191             cmdArgs = opts.argv.remain,
192             file,
193             stats,
194             stream,
195             includes,
196             instrumenter,
197             needBaseline = iOpts.saveBaseline(),
198             baselineFile = path.resolve(iOpts.baselineFile()),
199             output = opts.output;
200
201         verbose = config.verbose;
202         if (cmdArgs.length !== 1) {
203             return callback(inputError.create('Need exactly one filename/ dirname argument for the instrument command!'));
204         }
205
206         if (iOpts.completeCopy()) {
207             includes = ['**/*'];
208         }
209         else {
210             includes = iOpts.extensions().map(function(ext) {
211                 return '**/*' + ext;
212             });
213         }
214
215         instrumenter = new Instrumenter({
216             coverageVariable: iOpts.variable(),
217             embedSource: iOpts.embedSource(),
218             noCompact: !iOpts.compact(),
219             preserveComments: iOpts.preserveComments()
220         });
221
222         if (needBaseline) {
223             mkdirp.sync(path.dirname(baselineFile));
224             instrumenter = new BaselineCollector(instrumenter);
225             process.on('exit', function () {
226                 console.log('Saving baseline coverage at: ' + baselineFile);
227                 fs.writeFileSync(baselineFile, JSON.stringify(instrumenter.getCoverage()), 'utf8');
228             });
229         }
230
231         file = path.resolve(cmdArgs[0]);
232         stats = fs.statSync(file);
233         if (stats.isDirectory()) {
234             if (!output) { return callback(inputError.create('Need an output directory [-o <dir>] when input is a directory!')); }
235             if (output === file) { return callback(inputError.create('Cannot instrument into the same directory/ file as input!')); }
236             mkdirp.sync(output);
237             filesFor({
238                 root: file,
239                 includes: includes,
240                 excludes: opts.x || iOpts.excludes(false), // backwards-compat, *sigh*
241                 relative: true
242             }, function (err, files) {
243                 if (err) { return callback(err); }
244                 processFiles(instrumenter, file, output, files, iOpts.extensions());
245             });
246         } else {
247             if (output) {
248                 stream = fs.createWriteStream(output);
249             } else {
250                 stream = process.stdout;
251             }
252             stream.write(instrumenter.instrumentSync(fs.readFileSync(file, 'utf8'), file));
253             if (stream !== process.stdout) {
254                 stream.end();
255             }
256         }
257     }
258 });
259
260 module.exports = InstrumentCommand;
261