Bug 1270357 Add some test hooks to Subprocess.jsm r?kmag
MozReview-Commit-ID: Or2EOAL1eC
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* These modules are loosely based on the subprocess.jsm module created
* by Jan Gerber and Patrick Brunschwig, though the implementation
* differs drastically.
*/
"use strict";
let EXPORTED_SYMBOLS = ["Subprocess"];
/* exported Subprocess */
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/AppConstants.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/subprocess/subprocess_common.jsm");
if (AppConstants.platform == "win") {
XPCOMUtils.defineLazyModuleGetter(this, "SubprocessImpl",
"resource://gre/modules/subprocess/subprocess_win.jsm");
} else {
XPCOMUtils.defineLazyModuleGetter(this, "SubprocessImpl",
"resource://gre/modules/subprocess/subprocess_unix.jsm");
}
/**
* Allows for creation of and communication with OS-level sub-processes.
* @namespace
*/
var Subprocess = {
/**
* Launches a process, and returns a handle to it.
*
* @param {object} options
* An object describing the process to launch.
* @param {string} options.command
* The name of the execuable to launch. If not a full path, it is
* resolved to an executable by searching the directories in $PATH.
* @param {string[]} [options.arguments]
* A list of strings to pass as arguments to the process.
* @param {object} [options.environment]
* An object containing a key and value for each environment variable
* to pass to the process.
* @param {boolean} [options.environmentAppend]
* If true, append the environment variables passed in `environment` to
* the existing set of environment variables. Otherwise, the values in
* 'environment' constitute the entire set of environment variables
* passed to the new process.
* @param {string} [options.stderr]
* Defines how the process's stderr output is handled. One of:
*
* - "ignore": (default) The process's standard error is not
* redirected.
* - "stdout": The process's stderr is merged with its stdout.
* - "pipe": The process's stderr is redirected to a pipe, which can
* be read from via its `stderr` property.
*
* @param {string} [options.workdir]
* The working directory in which to launch the new process.
*
* @returns {Promise<Process>}
*
* @rejects {Error}
* May be rejected with an Error object if the process can not be
* launched. The object will include an `errorCode` property with
* one of the following values if it was rejected for the
* corresponding reason:
*
* - Subprocess.ERROR_BAD_EXECUTABLE: The given command could not
* be found, or the file that it references is not executable.
*/
call(options) {
options = Object.assign({}, options);
options.stderr = options.stderr || "ignore";
options.workdir = options.workdir || null;
let environment = {};
if (!options.environment || options.environmentAppend) {
environment = this.getEnvironment();
}
if (options.environment) {
Object.assign(environment, options.environment);
}
options.environment = Object.keys(environment)
.map(key => `${key}=${environment[key]}`);
options.arguments = Array.from(options.arguments || []);
return this.pathSearch(options.command, environment).then(command => {
options.arguments.unshift(options.command);
options.command = command;
let result = SubprocessImpl.call(options);
result.then(proc => {
this._startProc();
proc.exitPromise.then(() => {
this._stopProc();
});
});
return result;
});
},
/**
* Returns an object with a key-value pair for every variable in the process's
* current environment.
*
* @returns {object}
*/
getEnvironment() {
let environment = Object.create(null);
for (let [k, v] of SubprocessImpl.getEnvironment()) {
environment[k] = v;
}
return environment;
},
/**
* Searches for the given executable file in the system executable
* file paths as specified by the PATH environment variable.
*
* On Windows, if the unadorned filename cannot be found, the
* extensions in the semicolon-separated list in the PATHSEP
* environment variable are successively appended to the original
* name and searched for in turn.
*
* @param {string} bin
* The name of the executable to find.
* @param {object} environment
* An object containing a key for each environment variable to be used
* in the search.
* @returns {Promise<string>}
*/
pathSearch(command, environment) {
// Promise.resolve lets us get around returning one of the Promise.jsm
// pseudo-promises returned by Task.jsm.
let path = SubprocessImpl.pathSearch(command, environment);
return Promise.resolve(path);
},
/**
* Things below this point are meant only for use in tests
*/
_liveProcCount: 0,
_onLiveCountChange: null,
_startProc() {
this._liveProcCount++;
if (this._onLiveCountChange) {
this._onLiveCountChange(this._liveProcCount);
}
},
_stopProc() {
this._liveProcCount--;
if (this._onLiveCountChange) {
this._onLiveCountChange(this._liveProcCount);
}
},
};
Object.assign(Subprocess, SubprocessConstants);