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