doc: fix scrolling on iOS devices
[platform/upstream/nodejs.git] / test / common.js
1 /* eslint-disable required-modules */
2 'use strict';
3 var path = require('path');
4 var fs = require('fs');
5 var assert = require('assert');
6 var os = require('os');
7 var child_process = require('child_process');
8 const stream = require('stream');
9 const util = require('util');
10
11 const testRoot = path.resolve(process.env.NODE_TEST_DIR ||
12                               path.dirname(__filename));
13
14 exports.testDir = path.dirname(__filename);
15 exports.fixturesDir = path.join(exports.testDir, 'fixtures');
16 exports.libDir = path.join(exports.testDir, '../lib');
17 exports.tmpDirName = 'tmp';
18 exports.PORT = +process.env.NODE_COMMON_PORT || 12346;
19 exports.isWindows = process.platform === 'win32';
20 exports.isAix = process.platform === 'aix';
21 exports.isLinuxPPCBE = (process.platform === 'linux') &&
22                        (process.arch === 'ppc64') &&
23                        (os.endianness() === 'BE');
24 exports.isSunOS = process.platform === 'sunos';
25 exports.isFreeBSD = process.platform === 'freebsd';
26
27 exports.enoughTestMem = os.totalmem() > 0x20000000; /* 512MB */
28
29 function rimrafSync(p) {
30   try {
31     var st = fs.lstatSync(p);
32   } catch (e) {
33     if (e.code === 'ENOENT')
34       return;
35   }
36
37   try {
38     if (st && st.isDirectory())
39       rmdirSync(p, null);
40     else
41       fs.unlinkSync(p);
42   } catch (e) {
43     if (e.code === 'ENOENT')
44       return;
45     if (e.code === 'EPERM')
46       return rmdirSync(p, e);
47     if (e.code !== 'EISDIR')
48       throw e;
49     rmdirSync(p, e);
50   }
51 }
52
53 function rmdirSync(p, originalEr) {
54   try {
55     fs.rmdirSync(p);
56   } catch (e) {
57     if (e.code === 'ENOTDIR')
58       throw originalEr;
59     if (e.code === 'ENOTEMPTY' || e.code === 'EEXIST' || e.code === 'EPERM') {
60       fs.readdirSync(p).forEach(function(f) {
61         rimrafSync(path.join(p, f));
62       });
63       fs.rmdirSync(p);
64     }
65   }
66 }
67
68 exports.refreshTmpDir = function() {
69   rimrafSync(exports.tmpDir);
70   fs.mkdirSync(exports.tmpDir);
71 };
72
73 if (process.env.TEST_THREAD_ID) {
74   exports.PORT += process.env.TEST_THREAD_ID * 100;
75   exports.tmpDirName += '.' + process.env.TEST_THREAD_ID;
76 }
77 exports.tmpDir = path.join(testRoot, exports.tmpDirName);
78
79 var opensslCli = null;
80 var inFreeBSDJail = null;
81 var localhostIPv4 = null;
82
83 exports.localIPv6Hosts = ['localhost'];
84 if (process.platform === 'linux') {
85   exports.localIPv6Hosts = [
86     // Debian/Ubuntu
87     'ip6-localhost',
88     'ip6-loopback',
89
90     // SUSE
91     'ipv6-localhost',
92     'ipv6-loopback',
93
94     // Typically universal
95     'localhost',
96   ];
97 }
98
99 Object.defineProperty(exports, 'inFreeBSDJail', {
100   get: function() {
101     if (inFreeBSDJail !== null) return inFreeBSDJail;
102
103     if (process.platform === 'freebsd' &&
104       child_process.execSync('sysctl -n security.jail.jailed').toString() ===
105       '1\n') {
106       inFreeBSDJail = true;
107     } else {
108       inFreeBSDJail = false;
109     }
110     return inFreeBSDJail;
111   }
112 });
113
114 Object.defineProperty(exports, 'localhostIPv4', {
115   get: function() {
116     if (localhostIPv4 !== null) return localhostIPv4;
117
118     if (exports.inFreeBSDJail) {
119       // Jailed network interfaces are a bit special - since we need to jump
120       // through loops, as well as this being an exception case, assume the
121       // user will provide this instead.
122       if (process.env.LOCALHOST) {
123         localhostIPv4 = process.env.LOCALHOST;
124       } else {
125         console.error('Looks like we\'re in a FreeBSD Jail. ' +
126                       'Please provide your default interface address ' +
127                       'as LOCALHOST or expect some tests to fail.');
128       }
129     }
130
131     if (localhostIPv4 === null) localhostIPv4 = '127.0.0.1';
132
133     return localhostIPv4;
134   }
135 });
136
137 // opensslCli defined lazily to reduce overhead of spawnSync
138 Object.defineProperty(exports, 'opensslCli', {get: function() {
139   if (opensslCli !== null) return opensslCli;
140
141   if (process.config.variables.node_shared_openssl) {
142     // use external command
143     opensslCli = 'openssl';
144   } else {
145     // use command built from sources included in Node.js repository
146     opensslCli = path.join(path.dirname(process.execPath), 'openssl-cli');
147   }
148
149   if (exports.isWindows) opensslCli += '.exe';
150
151   var openssl_cmd = child_process.spawnSync(opensslCli, ['version']);
152   if (openssl_cmd.status !== 0 || openssl_cmd.error !== undefined) {
153     // openssl command cannot be executed
154     opensslCli = false;
155   }
156   return opensslCli;
157 }, enumerable: true });
158
159 Object.defineProperty(exports, 'hasCrypto', {
160   get: function() {
161     return process.versions.openssl ? true : false;
162   }
163 });
164
165 Object.defineProperty(exports, 'hasFipsCrypto', {
166   get: function() {
167     return process.config.variables.openssl_fips ? true : false;
168   }
169 });
170
171 if (exports.isWindows) {
172   exports.PIPE = '\\\\.\\pipe\\libuv-test';
173   if (process.env.TEST_THREAD_ID) {
174     exports.PIPE += '.' + process.env.TEST_THREAD_ID;
175   }
176 } else {
177   exports.PIPE = exports.tmpDir + '/test.sock';
178 }
179
180 if (exports.isWindows) {
181   exports.faketimeCli = false;
182 } else {
183   exports.faketimeCli = path.join(__dirname, '..', 'tools', 'faketime', 'src',
184                                   'faketime');
185 }
186
187 var ifaces = os.networkInterfaces();
188 exports.hasIPv6 = Object.keys(ifaces).some(function(name) {
189   return /lo/.test(name) && ifaces[name].some(function(info) {
190     return info.family === 'IPv6';
191   });
192 });
193
194
195 exports.ddCommand = function(filename, kilobytes) {
196   if (exports.isWindows) {
197     var p = path.resolve(exports.fixturesDir, 'create-file.js');
198     return '"' + process.argv[0] + '" "' + p + '" "' +
199            filename + '" ' + (kilobytes * 1024);
200   } else {
201     return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes;
202   }
203 };
204
205
206 exports.spawnCat = function(options) {
207   var spawn = require('child_process').spawn;
208
209   if (exports.isWindows) {
210     return spawn('more', [], options);
211   } else {
212     return spawn('cat', [], options);
213   }
214 };
215
216
217 exports.spawnSyncCat = function(options) {
218   var spawnSync = require('child_process').spawnSync;
219
220   if (exports.isWindows) {
221     return spawnSync('more', [], options);
222   } else {
223     return spawnSync('cat', [], options);
224   }
225 };
226
227
228 exports.spawnPwd = function(options) {
229   var spawn = require('child_process').spawn;
230
231   if (exports.isWindows) {
232     return spawn('cmd.exe', ['/c', 'cd'], options);
233   } else {
234     return spawn('pwd', [], options);
235   }
236 };
237
238 exports.platformTimeout = function(ms) {
239   if (process.config.target_defaults.default_configuration === 'Debug')
240     ms = 2 * ms;
241
242   if (process.arch !== 'arm')
243     return ms;
244
245   const armv = process.config.variables.arm_version;
246
247   if (armv === '6')
248     return 7 * ms;  // ARMv6
249
250   if (armv === '7')
251     return 2 * ms;  // ARMv7
252
253   return ms; // ARMv8+
254 };
255
256 var knownGlobals = [setTimeout,
257                     setInterval,
258                     setImmediate,
259                     clearTimeout,
260                     clearInterval,
261                     clearImmediate,
262                     console,
263                     constructor, // Enumerable in V8 3.21.
264                     Buffer,
265                     process,
266                     global];
267
268 if (global.gc) {
269   knownGlobals.push(gc);
270 }
271
272 if (global.DTRACE_HTTP_SERVER_RESPONSE) {
273   knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
274   knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);
275   knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE);
276   knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST);
277   knownGlobals.push(DTRACE_NET_STREAM_END);
278   knownGlobals.push(DTRACE_NET_SERVER_CONNECTION);
279 }
280
281 if (global.COUNTER_NET_SERVER_CONNECTION) {
282   knownGlobals.push(COUNTER_NET_SERVER_CONNECTION);
283   knownGlobals.push(COUNTER_NET_SERVER_CONNECTION_CLOSE);
284   knownGlobals.push(COUNTER_HTTP_SERVER_REQUEST);
285   knownGlobals.push(COUNTER_HTTP_SERVER_RESPONSE);
286   knownGlobals.push(COUNTER_HTTP_CLIENT_REQUEST);
287   knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE);
288 }
289
290 if (global.LTTNG_HTTP_SERVER_RESPONSE) {
291   knownGlobals.push(LTTNG_HTTP_SERVER_RESPONSE);
292   knownGlobals.push(LTTNG_HTTP_SERVER_REQUEST);
293   knownGlobals.push(LTTNG_HTTP_CLIENT_RESPONSE);
294   knownGlobals.push(LTTNG_HTTP_CLIENT_REQUEST);
295   knownGlobals.push(LTTNG_NET_STREAM_END);
296   knownGlobals.push(LTTNG_NET_SERVER_CONNECTION);
297 }
298
299 if (global.ArrayBuffer) {
300   knownGlobals.push(ArrayBuffer);
301   knownGlobals.push(Int8Array);
302   knownGlobals.push(Uint8Array);
303   knownGlobals.push(Uint8ClampedArray);
304   knownGlobals.push(Int16Array);
305   knownGlobals.push(Uint16Array);
306   knownGlobals.push(Int32Array);
307   knownGlobals.push(Uint32Array);
308   knownGlobals.push(Float32Array);
309   knownGlobals.push(Float64Array);
310   knownGlobals.push(DataView);
311 }
312
313 // Harmony features.
314 if (global.Proxy) {
315   knownGlobals.push(Proxy);
316 }
317
318 if (global.Symbol) {
319   knownGlobals.push(Symbol);
320 }
321
322 function leakedGlobals() {
323   var leaked = [];
324
325   for (var val in global)
326     if (-1 === knownGlobals.indexOf(global[val]))
327       leaked.push(val);
328
329   return leaked;
330 }
331 exports.leakedGlobals = leakedGlobals;
332
333 // Turn this off if the test should not check for global leaks.
334 exports.globalCheck = true;
335
336 process.on('exit', function() {
337   if (!exports.globalCheck) return;
338   var leaked = leakedGlobals();
339   if (leaked.length > 0) {
340     console.error('Unknown globals: %s', leaked);
341     assert.ok(false, 'Unknown global found');
342   }
343 });
344
345
346 var mustCallChecks = [];
347
348
349 function runCallChecks(exitCode) {
350   if (exitCode !== 0) return;
351
352   var failed = mustCallChecks.filter(function(context) {
353     return context.actual !== context.expected;
354   });
355
356   failed.forEach(function(context) {
357     console.log('Mismatched %s function calls. Expected %d, actual %d.',
358                 context.name,
359                 context.expected,
360                 context.actual);
361     console.log(context.stack.split('\n').slice(2).join('\n'));
362   });
363
364   if (failed.length) process.exit(1);
365 }
366
367
368 exports.mustCall = function(fn, expected) {
369   if (typeof expected !== 'number') expected = 1;
370
371   var context = {
372     expected: expected,
373     actual: 0,
374     stack: (new Error()).stack,
375     name: fn.name || '<anonymous>'
376   };
377
378   // add the exit listener only once to avoid listener leak warnings
379   if (mustCallChecks.length === 0) process.on('exit', runCallChecks);
380
381   mustCallChecks.push(context);
382
383   return function() {
384     context.actual++;
385     return fn.apply(this, arguments);
386   };
387 };
388
389 var etcServicesFileName = path.join('/etc', 'services');
390 if (exports.isWindows) {
391   etcServicesFileName = path.join(process.env.SystemRoot, 'System32', 'drivers',
392     'etc', 'services');
393 }
394
395 /*
396  * Returns a string that represents the service name associated
397  * to the service bound to port "port" and using protocol "protocol".
398  *
399  * If the service is not defined in the services file, it returns
400  * the port number as a string.
401  *
402  * Returns undefined if /etc/services (or its equivalent on non-UNIX
403  * platforms) can't be read.
404  */
405 exports.getServiceName = function getServiceName(port, protocol) {
406   if (port == null) {
407     throw new Error('Missing port number');
408   }
409
410   if (typeof protocol !== 'string') {
411     throw new Error('Protocol must be a string');
412   }
413
414   /*
415    * By default, if a service can't be found in /etc/services,
416    * its name is considered to be its port number.
417    */
418   var serviceName = port.toString();
419
420   try {
421     var servicesContent = fs.readFileSync(etcServicesFileName,
422       { encoding: 'utf8'});
423     var regexp = `^(\\w+)\\s+\\s${port}/${protocol}\\s`;
424     var re = new RegExp(regexp, 'm');
425
426     var matches = re.exec(servicesContent);
427     if (matches && matches.length > 1) {
428       serviceName = matches[1];
429     }
430   } catch (e) {
431     console.error('Cannot read file: ', etcServicesFileName);
432     return undefined;
433   }
434
435   return serviceName;
436 };
437
438 exports.hasMultiLocalhost = function hasMultiLocalhost() {
439   var TCP = process.binding('tcp_wrap').TCP;
440   var t = new TCP();
441   var ret = t.bind('127.0.0.2', exports.PORT);
442   t.close();
443   return ret === 0;
444 };
445
446 exports.fileExists = function(pathname) {
447   try {
448     fs.accessSync(pathname);
449     return true;
450   } catch (err) {
451     return false;
452   }
453 };
454
455 exports.fail = function(msg) {
456   assert.fail(null, null, msg);
457 };
458
459 // Returns true if the exit code "exitCode" and/or signal name "signal"
460 // represent the exit code and/or signal name of a node process that aborted,
461 // false otherwise.
462 exports.nodeProcessAborted = function nodeProcessAborted(exitCode, signal) {
463   // Depending on the compiler used, node will exit with either
464   // exit code 132 (SIGILL), 133 (SIGTRAP) or 134 (SIGABRT).
465   var expectedExitCodes = [132, 133, 134];
466
467   // On platforms using KSH as the default shell (like SmartOS),
468   // when a process aborts, KSH exits with an exit code that is
469   // greater than 256, and thus the exit code emitted with the 'exit'
470   // event is null and the signal is set to either SIGILL, SIGTRAP,
471   // or SIGABRT (depending on the compiler).
472   const expectedSignals = ['SIGILL', 'SIGTRAP', 'SIGABRT'];
473
474   // On Windows, v8's base::OS::Abort triggers an access violation,
475   // which corresponds to exit code 3221225477 (0xC0000005)
476   if (process.platform === 'win32')
477     expectedExitCodes = [3221225477];
478
479   // When using --abort-on-uncaught-exception, V8 will use
480   // base::OS::Abort to terminate the process.
481   // Depending on the compiler used, the shell or other aspects of
482   // the platform used to build the node binary, this will actually
483   // make V8 exit by aborting or by raising a signal. In any case,
484   // one of them (exit code or signal) needs to be set to one of
485   // the expected exit codes or signals.
486   if (signal !== null) {
487     return expectedSignals.indexOf(signal) > -1;
488   } else {
489     return expectedExitCodes.indexOf(exitCode) > -1;
490   }
491 };
492
493 // A stream to push an array into a REPL
494 function ArrayStream() {
495   this.run = function(data) {
496     data.forEach((line) => {
497       this.emit('data', line + '\n');
498     });
499   };
500 }
501
502 util.inherits(ArrayStream, stream.Stream);
503 exports.ArrayStream = ArrayStream;
504 ArrayStream.prototype.readable = true;
505 ArrayStream.prototype.writable = true;
506 ArrayStream.prototype.pause = function() {};
507 ArrayStream.prototype.resume = function() {};
508 ArrayStream.prototype.write = function() {};