fs: set encoding on fs.createWriteStream
[platform/upstream/nodejs.git] / test / common.js
1 'use strict';
2 var path = require('path');
3 var fs = require('fs');
4 var assert = require('assert');
5 var os = require('os');
6 var child_process = require('child_process');
7
8 exports.testDir = path.dirname(__filename);
9 exports.fixturesDir = path.join(exports.testDir, 'fixtures');
10 exports.libDir = path.join(exports.testDir, '../lib');
11 exports.tmpDirName = 'tmp';
12 exports.PORT = +process.env.NODE_COMMON_PORT || 12346;
13
14 if (process.env.TEST_THREAD_ID) {
15   // Distribute ports in parallel tests
16   if (!process.env.NODE_COMMON_PORT)
17     exports.PORT += +process.env.TEST_THREAD_ID * 100;
18
19   exports.tmpDirName += '.' + process.env.TEST_THREAD_ID;
20 }
21 exports.tmpDir = path.join(exports.testDir, exports.tmpDirName);
22
23 var opensslCli = null;
24 var inFreeBSDJail = null;
25 var localhostIPv4 = null;
26
27 Object.defineProperty(exports, 'inFreeBSDJail', {
28   get: function() {
29     if (inFreeBSDJail !== null) return inFreeBSDJail;
30
31     if (process.platform === 'freebsd' &&
32       child_process.execSync('sysctl -n security.jail.jailed').toString() ===
33       '1\n') {
34       inFreeBSDJail = true;
35     } else {
36       inFreeBSDJail = false;
37     }
38     return inFreeBSDJail;
39   }
40 });
41
42 Object.defineProperty(exports, 'localhostIPv4', {
43   get: function() {
44     if (localhostIPv4 !== null) return localhostIPv4;
45
46     if (exports.inFreeBSDJail) {
47       // Jailed network interfaces are a bit special - since we need to jump
48       // through loops, as well as this being an exception case, assume the
49       // user will provide this instead.
50       if (process.env.LOCALHOST) {
51         localhostIPv4 = process.env.LOCALHOST;
52       } else {
53         console.error('Looks like we\'re in a FreeBSD Jail. ' +
54                       'Please provide your default interface address ' +
55                       'as LOCALHOST or expect some tests to fail.');
56       }
57     }
58
59     if (localhostIPv4 === null) localhostIPv4 = '127.0.0.1';
60
61     return localhostIPv4;
62   }
63 });
64
65 // opensslCli defined lazily to reduce overhead of spawnSync
66 Object.defineProperty(exports, 'opensslCli', {get: function() {
67   if (opensslCli !== null) return opensslCli;
68
69   if (process.config.variables.node_shared_openssl) {
70     // use external command
71     opensslCli = 'openssl';
72   } else {
73     // use command built from sources included in io.js repository
74     opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli');
75   }
76
77   if (process.platform === 'win32') opensslCli += '.exe';
78
79   var openssl_cmd = child_process.spawnSync(opensslCli, ['version']);
80   if (openssl_cmd.status !== 0 || openssl_cmd.error !== undefined) {
81     // openssl command cannot be executed
82     opensslCli = false;
83   }
84   return opensslCli;
85 }, enumerable: true });
86
87 Object.defineProperty(exports, 'hasCrypto', {get: function() {
88   return process.versions.openssl ? true : false;
89 }});
90
91 if (process.platform === 'win32') {
92   exports.PIPE = '\\\\.\\pipe\\libuv-test';
93 } else {
94   exports.PIPE = exports.tmpDir + '/test.sock';
95 }
96
97 if (process.env.NODE_COMMON_PIPE) {
98   exports.PIPE = process.env.NODE_COMMON_PIPE;
99   // Remove manually, the test runner won't do it
100   // for us like it does for files in test/tmp.
101   try {
102     fs.unlinkSync(exports.PIPE);
103   } catch (e) {
104     // Ignore.
105   }
106 }
107
108 if (process.platform === 'win32') {
109   exports.faketimeCli = false;
110 } else {
111   exports.faketimeCli = path.join(__dirname, '..', 'tools', 'faketime', 'src',
112                                   'faketime');
113 }
114
115 var ifaces = os.networkInterfaces();
116 exports.hasIPv6 = Object.keys(ifaces).some(function(name) {
117   return /lo/.test(name) && ifaces[name].some(function(info) {
118     return info.family === 'IPv6';
119   });
120 });
121
122 var util = require('util');
123 for (var i in util) exports[i] = util[i];
124 //for (var i in exports) global[i] = exports[i];
125
126 function protoCtrChain(o) {
127   var result = [];
128   for (; o; o = o.__proto__) { result.push(o.constructor); }
129   return result.join();
130 }
131
132 exports.indirectInstanceOf = function(obj, cls) {
133   if (obj instanceof cls) { return true; }
134   var clsChain = protoCtrChain(cls.prototype);
135   var objChain = protoCtrChain(obj);
136   return objChain.slice(-clsChain.length) === clsChain;
137 };
138
139
140 exports.ddCommand = function(filename, kilobytes) {
141   if (process.platform === 'win32') {
142     var p = path.resolve(exports.fixturesDir, 'create-file.js');
143     return '"' + process.argv[0] + '" "' + p + '" "' +
144            filename + '" ' + (kilobytes * 1024);
145   } else {
146     return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes;
147   }
148 };
149
150
151 exports.spawnCat = function(options) {
152   var spawn = require('child_process').spawn;
153
154   if (process.platform === 'win32') {
155     return spawn('more', [], options);
156   } else {
157     return spawn('cat', [], options);
158   }
159 };
160
161
162 exports.spawnSyncCat = function(options) {
163   var spawnSync = require('child_process').spawnSync;
164
165   if (process.platform === 'win32') {
166     return spawnSync('more', [], options);
167   } else {
168     return spawnSync('cat', [], options);
169   }
170 };
171
172
173 exports.spawnPwd = function(options) {
174   var spawn = require('child_process').spawn;
175
176   if (process.platform === 'win32') {
177     return spawn('cmd.exe', ['/c', 'cd'], options);
178   } else {
179     return spawn('pwd', [], options);
180   }
181 };
182
183 exports.platformTimeout = function(ms) {
184   if (process.arch !== 'arm')
185     return ms;
186
187   if (process.config.variables.arm_version === '6')
188     return 7 * ms;  // ARMv6
189
190   return 2 * ms;  // ARMv7 and up.
191 };
192
193 var knownGlobals = [setTimeout,
194                     setInterval,
195                     setImmediate,
196                     clearTimeout,
197                     clearInterval,
198                     clearImmediate,
199                     console,
200                     constructor, // Enumerable in V8 3.21.
201                     Buffer,
202                     process,
203                     global];
204
205 if (global.gc) {
206   knownGlobals.push(gc);
207 }
208
209 if (global.DTRACE_HTTP_SERVER_RESPONSE) {
210   knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
211   knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);
212   knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE);
213   knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST);
214   knownGlobals.push(DTRACE_NET_STREAM_END);
215   knownGlobals.push(DTRACE_NET_SERVER_CONNECTION);
216 }
217
218 if (global.COUNTER_NET_SERVER_CONNECTION) {
219   knownGlobals.push(COUNTER_NET_SERVER_CONNECTION);
220   knownGlobals.push(COUNTER_NET_SERVER_CONNECTION_CLOSE);
221   knownGlobals.push(COUNTER_HTTP_SERVER_REQUEST);
222   knownGlobals.push(COUNTER_HTTP_SERVER_RESPONSE);
223   knownGlobals.push(COUNTER_HTTP_CLIENT_REQUEST);
224   knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE);
225 }
226
227 if (global.LTTNG_HTTP_SERVER_RESPONSE) {
228   knownGlobals.push(LTTNG_HTTP_SERVER_RESPONSE);
229   knownGlobals.push(LTTNG_HTTP_SERVER_REQUEST);
230   knownGlobals.push(LTTNG_HTTP_CLIENT_RESPONSE);
231   knownGlobals.push(LTTNG_HTTP_CLIENT_REQUEST);
232   knownGlobals.push(LTTNG_NET_STREAM_END);
233   knownGlobals.push(LTTNG_NET_SERVER_CONNECTION);
234 }
235
236 if (global.ArrayBuffer) {
237   knownGlobals.push(ArrayBuffer);
238   knownGlobals.push(Int8Array);
239   knownGlobals.push(Uint8Array);
240   knownGlobals.push(Uint8ClampedArray);
241   knownGlobals.push(Int16Array);
242   knownGlobals.push(Uint16Array);
243   knownGlobals.push(Int32Array);
244   knownGlobals.push(Uint32Array);
245   knownGlobals.push(Float32Array);
246   knownGlobals.push(Float64Array);
247   knownGlobals.push(DataView);
248 }
249
250 // Harmony features.
251 if (global.Proxy) {
252   knownGlobals.push(Proxy);
253 }
254
255 if (global.Symbol) {
256   knownGlobals.push(Symbol);
257 }
258
259 function leakedGlobals() {
260   var leaked = [];
261
262   for (var val in global)
263     if (-1 === knownGlobals.indexOf(global[val]))
264       leaked.push(val);
265
266   return leaked;
267 };
268 exports.leakedGlobals = leakedGlobals;
269
270 // Turn this off if the test should not check for global leaks.
271 exports.globalCheck = true;
272
273 process.on('exit', function() {
274   if (!exports.globalCheck) return;
275   var leaked = leakedGlobals();
276   if (leaked.length > 0) {
277     console.error('Unknown globals: %s', leaked);
278     assert.ok(false, 'Unknown global found');
279   }
280 });
281
282
283 var mustCallChecks = [];
284
285
286 function runCallChecks(exitCode) {
287   if (exitCode !== 0) return;
288
289   var failed = mustCallChecks.filter(function(context) {
290     return context.actual !== context.expected;
291   });
292
293   failed.forEach(function(context) {
294     console.log('Mismatched %s function calls. Expected %d, actual %d.',
295                 context.name,
296                 context.expected,
297                 context.actual);
298     console.log(context.stack.split('\n').slice(2).join('\n'));
299   });
300
301   if (failed.length) process.exit(1);
302 }
303
304
305 exports.mustCall = function(fn, expected) {
306   if (typeof expected !== 'number') expected = 1;
307
308   var context = {
309     expected: expected,
310     actual: 0,
311     stack: (new Error()).stack,
312     name: fn.name || '<anonymous>'
313   };
314
315   // add the exit listener only once to avoid listener leak warnings
316   if (mustCallChecks.length === 0) process.on('exit', runCallChecks);
317
318   mustCallChecks.push(context);
319
320   return function() {
321     context.actual++;
322     return fn.apply(this, arguments);
323   };
324 };
325
326 exports.checkSpawnSyncRet = function(ret) {
327   assert.strictEqual(ret.status, 0);
328   assert.strictEqual(ret.error, undefined);
329 };
330
331 var etcServicesFileName = path.join('/etc', 'services');
332 if (process.platform === 'win32') {
333   etcServicesFileName = path.join(process.env.SystemRoot, 'System32', 'drivers',
334     'etc', 'services');
335 }
336
337 /*
338  * Returns a string that represents the service name associated
339  * to the service bound to port "port" and using protocol "protocol".
340  *
341  * If the service is not defined in the services file, it returns
342  * the port number as a string.
343  *
344  * Returns undefined if /etc/services (or its equivalent on non-UNIX
345  * platforms) can't be read.
346  */
347 exports.getServiceName = function getServiceName(port, protocol) {
348   if (port == null) {
349     throw new Error('Missing port number');
350   }
351
352   if (typeof protocol !== 'string') {
353     throw new Error('Protocol must be a string');
354   }
355
356   /*
357    * By default, if a service can't be found in /etc/services,
358    * its name is considered to be its port number.
359    */
360   var serviceName = port.toString();
361
362   try {
363     /*
364      * I'm not a big fan of readFileSync, but reading /etc/services
365      * asynchronously here would require implementing a simple line parser,
366      * which seems overkill for a simple utility function that is not running
367      * concurrently with any other one.
368      */
369     var servicesContent = fs.readFileSync(etcServicesFileName,
370       { encoding: 'utf8'});
371     var regexp = util.format('^(\\w+)\\s+\\s%d/%s\\s', port, protocol);
372     var re = new RegExp(regexp, 'm');
373
374     var matches = re.exec(servicesContent);
375     if (matches && matches.length > 1) {
376       serviceName = matches[1];
377     }
378   } catch(e) {
379     console.error('Cannot read file: ', etcServicesFileName);
380     return undefined;
381   }
382
383   return serviceName;
384 };
385
386 exports.hasMultiLocalhost = function hasMultiLocalhost() {
387   var TCP = process.binding('tcp_wrap').TCP;
388   var t = new TCP();
389   var ret = t.bind('127.0.0.2', exports.PORT);
390   t.close();
391   return ret === 0;
392 };
393
394 exports.isValidHostname = function(str) {
395   // See http://stackoverflow.com/a/3824105
396   var re = new RegExp(
397     '^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])' +
398     '(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]))*$');
399
400   return !!str.match(re) && str.length <= 255;
401 };
402
403 exports.fileExists = function(pathname) {
404   try {
405     fs.accessSync(pathname);
406     return true;
407   } catch (err) {
408     return false;
409   }
410 };