aboutsummaryrefslogtreecommitdiff
path: root/node_modules/selenium-webdriver/io/exec.js
blob: 948749e83120a08be99a2d9c4116c06c8b974ad6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

'use strict';

const childProcess = require('child_process');


/**
 * A hash with configuration options for an executed command.
 *
 * - `args` - Command line arguments.
 * - `env` - Command environment; will inherit from the current process if
 *     missing.
 * - `stdio` - IO configuration for the spawned server process. For more
 *     information, refer to the documentation of `child_process.spawn`.
 *
 * @typedef {{
 *   args: (!Array<string>|undefined),
 *   env: (!Object<string, string>|undefined),
 *   stdio: (string|!Array<string|number|!stream.Stream|null|undefined>|
 *           undefined)
 * }}
 */
var Options;


/**
 * Describes a command's termination conditions.
 */
class Result {
  /**
   * @param {?number} code The exit code, or {@code null} if the command did not
   *     exit normally.
   * @param {?string} signal The signal used to kill the command, or
   *     {@code null}.
   */
  constructor(code, signal) {
    /** @type {?number} */
    this.code = code;

    /** @type {?string} */
    this.signal = signal;
  }

  /** @override */
  toString() {
    return `Result(code=${this.code}, signal=${this.signal})`;
  }
}


const COMMAND_RESULT = /** !WeakMap<!Command, !Promise<!Result>> */new WeakMap;
const KILL_HOOK = /** !WeakMap<!Command, function(string)> */new WeakMap;

/**
 * Represents a command running in a sub-process.
 */
class Command {
  /**
   * @param {!Promise<!Result>} result The command result.
   * @param {function(string)} onKill The function to call when {@link #kill()}
   *     is called.
   */
  constructor(result, onKill) {
    COMMAND_RESULT.set(this, result);
    KILL_HOOK.set(this, onKill);
  }

  /**
   * @return {!Promise<!Result>} A promise for the result of this
   *     command.
   */
  result() {
    return /** @type {!Promise<!Result>} */(COMMAND_RESULT.get(this));
  }

  /**
   * Sends a signal to the underlying process.
   * @param {string=} opt_signal The signal to send; defaults to `SIGTERM`.
   */
  kill(opt_signal) {
    KILL_HOOK.get(this)(opt_signal || 'SIGTERM');
  }
}


// PUBLIC API


/**
 * Spawns a child process. The returned {@link Command} may be used to wait
 * for the process result or to send signals to the process.
 *
 * @param {string} command The executable to spawn.
 * @param {Options=} opt_options The command options.
 * @return {!Command} The launched command.
 */
module.exports = function exec(command, opt_options) {
  var options = opt_options || {};

  var proc = childProcess.spawn(command, options.args || [], {
    env: options.env || process.env,
    stdio: options.stdio || 'ignore'
  });

  // This process should not wait on the spawned child, however, we do
  // want to ensure the child is killed when this process exits.
  proc.unref();
  process.once('exit', onProcessExit);

  let result = new Promise(resolve => {
    proc.once('exit', (code, signal) => {
      proc = null;
      process.removeListener('exit', onProcessExit);
      resolve(new Result(code, signal));
    });
  });
  return new Command(result, killCommand);

  function onProcessExit() {
    killCommand('SIGTERM');
  }

  function killCommand(signal) {
    process.removeListener('exit', onProcessExit);
    if (proc) {
      proc.kill(signal);
      proc = null;
    }
  }
};

// Exported to improve generated API documentation.

module.exports.Command = Command;
/** @typedef {!Options} */
module.exports.Options = Options;
module.exports.Result = Result;