current commit
260925.61 · src: add --process-timeout=N
'use strict';

// Tests the validation of --process-timeout and of the options that cannot be
// combined with it.

const common = require('../common');
const assert = require('assert');
const { Worker } = require('worker_threads');
const {
  spawnSyncAndAssert,
  spawnSyncAndExit,
} = require('../common/child_process');

// Long enough for the process to exit on its own.
for (const args of [
  ['--process-timeout=600000ms'],
  ['--process-timeout=30s'],
  ['--process-timeout=5m'],
  ['--process-timeout', '1h'],
]) {
  spawnSyncAndAssert(process.execPath, [...args, '-e', ''], {
    stdout: '',
    stderr: '',
  });
}

for (const value of [
  '30', '0s', '0ms', '1.5s', '-1s', '1d', 's', 'abc', ' 1s', '1s ',
  '99999999999999999999h',
]) {
  spawnSyncAndExit(process.execPath, [`--process-timeout=${value}`, '-e', ''], {
    status: 9,
    signal: null,
    trim: true,
    stderr: `${process.execPath}: invalid value for --process-timeout: ` +
            `'${value}'. Expected a positive integer followed by a unit ` +
            '(ms, s, m or h), e.g. 30s',
  });
}

spawnSyncAndExit(process.execPath, ['--report-on-process-timeout', '-e', ''], {
  status: 9,
  signal: null,
  trim: true,
  stderr: `${process.execPath}: --report-on-process-timeout must be used with ` +
          '--process-timeout',
});

if (!process.config.variables.node_without_node_options) {
  for (const option of ['--process-timeout=1s', '--report-on-process-timeout']) {
    spawnSyncAndExit(process.execPath, ['-e', ''], {
      env: { ...process.env, NODE_OPTIONS: option },
    }, {
      status: 9,
      signal: null,
      trim: true,
      stderr: `${process.execPath}: ${option.replace(/=.*/, '=')} is not ` +
              'allowed in NODE_OPTIONS',
    });
  }

  // Conflicts are detected across option sources. The inspector options are
  // the only conflicting options allowed in NODE_OPTIONS, and they only exist
  // when Node.js is built with the inspector.
  if (common.hasInspector) {
    spawnSyncAndExit(process.execPath, ['--process-timeout=1s', '-e', ''], {
      env: { ...process.env, NODE_OPTIONS: '--inspect=0' },
    }, {
      status: 9,
      signal: null,
      trim: true,
      stderr: `${process.execPath}: either --process-timeout or --inspect can be ` +
              'used, not both',
    });
  }
}

const conflicts = [
  [['--run', 'test'], '--run'],
  [['--build-snapshot', 'entry.js'], '--build-snapshot'],
];
// Without the inspector, these options are rejected as bad options instead.
if (common.hasInspector) {
  conflicts.push(
    [['--inspect=0'], '--inspect'],
    [['--inspect-brk=0'], '--inspect-brk'],
    [['--inspect-brk-node=0'], '--inspect-brk-node'],
    [['--inspect-wait=0'], '--inspect-wait'],
    [['--inspect-port=0'], '--inspect-port'],
    [['--debug-port=0'], '--inspect-port'],
    [['--inspect-publish-uid=http'], '--inspect-publish-uid'],
  );
}

for (const [args, conflict] of conflicts) {
  spawnSyncAndExit(process.execPath, ['--process-timeout=1s', ...args], {
    status: 9,
    signal: null,
    trim: true,
    stderr: `${process.execPath}: either --process-timeout or ${conflict} can ` +
            'be used, not both',
  });
}

spawnSyncAndExit(process.execPath, ['--process-timeout=1s', 'inspect', 'app.js'], {
  status: 9,
  signal: null,
  trim: true,
  stderr: `${process.execPath}: --process-timeout cannot be used with ` +
          '`node inspect`',
});

// --process-timeout applies to the whole process, so Workers do not accept it.
assert.throws(() => {
  new Worker('', { eval: true, execArgv: ['--process-timeout=1s'] });
}, {
  code: 'ERR_WORKER_INVALID_EXEC_ARGV',
});