зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1056136 - Get rid of SDK dependency for simulator addon. r=jryans, r=gps
This commit is contained in:
Родитель
8ed74d7d51
Коммит
dc17cf4f2f
|
@ -0,0 +1,67 @@
|
|||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Useful piece of code from :bent
|
||||
// http://mxr.mozilla.org/mozilla-central/source/dom/workers/test/extensions/bootstrap/bootstrap.js
|
||||
function registerAddonResourceHandler(data) {
|
||||
let file = data.installPath;
|
||||
let fileuri = file.isDirectory() ?
|
||||
Services.io.newFileURI(file) :
|
||||
Services.io.newURI("jar:" + file.path + "!/", null, null);
|
||||
let resourceName = encodeURIComponent(data.id.replace("@", "at"));
|
||||
|
||||
Services.io.getProtocolHandler("resource").
|
||||
QueryInterface(Ci.nsIResProtocolHandler).
|
||||
setSubstitution(resourceName, fileuri);
|
||||
|
||||
return "resource://" + resourceName + "/";
|
||||
}
|
||||
|
||||
let mainModule;
|
||||
|
||||
function install(data, reason) {}
|
||||
function uninstall(data, reason) {}
|
||||
|
||||
function startup(data, reason) {
|
||||
let uri = registerAddonResourceHandler(data);
|
||||
|
||||
let loaderModule =
|
||||
Cu.import('resource://gre/modules/commonjs/toolkit/loader.js').Loader;
|
||||
let { Loader, Require, Main } = loaderModule;
|
||||
|
||||
const { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm");
|
||||
|
||||
let loader = Loader({
|
||||
paths: {
|
||||
"./": uri + "lib/",
|
||||
"": "resource://gre/modules/commonjs/"
|
||||
},
|
||||
globals: {
|
||||
console: new ConsoleAPI({
|
||||
prefix: data.id
|
||||
})
|
||||
},
|
||||
modules: {
|
||||
"toolkit/loader": loaderModule,
|
||||
addon: {
|
||||
id: data.id,
|
||||
version: data.version,
|
||||
uri: uri
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let require_ = Require(loader, { id: "./addon" });
|
||||
mainModule = require_("./main");
|
||||
}
|
||||
|
||||
function shutdown(data, reason) {
|
||||
if (mainModule && mainModule.shutdown) {
|
||||
mainModule.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
function uninstall(data, reason) {}
|
||||
|
|
@ -18,11 +18,11 @@ import sys, os, re, subprocess
|
|||
from mozbuild.preprocessor import Preprocessor
|
||||
from mozbuild.base import MozbuildObject
|
||||
from mozbuild.util import ensureParentDir
|
||||
from mozpack.mozjar import JarWriter
|
||||
from zipfile import ZipFile
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
ftp_root_path = "/pub/mozilla.org/labs/fxos-simulator"
|
||||
UPDATE_LINK = "https://ftp.mozilla.org" + ftp_root_path + "/%(update_path)s/%(xpi_name)s"
|
||||
UPDATE_URL = "https://ftp.mozilla.org" + ftp_root_path + "/%(update_path)s/update.rdf"
|
||||
XPI_NAME = "fxos-simulator-%(version)s-%(platform)s.xpi"
|
||||
|
||||
|
@ -43,17 +43,17 @@ class GaiaBuilder(object):
|
|||
with open(os.path.join(self.gaia_path, "profile", "user.js"), "a") as userJs:
|
||||
userJs.write(open(srcfile).read())
|
||||
|
||||
def process_package_overload(src, dst, version, app_buildid):
|
||||
def preprocess_file(src, dst, version, app_buildid, update_url):
|
||||
ensureParentDir(dst)
|
||||
# First replace numeric version like '1.3'
|
||||
# Then replace with 'slashed' version like '1_4'
|
||||
# Finally set the full length addon version like 1.3.20131230
|
||||
# (reduce the app build id to only the build date
|
||||
# as addon manager doesn't handle big ints in addon versions)
|
||||
|
||||
defines = {
|
||||
"NUM_VERSION": version,
|
||||
"SLASH_VERSION": version.replace(".", "_"),
|
||||
"FULL_VERSION": ("%s.%s" % (version, app_buildid[:8]))
|
||||
"ADDON_ID": "fxos_" + version.replace(".", "_") + "_simulator@mozilla.org",
|
||||
# (reduce the app build id to only the build date
|
||||
# as addon manager doesn't handle big ints in addon versions)
|
||||
"ADDON_VERSION": ("%s.%s" % (version, app_buildid[:8])),
|
||||
"ADDON_NAME": "Firefox OS " + version + " Simulator",
|
||||
"ADDON_DESCRIPTION": "a Firefox OS " + version + " simulator",
|
||||
"ADDON_UPDATE_URL": update_url
|
||||
}
|
||||
pp = Preprocessor(defines=defines)
|
||||
pp.do_filter("substitution")
|
||||
|
@ -61,20 +61,24 @@ def process_package_overload(src, dst, version, app_buildid):
|
|||
with open(src, "r") as input:
|
||||
pp.processFile(input=input, output=output)
|
||||
|
||||
def add_dir_to_zip(zip, top, pathInZip, blacklist=()):
|
||||
zf = ZipFile(zip, "a")
|
||||
def add_dir_to_zip(jar, top, pathInZip, blacklist=()):
|
||||
for dirpath, subdirs, files in os.walk(top):
|
||||
dir_relpath = os.path.relpath(dirpath, top)
|
||||
if dir_relpath.startswith(blacklist):
|
||||
continue
|
||||
zf.write(dirpath, os.path.join(pathInZip, dir_relpath))
|
||||
for filename in files:
|
||||
relpath = os.path.join(dir_relpath, filename)
|
||||
if relpath in blacklist:
|
||||
continue
|
||||
zf.write(os.path.join(dirpath, filename),
|
||||
os.path.join(pathInZip, relpath))
|
||||
zf.close()
|
||||
path = os.path.normpath(os.path.join(pathInZip, relpath))
|
||||
file = open(os.path.join(dirpath, filename), "rb")
|
||||
mode = os.stat(os.path.join(dirpath, filename)).st_mode
|
||||
jar.add(path.encode("ascii"), file, mode=mode)
|
||||
|
||||
def add_file_to_zip(jar, path, pathInZip):
|
||||
file = open(path, "rb")
|
||||
mode = os.stat(path).st_mode
|
||||
jar.add(pathInZip.encode("ascii"), file, mode=mode)
|
||||
|
||||
def main(platform):
|
||||
build = MozbuildObject.from_environment()
|
||||
|
@ -107,38 +111,41 @@ def main(platform):
|
|||
builder.profile(env)
|
||||
builder.override_prefs(os.path.join(srcdir, "custom-prefs.js"))
|
||||
|
||||
# Substitute version strings in the package manifest overload file
|
||||
manifest_overload = os.path.join(build.topobjdir, "b2g", "simulator", "package-overload.json")
|
||||
process_package_overload(os.path.join(srcdir, "package-overload.json.in"),
|
||||
manifest_overload,
|
||||
version,
|
||||
app_buildid)
|
||||
|
||||
# Build the simulator addon xpi
|
||||
xpi_name = XPI_NAME % {"version": version, "platform": platform}
|
||||
xpi_path = os.path.join(distdir, xpi_name)
|
||||
|
||||
update_path = "%s/%s" % (version, platform)
|
||||
update_link = UPDATE_LINK % {"update_path": update_path, "xpi_name": xpi_name}
|
||||
update_url = UPDATE_URL % {"update_path": update_path}
|
||||
subprocess.check_call([
|
||||
build.virtualenv_manager.python_path, os.path.join(topsrcdir, "addon-sdk", "source", "bin", "cfx"), "xpi", \
|
||||
"--pkgdir", srcdir, \
|
||||
"--manifest-overload", manifest_overload, \
|
||||
"--strip-sdk", \
|
||||
"--update-link", update_link, \
|
||||
"--update-url", update_url, \
|
||||
"--static-args", "{\"label\": \"Firefox OS %s\"}" % version, \
|
||||
"--output-file", xpi_path \
|
||||
])
|
||||
|
||||
# Ship b2g-desktop, but prevent its gaia profile to be shipped in the xpi
|
||||
add_dir_to_zip(xpi_path, os.path.join(distdir, "b2g"), "b2g", ("gaia", "B2G.app/Contents/MacOS/gaia"))
|
||||
# Then ship our own gaia profile
|
||||
add_dir_to_zip(xpi_path, os.path.join(gaia_path, "profile"), "profile")
|
||||
# Add "defaults" directory (required by add-on runner in Firefox 31 and
|
||||
# earlier)
|
||||
add_dir_to_zip(xpi_path, os.path.join(srcdir, "defaults"), "defaults")
|
||||
# Preprocess some files...
|
||||
manifest = os.path.join(build.topobjdir, "b2g", "simulator", "install.rdf")
|
||||
preprocess_file(os.path.join(srcdir, "install.rdf.in"),
|
||||
manifest,
|
||||
version,
|
||||
app_buildid,
|
||||
update_url)
|
||||
|
||||
options_file = os.path.join(build.topobjdir, "b2g", "simulator", "options.xul")
|
||||
preprocess_file(os.path.join(srcdir, "options.xul.in"),
|
||||
options_file,
|
||||
version,
|
||||
app_buildid,
|
||||
update_url)
|
||||
|
||||
with JarWriter(xpi_path, optimize=False) as zip:
|
||||
# Ship addon files into the .xpi
|
||||
add_dir_to_zip(zip, os.path.join(srcdir, "lib"), "lib")
|
||||
add_file_to_zip(zip, manifest, "install.rdf")
|
||||
add_file_to_zip(zip, os.path.join(srcdir, "bootstrap.js"), "bootstrap.js")
|
||||
add_file_to_zip(zip, options_file, "options.xul")
|
||||
add_file_to_zip(zip, os.path.join(srcdir, "icon.png"), "icon.png")
|
||||
add_file_to_zip(zip, os.path.join(srcdir, "icon64.png"), "icon64.png")
|
||||
|
||||
# Ship b2g-desktop, but prevent its gaia profile to be shipped in the xpi
|
||||
add_dir_to_zip(zip, os.path.join(distdir, "b2g"), "b2g", ("gaia", "B2G.app/Contents/MacOS/gaia"))
|
||||
# Then ship our own gaia profile
|
||||
add_dir_to_zip(zip, os.path.join(gaia_path, "profile"), "profile")
|
||||
|
||||
if __name__ == '__main__':
|
||||
if 2 != len(sys.argv):
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>@ADDON_ID@</em:id>
|
||||
<em:version>@ADDON_VERSION@</em:version>
|
||||
<em:type>2</em:type>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
<em:unpack>true</em:unpack>
|
||||
|
||||
<!-- Firefox -->
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>26.0</em:minVersion>
|
||||
<em:maxVersion>30.0</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>@ADDON_NAME@</em:name>
|
||||
<em:description>@ADDON_DESCRIPTION@</em:description>
|
||||
<em:creator>Myk Melez (https://github.com/mykmelez)</em:creator>
|
||||
|
||||
<em:optionsType>2</em:optionsType>
|
||||
|
||||
<em:updateURL>@ADDON_UPDATE_URL@</em:updateURL>
|
||||
|
||||
<em:contributor>Alexandre Poirot (https://github.com/ochameau)</em:contributor>
|
||||
<em:contributor>Anant Narayanan (https://github.com/anantn)</em:contributor>
|
||||
<em:contributor>Brandon Kase (https://github.com/bkase)</em:contributor>
|
||||
<em:contributor>Breck Yunits (https://github.com/breck7)</em:contributor>
|
||||
<em:contributor>César Carruitero (https://github.com/ccarruitero)</em:contributor>
|
||||
<em:contributor>David Gomes (https://github.com/davidgomes)</em:contributor>
|
||||
<em:contributor>Fabrice Desré (https://github.com/fabricedesre)</em:contributor>
|
||||
<em:contributor>Fraser Tweedale (https://github.com/frasertweedale)</em:contributor>
|
||||
<em:contributor>Harald Kirschner (https://github.com/digitarald)</em:contributor>
|
||||
<em:contributor>Jérémie Patonnier (https://github.com/JeremiePat)</em:contributor>
|
||||
<em:contributor>J. Ryan Stinnett (https://github.com/jryans)</em:contributor>
|
||||
<em:contributor>Kan-Ru Chen (陳侃如) (https://github.com/kanru)</em:contributor>
|
||||
<em:contributor>Louis Stowasser (https://github.com/louisstow)</em:contributor>
|
||||
<em:contributor>Luca Greco (https://github.com/rpl)</em:contributor>
|
||||
<em:contributor>Matthew Claypotch (https://github.com/potch)</em:contributor>
|
||||
<em:contributor>Matthew Riley MacPherson (https://github.com/tofumatt)</em:contributor>
|
||||
<em:contributor>Nick Desaulniers (https://github.com/nickdesaulniers)</em:contributor>
|
||||
<em:contributor>Soumen Ganguly (https://github.com/SoumenG)</em:contributor>
|
||||
<em:contributor>Sudheesh Singanamalla (https://github.com/sudheesh001)</em:contributor>
|
||||
<em:contributor>Victor Bjelkholm (https://github.com/VictorBjelkholm)</em:contributor>
|
||||
</Description>
|
||||
</RDF>
|
|
@ -7,9 +7,8 @@ const { Cc, Ci, Cu } = require("chrome");
|
|||
|
||||
const { SimulatorProcess } = require("./simulator-process");
|
||||
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const Self = require("sdk/self");
|
||||
const System = require("sdk/system");
|
||||
const { Simulator } = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
|
||||
const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
|
||||
|
||||
let process;
|
||||
|
||||
|
@ -35,18 +34,20 @@ function close() {
|
|||
return p.kill();
|
||||
}
|
||||
|
||||
let appinfo = {};
|
||||
|
||||
// Load data generated at build time that
|
||||
// expose various information about the runtime we ship
|
||||
let appinfo = System.staticArgs;
|
||||
AddonManager.getAddonByID(require("addon").id, function (addon) {
|
||||
appinfo.label = addon.name.replace(" Simulator", "");
|
||||
|
||||
Simulator.register(appinfo.label, {
|
||||
appinfo: appinfo,
|
||||
launch: launch,
|
||||
close: close
|
||||
Simulator.register(appinfo.label, {
|
||||
appinfo: appinfo,
|
||||
launch: launch,
|
||||
close: close
|
||||
});
|
||||
});
|
||||
|
||||
require("sdk/system/unload").when(function () {
|
||||
exports.shutdown = function () {
|
||||
Simulator.unregister(appinfo.label);
|
||||
close();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -14,13 +14,11 @@ const { emit, off } = require("sdk/event/core");
|
|||
const { Class } = require("sdk/core/heritage");
|
||||
const Environment = require("sdk/system/environment").env;
|
||||
const Runtime = require("sdk/system/runtime");
|
||||
const Self = require("sdk/self");
|
||||
const URL = require("sdk/url");
|
||||
const Subprocess = require("subprocess");
|
||||
const Subprocess = require("sdk/system/child_process/subprocess");
|
||||
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||
const Prefs = require("sdk/simple-prefs").prefs;
|
||||
|
||||
const { rootURI: ROOT_URI } = require('@loader/options');
|
||||
const ROOT_URI = require("addon").uri;
|
||||
const PROFILE_URL = ROOT_URI + "profile/";
|
||||
const BIN_URL = ROOT_URI + "b2g/";
|
||||
|
||||
|
@ -140,9 +138,14 @@ exports.SimulatorProcess = Class({
|
|||
if (this._executable) {
|
||||
return this._executable;
|
||||
}
|
||||
let customRuntime;
|
||||
try {
|
||||
let pref = "extensions." + require("addon").id + ".customRuntime";
|
||||
customRuntime = Services.prefs.getComplexValue(pref, Ci.nsIFile);
|
||||
} catch(e) {}
|
||||
|
||||
if (Prefs.customRuntime) {
|
||||
this._executable = Prefs.customRuntime;
|
||||
if (customRuntime) {
|
||||
this._executable = customRuntime;
|
||||
this._executableFilename = "Custom runtime";
|
||||
return this._executable;
|
||||
}
|
||||
|
@ -177,7 +180,13 @@ exports.SimulatorProcess = Class({
|
|||
get b2gArguments() {
|
||||
let args = [];
|
||||
|
||||
let profile = Prefs.gaiaProfile || URL.toFilename(PROFILE_URL);
|
||||
let gaiaProfile;
|
||||
try {
|
||||
let pref = "extensions." + require("addon").id + ".gaiaProfile";
|
||||
gaiaProfile = Services.prefs.getComplexValue(pref, Ci.nsIFile).path;
|
||||
} catch(e) {}
|
||||
|
||||
let profile = gaiaProfile || URL.toFilename(PROFILE_URL);
|
||||
args.push("-profile", profile);
|
||||
console.log("profile", profile);
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" ?>
|
||||
<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<setting pref="extensions.@ADDON_ID@.gaiaProfile" type="directory" title="Select a custom Gaia profile directory"/>
|
||||
<setting pref="extensions.@ADDON_ID@.customRuntime" type="file" title="Select a custom runtime executable"/>
|
||||
</vbox>
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"id": "fxos_@SLASH_VERSION@_simulator@mozilla.org",
|
||||
"name": "fxos_@SLASH_VERSION@_simulator",
|
||||
"version": "@FULL_VERSION@",
|
||||
"fullName": "Firefox OS @NUM_VERSION@ Simulator",
|
||||
"description": "a Firefox OS @NUM_VERSION@ simulator"
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
{
|
||||
"id": "fxos_simulator@mozilla.org",
|
||||
"name": "fxos_simulator",
|
||||
"version": "1.0.dev",
|
||||
"fullName": "Firefox OS Simulator",
|
||||
"label": "Firefox OS",
|
||||
"description": "a Firefox OS simulator",
|
||||
"author": "Myk Melez (https://github.com/mykmelez)",
|
||||
"contributors": [
|
||||
"Alexandre Poirot (https://github.com/ochameau)",
|
||||
"Anant Narayanan (https://github.com/anantn)",
|
||||
"Brandon Kase (https://github.com/bkase)",
|
||||
"Breck Yunits (https://github.com/breck7)",
|
||||
"César Carruitero (https://github.com/ccarruitero)",
|
||||
"David Gomes (https://github.com/davidgomes)",
|
||||
"Fabrice Desré (https://github.com/fabricedesre)",
|
||||
"Fraser Tweedale (https://github.com/frasertweedale)",
|
||||
"Harald Kirschner (https://github.com/digitarald)",
|
||||
"Jérémie Patonnier (https://github.com/JeremiePat)",
|
||||
"J. Ryan Stinnett (https://github.com/jryans)",
|
||||
"Kan-Ru Chen (陳侃如) (https://github.com/kanru)",
|
||||
"Louis Stowasser (https://github.com/louisstow)",
|
||||
"Luca Greco (https://github.com/rpl)",
|
||||
"Matthew Claypotch (https://github.com/potch)",
|
||||
"Matthew Riley MacPherson (https://github.com/tofumatt)",
|
||||
"Nick Desaulniers (https://github.com/nickdesaulniers)",
|
||||
"Soumen Ganguly (https://github.com/SoumenG)",
|
||||
"Sudheesh Singanamalla (https://github.com/sudheesh001)",
|
||||
"Victor Bjelkholm (https://github.com/VictorBjelkholm)"
|
||||
],
|
||||
"permissions": {
|
||||
"private-browsing": true
|
||||
},
|
||||
"preferences": [{
|
||||
"type": "directory",
|
||||
"name": "gaiaProfile",
|
||||
"title": "Select a custom Gaia profile directory"
|
||||
}, {
|
||||
"type": "file",
|
||||
"name": "customRuntime",
|
||||
"title": "Select a custom runtime executable"
|
||||
}],
|
||||
"license": "MPL 2.0",
|
||||
"unpack": true,
|
||||
"dependencies": ["subprocess"]
|
||||
}
|
|
@ -1,124 +0,0 @@
|
|||
<h2>What's that?</h2>
|
||||
Simply package enigmail hard work on providing IPC feature in mozilla platform.
|
||||
So we are able to launch child proccesses from javascript,
|
||||
and in our case, from addon-sdk libraries :)
|
||||
|
||||
<h2>Sample of code:</h2>
|
||||
This object allows to start a process, and read/write data to/from it
|
||||
using stdin/stdout/stderr streams.
|
||||
Usage example:
|
||||
|
||||
const subprocess = require("subprocess");
|
||||
var p = subprocess.call({
|
||||
command: '/bin/foo',
|
||||
arguments: ['-v', 'foo'],
|
||||
environment: [ "XYZ=abc", "MYVAR=def" ],
|
||||
charset: 'UTF-8',
|
||||
workdir: '/home/foo',
|
||||
//stdin: "some value to write to stdin\nfoobar",
|
||||
stdin: function(stdin) {
|
||||
stdin.write("some value to write to stdin\nfoobar");
|
||||
stdin.close();
|
||||
},
|
||||
stdout: function(data) {
|
||||
dump("got data on stdout:" + data + "\n");
|
||||
},
|
||||
stderr: function(data) {
|
||||
dump("got data on stderr:" + data + "\n");
|
||||
},
|
||||
done: function(result) {
|
||||
dump("process terminated with " + result.exitCode + "\n");
|
||||
},
|
||||
mergeStderr: false
|
||||
});
|
||||
p.wait(); // wait for the subprocess to terminate
|
||||
// this will block the main thread,
|
||||
// only do if you can wait that long
|
||||
|
||||
|
||||
Description of parameters:
|
||||
--------------------------
|
||||
Apart from <command>, all arguments are optional.
|
||||
|
||||
command: either a |nsIFile| object pointing to an executable file or a
|
||||
String containing the platform-dependent path to an executable
|
||||
file.
|
||||
|
||||
arguments: optional string array containing the arguments to the command.
|
||||
|
||||
environment: optional string array containing environment variables to pass
|
||||
to the command. The array elements must have the form
|
||||
"VAR=data". Please note that if environment is defined, it
|
||||
replaces any existing environment variables for the subprocess.
|
||||
|
||||
charset: Output is decoded with given charset and a string is returned.
|
||||
If charset is undefined, "UTF-8" is used as default.
|
||||
To get binary data, set this to null and the returned string
|
||||
is not decoded in any way.
|
||||
|
||||
workdir: optional; String containing the platform-dependent path to a
|
||||
directory to become the current working directory of the subprocess.
|
||||
|
||||
stdin: optional input data for the process to be passed on standard
|
||||
input. stdin can either be a string or a function.
|
||||
A |string| gets written to stdin and stdin gets closed;
|
||||
A |function| gets passed an object with write and close function.
|
||||
Please note that the write() function will return almost immediately;
|
||||
data is always written asynchronously on a separate thread.
|
||||
|
||||
stdout: an optional function that can receive output data from the
|
||||
process. The stdout-function is called asynchronously; it can be
|
||||
called mutliple times during the execution of a process.
|
||||
At a minimum at each occurance of \n or \r.
|
||||
Please note that null-characters might need to be escaped
|
||||
with something like 'data.replace(/\0/g, "\\0");'.
|
||||
|
||||
stderr: an optional function that can receive stderr data from the
|
||||
process. The stderr-function is called asynchronously; it can be
|
||||
called mutliple times during the execution of a process. Please
|
||||
note that null-characters might need to be escaped with
|
||||
something like 'data.replace(/\0/g, "\\0");'.
|
||||
(on windows it only gets called once right now)
|
||||
|
||||
done: optional function that is called when the process has terminated.
|
||||
The exit code from the process available via result.exitCode. If
|
||||
stdout is not defined, then the output from stdout is available
|
||||
via result.stdout. stderr data is in result.stderr
|
||||
|
||||
mergeStderr: optional boolean value. If true, stderr is merged with stdout;
|
||||
no data will be provided to stderr.
|
||||
|
||||
|
||||
Description of object returned by subprocess.call(...)
|
||||
------------------------------------------------------
|
||||
The object returned by subprocess.call offers a few methods that can be
|
||||
executed:
|
||||
|
||||
wait(): waits for the subprocess to terminate. It is not required to use
|
||||
wait; done will be called in any case when the subprocess terminated.
|
||||
|
||||
kill(hardKill): kill the subprocess. Any open pipes will be closed and
|
||||
done will be called.
|
||||
hardKill [ignored on Windows]:
|
||||
- false: signal the process terminate (SIGTERM)
|
||||
- true: kill the process (SIGKILL)
|
||||
|
||||
|
||||
Other methods in subprocess
|
||||
---------------------------
|
||||
|
||||
registerDebugHandler(functionRef): register a handler that is called to get
|
||||
debugging information
|
||||
registerLogHandler(functionRef): register a handler that is called to get error
|
||||
messages
|
||||
|
||||
example:
|
||||
subprocess.registerLogHandler( function(s) { dump(s); } );
|
||||
|
||||
|
||||
<h2>Credits:</h2>
|
||||
All enigmail team working on IPC component.
|
||||
The Initial Developer of this code is Jan Gerber.
|
||||
Portions created by Jan Gerber <j@mailb.org>,
|
||||
Patrick Brunschwig (author of almost all code) <patrick@mozilla-enigmail.org>,
|
||||
Ramalingam Saravanan (from enigmail team) <svn@xmlterm.org>
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,248 +0,0 @@
|
|||
/* 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/.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ChromeWorker Object subprocess.jsm on Unix-like systems (Linux, Mac OS X, ...)
|
||||
* to process stdin/stdout/stderr on separate threads.
|
||||
*
|
||||
*/
|
||||
|
||||
// Being a ChromeWorker object, implicitly uses the following:
|
||||
// Components.utils.import("resource://gre/modules/ctypes.jsm");
|
||||
|
||||
'use strict';
|
||||
|
||||
const BufferSize = 1024;
|
||||
|
||||
var libc = null;
|
||||
var libcFunc = {};
|
||||
|
||||
|
||||
/*
|
||||
struct pollfd {
|
||||
int fd; // file descriptor
|
||||
short events; // events to look for
|
||||
short revents; // events returned
|
||||
};
|
||||
*/
|
||||
|
||||
var pollfd = new ctypes.StructType("pollfd",
|
||||
[ {'fd': ctypes.int},
|
||||
{'events': ctypes.short},
|
||||
{'revents': ctypes.short}
|
||||
]);
|
||||
|
||||
var WriteBuffer = ctypes.uint8_t.array(BufferSize);
|
||||
var ReadBuffer = ctypes.char.array(BufferSize);
|
||||
|
||||
|
||||
const POLLIN = 0x0001;
|
||||
const POLLOUT = 0x0004;
|
||||
|
||||
const POLLERR = 0x0008; // some poll error occurred
|
||||
const POLLHUP = 0x0010; // file descriptor was "hung up"
|
||||
const POLLNVAL = 0x0020; // requested events "invalid"
|
||||
|
||||
const WNOHANG = 0x01;
|
||||
|
||||
const pid_t = ctypes.int32_t;
|
||||
|
||||
const INDEFINITE = -1;
|
||||
const NOWAIT = 0;
|
||||
const WAITTIME = 200 // wait time for poll() in ms
|
||||
|
||||
function initLibc(libName) {
|
||||
postMessage({msg: "debug", data: "initialising library with "+ libName});
|
||||
|
||||
libc = ctypes.open(libName);
|
||||
|
||||
libcFunc.pollFds = pollfd.array(1);
|
||||
|
||||
// int poll(struct pollfd fds[], nfds_t nfds, int timeout);
|
||||
libcFunc.poll = libc.declare("poll",
|
||||
ctypes.default_abi,
|
||||
ctypes.int,
|
||||
libcFunc.pollFds,
|
||||
ctypes.unsigned_int,
|
||||
ctypes.int);
|
||||
|
||||
//ssize_t write(int fd, const void *buf, size_t count);
|
||||
// NOTE: buf is declared as array of unsigned int8 instead of char to avoid
|
||||
// implicit charset conversion
|
||||
libcFunc.write = libc.declare("write",
|
||||
ctypes.default_abi,
|
||||
ctypes.int,
|
||||
ctypes.int,
|
||||
WriteBuffer,
|
||||
ctypes.int);
|
||||
|
||||
//int read(int fd, void *buf, size_t count);
|
||||
libcFunc.read = libc.declare("read",
|
||||
ctypes.default_abi,
|
||||
ctypes.int,
|
||||
ctypes.int,
|
||||
ReadBuffer,
|
||||
ctypes.int);
|
||||
|
||||
//int pipe(int pipefd[2]);
|
||||
libcFunc.pipefd = ctypes.int.array(2);
|
||||
|
||||
//int close(int fd);
|
||||
libcFunc.close = libc.declare("close",
|
||||
ctypes.default_abi,
|
||||
ctypes.int,
|
||||
ctypes.int);
|
||||
|
||||
//pid_t waitpid(pid_t pid, int *status, int options);
|
||||
libcFunc.waitpid = libc.declare("waitpid",
|
||||
ctypes.default_abi,
|
||||
pid_t,
|
||||
pid_t,
|
||||
ctypes.int.ptr,
|
||||
ctypes.int);
|
||||
}
|
||||
|
||||
function closePipe(pipe) {
|
||||
libcFunc.close(pipe);
|
||||
}
|
||||
|
||||
function writePipe(pipe, data) {
|
||||
|
||||
postMessage({msg: "debug", data: "trying to write to "+pipe});
|
||||
|
||||
let numChunks = Math.floor(data.length / BufferSize);
|
||||
let pData = new WriteBuffer();
|
||||
|
||||
for (var chunk = 0; chunk <= numChunks; chunk ++) {
|
||||
let numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize;
|
||||
for (var i=0; i < numBytes; i++) {
|
||||
pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256;
|
||||
}
|
||||
|
||||
let bytesWritten = libcFunc.write(pipe, pData, numBytes);
|
||||
if (bytesWritten != numBytes) {
|
||||
closePipe();
|
||||
libc.close();
|
||||
postMessage({ msg: "error", data: "error: wrote "+bytesWritten+" instead of "+numBytes+" bytes"});
|
||||
close();
|
||||
}
|
||||
}
|
||||
postMessage({msg: "info", data: "wrote "+data.length+" bytes of data"});
|
||||
}
|
||||
|
||||
|
||||
function readString(data, length, charset) {
|
||||
var string = '', bytes = [];
|
||||
for(var i = 0;i < length; i++) {
|
||||
if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data
|
||||
break;
|
||||
|
||||
bytes.push(data[i]);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function readPipe(pipe, charset, pid) {
|
||||
var p = new libcFunc.pollFds;
|
||||
p[0].fd = pipe;
|
||||
p[0].events = POLLIN | POLLERR | POLLHUP;
|
||||
p[0].revents = 0;
|
||||
var pollTimeout = WAITTIME;
|
||||
var exitCode = -1;
|
||||
var readCount = 0;
|
||||
var result, status = ctypes.int();
|
||||
result = 0;
|
||||
|
||||
|
||||
const i=0;
|
||||
while (true) {
|
||||
if (result == 0) {
|
||||
result = libcFunc.waitpid(pid, status.address(), WNOHANG);
|
||||
if (result > 0) {
|
||||
pollTimeout = NOWAIT;
|
||||
exitCode = parseInt(status.value);
|
||||
postMessage({msg: "debug", data: "waitpid signaled subprocess stop, exitcode="+status.value });
|
||||
}
|
||||
}
|
||||
var r = libcFunc.poll(p, 1, pollTimeout);
|
||||
if (r > 0) {
|
||||
if (p[i].revents & POLLIN) {
|
||||
postMessage({msg: "debug", data: "reading next chunk"});
|
||||
readCount = readPolledFd(p[i].fd, charset);
|
||||
if (readCount == 0) break;
|
||||
}
|
||||
|
||||
if (p[i].revents & POLLHUP) {
|
||||
postMessage({msg: "debug", data: "poll returned HUP"});
|
||||
break;
|
||||
}
|
||||
else if (p[i].revents & POLLERR) {
|
||||
postMessage({msg: "error", data: "poll returned error"});
|
||||
break;
|
||||
}
|
||||
else if (p[i].revents != POLLIN) {
|
||||
postMessage({msg: "error", data: "poll returned "+p[i]});
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (pollTimeout == 0 || r < 0) break;
|
||||
}
|
||||
|
||||
// continue reading until the buffer is empty
|
||||
while (readCount > 0) {
|
||||
readCount = readPolledFd(pipe, charset);
|
||||
}
|
||||
|
||||
libcFunc.close(pipe);
|
||||
postMessage({msg: "done", data: exitCode });
|
||||
libc.close();
|
||||
close();
|
||||
}
|
||||
|
||||
function readPolledFd(pipe, charset) {
|
||||
var line = new ReadBuffer();
|
||||
var r = libcFunc.read(pipe, line, BufferSize);
|
||||
|
||||
if (r > 0) {
|
||||
var c = readString(line, r, charset);
|
||||
postMessage({msg: "data", data: c, count: c.length});
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
onmessage = function (event) {
|
||||
switch (event.data.msg) {
|
||||
case "init":
|
||||
initLibc(event.data.libc);
|
||||
break;
|
||||
case "read":
|
||||
initLibc(event.data.libc);
|
||||
readPipe(event.data.pipe, event.data.charset, event.data.pid);
|
||||
break;
|
||||
case "write":
|
||||
// data contents:
|
||||
// msg: 'write'
|
||||
// data: the data (string) to write
|
||||
// pipe: ptr to pipe
|
||||
writePipe(event.data.pipe, event.data.data);
|
||||
postMessage({msg: "info", data: "WriteOK"});
|
||||
break;
|
||||
case "close":
|
||||
postMessage({msg: "debug", data: "closing stdin\n"});
|
||||
|
||||
closePipe(event.data.pipe);
|
||||
postMessage({msg: "info", data: "ClosedOK"});
|
||||
break;
|
||||
case "stop":
|
||||
libc.close(); // do not use libc after this point
|
||||
close();
|
||||
break;
|
||||
default:
|
||||
throw("error: Unknown command"+event.data.msg+"\n");
|
||||
}
|
||||
return;
|
||||
};
|
|
@ -1,206 +0,0 @@
|
|||
/* 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/.
|
||||
*/
|
||||
|
||||
/*
|
||||
* ChromeWorker Object subprocess.jsm on Windows to process stdin/stdout/stderr
|
||||
* on separate threads.
|
||||
*
|
||||
*/
|
||||
|
||||
// Being a ChromeWorker object, implicitly uses the following:
|
||||
// Components.utils.import("resource://gre/modules/ctypes.jsm");
|
||||
|
||||
'use strict';
|
||||
|
||||
const BufferSize = 1024;
|
||||
|
||||
const BOOL = ctypes.bool;
|
||||
const HANDLE = ctypes.size_t;
|
||||
const DWORD = ctypes.uint32_t;
|
||||
const LPDWORD = DWORD.ptr;
|
||||
const PVOID = ctypes.voidptr_t;
|
||||
const LPVOID = PVOID;
|
||||
|
||||
/*
|
||||
typedef struct _OVERLAPPED {
|
||||
ULONG_PTR Internal;
|
||||
ULONG_PTR InternalHigh;
|
||||
union {
|
||||
struct {
|
||||
DWORD Offset;
|
||||
DWORD OffsetHigh;
|
||||
};
|
||||
PVOID Pointer;
|
||||
};
|
||||
HANDLE hEvent;
|
||||
} OVERLAPPED, *LPOVERLAPPED;
|
||||
*/
|
||||
const OVERLAPPED = new ctypes.StructType("OVERLAPPED");
|
||||
|
||||
var ReadFileBuffer = ctypes.char.array(BufferSize);
|
||||
var WriteFileBuffer = ctypes.uint8_t.array(BufferSize);
|
||||
|
||||
var kernel32dll = null;
|
||||
var libFunc = {};
|
||||
|
||||
function initLib(libName) {
|
||||
if (ctypes.size_t.size == 8) {
|
||||
var WinABI = ctypes.default_abi;
|
||||
} else {
|
||||
var WinABI = ctypes.winapi_abi;
|
||||
}
|
||||
|
||||
kernel32dll = ctypes.open(libName);
|
||||
|
||||
/*
|
||||
BOOL WINAPI WriteFile(
|
||||
__in HANDLE hFile,
|
||||
__in LPCVOID lpBuffer,
|
||||
__in DWORD nNumberOfBytesToWrite,
|
||||
__out_opt LPDWORD lpNumberOfBytesWritten,
|
||||
__inout_opt LPOVERLAPPED lpOverlapped
|
||||
);
|
||||
|
||||
NOTE: lpBuffer is declared as array of unsigned int8 instead of char to avoid
|
||||
implicit charset conversion
|
||||
*/
|
||||
libFunc.WriteFile = kernel32dll.declare("WriteFile",
|
||||
WinABI,
|
||||
BOOL,
|
||||
HANDLE,
|
||||
WriteFileBuffer,
|
||||
DWORD,
|
||||
LPDWORD,
|
||||
OVERLAPPED.ptr
|
||||
);
|
||||
|
||||
/*
|
||||
BOOL WINAPI ReadFile(
|
||||
__in HANDLE hFile,
|
||||
__out LPVOID ReadFileBuffer,
|
||||
__in DWORD nNumberOfBytesToRead,
|
||||
__out_opt LPDWORD lpNumberOfBytesRead,
|
||||
__inout_opt LPOVERLAPPED lpOverlapped
|
||||
);
|
||||
*/
|
||||
libFunc.ReadFile = kernel32dll.declare("ReadFile",
|
||||
WinABI,
|
||||
BOOL,
|
||||
HANDLE,
|
||||
ReadFileBuffer,
|
||||
DWORD,
|
||||
LPDWORD,
|
||||
OVERLAPPED.ptr
|
||||
);
|
||||
|
||||
/*
|
||||
BOOL WINAPI CloseHandle(
|
||||
__in HANDLE hObject
|
||||
);
|
||||
*/
|
||||
libFunc.CloseHandle = kernel32dll.declare("CloseHandle",
|
||||
WinABI,
|
||||
BOOL,
|
||||
HANDLE
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function writePipe(pipe, data) {
|
||||
var bytesWritten = DWORD(0);
|
||||
|
||||
var pData = new WriteFileBuffer();
|
||||
|
||||
var numChunks = Math.floor(data.length / BufferSize);
|
||||
for (var chunk = 0; chunk <= numChunks; chunk ++) {
|
||||
var numBytes = chunk < numChunks ? BufferSize : data.length - chunk * BufferSize;
|
||||
for (var i=0; i < numBytes; i++) {
|
||||
pData[i] = data.charCodeAt(chunk * BufferSize + i) % 256;
|
||||
}
|
||||
|
||||
var r = libFunc.WriteFile(pipe, pData, numBytes, bytesWritten.address(), null);
|
||||
if (bytesWritten.value != numBytes)
|
||||
throw("error: wrote "+bytesWritten.value+" instead of "+numBytes+" bytes");
|
||||
}
|
||||
postMessage("wrote "+data.length+" bytes of data");
|
||||
}
|
||||
|
||||
function readString(data, length, charset) {
|
||||
var string = '', bytes = [];
|
||||
for(var i = 0;i < length; i++) {
|
||||
if(data[i] == 0 && charset != "null") // stop on NULL character for non-binary data
|
||||
break;
|
||||
|
||||
bytes.push(data[i]);
|
||||
}
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function readPipe(pipe, charset) {
|
||||
while (true) {
|
||||
var bytesRead = DWORD(0);
|
||||
var line = new ReadFileBuffer();
|
||||
var r = libFunc.ReadFile(pipe, line, BufferSize, bytesRead.address(), null);
|
||||
|
||||
if (!r) {
|
||||
// stop if we get an error (such as EOF reached)
|
||||
postMessage({msg: "info", data: "ReadFile failed"});
|
||||
break;
|
||||
}
|
||||
|
||||
if (bytesRead.value > 0) {
|
||||
var c = readString(line, bytesRead.value, charset);
|
||||
postMessage({msg: "data", data: c, count: c.length});
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
libFunc.CloseHandle(pipe);
|
||||
postMessage({msg: "done"});
|
||||
kernel32dll.close();
|
||||
close();
|
||||
}
|
||||
|
||||
onmessage = function (event) {
|
||||
let pipePtr;
|
||||
switch (event.data.msg) {
|
||||
case "init":
|
||||
initLib(event.data.libc);
|
||||
break;
|
||||
case "write":
|
||||
// data contents:
|
||||
// msg: 'write'
|
||||
// data: the data (string) to write
|
||||
// pipe: ptr to pipe
|
||||
pipePtr = HANDLE.ptr(event.data.pipe);
|
||||
writePipe(pipePtr.contents, event.data.data);
|
||||
postMessage("WriteOK");
|
||||
break;
|
||||
case "read":
|
||||
initLib(event.data.libc);
|
||||
pipePtr = HANDLE.ptr(event.data.pipe);
|
||||
readPipe(pipePtr.contents, event.data.charset);
|
||||
break;
|
||||
case "close":
|
||||
pipePtr = HANDLE.ptr(event.data.pipe);
|
||||
postMessage("closing stdin\n");
|
||||
|
||||
if (libFunc.CloseHandle(pipePtr.contents)) {
|
||||
postMessage("ClosedOK");
|
||||
}
|
||||
else
|
||||
postMessage("Could not close stdin handle");
|
||||
break;
|
||||
case "stop":
|
||||
kernel32dll.close();
|
||||
close();
|
||||
break;
|
||||
default:
|
||||
throw("error: Unknown command"+event.data.msg+"\n");
|
||||
}
|
||||
return;
|
||||
};
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"name": "subprocess",
|
||||
"license": "MPL 1.1/GPL 2.0/LGPL 2.1",
|
||||
"author": "Alexandre Poirot",
|
||||
"contributors": [
|
||||
"Jan Gerber (original creator) <j@mailb.org>",
|
||||
"Patrick Brunschwig (author of almost all code) <patrick@mozilla-enigmail.org>",
|
||||
"Ramalingam Saravanan (from enigmail team) <svn@xmlterm.org>"
|
||||
],
|
||||
"version": "0.1.1",
|
||||
"dependencies": [
|
||||
"api-utils"
|
||||
],
|
||||
"description": "Addon-sdk package for subprocess xpcom components from enigmail. Allow to run process, manipulate stdin/out and kill it."
|
||||
}
|
|
@ -1,144 +0,0 @@
|
|||
/* 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/.
|
||||
*/
|
||||
|
||||
const { Cc, Ci } = require("chrome");
|
||||
const subprocess = require("subprocess");
|
||||
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
|
||||
// For now, only test on windows
|
||||
if (env.get('OS') && env.get('OS').match(/Windows/)) {
|
||||
|
||||
exports.testWindows = function (test) {
|
||||
test.waitUntilDone();
|
||||
let envTestValue = "OK";
|
||||
let gotStdout = false;
|
||||
|
||||
var p = subprocess.call({
|
||||
// Retrieve windows cmd.exe path from env
|
||||
command: env.get('ComSpec'),
|
||||
// In order to execute a simple "echo" function
|
||||
arguments: ['/C', 'echo %ENV_TEST%'], // ' & type CON' should display stdin, but doesn't work
|
||||
// Printing an environnement variable set here by the parent process
|
||||
environment: ['ENV_TEST='+envTestValue],
|
||||
|
||||
stdin: function(stdin) {
|
||||
// Win32 command line is not really made for stdin
|
||||
// So it doesn't seems to work as it's hard to retrieve stdin
|
||||
stdin.write("stdin");
|
||||
stdin.close();
|
||||
},
|
||||
stdout: function(data) {
|
||||
test.assert(!gotStdout,"don't get stdout twice");
|
||||
test.assertEqual(data,envTestValue+"\r\n","stdout contains the environment variable");
|
||||
gotStdout = true;
|
||||
},
|
||||
stderr: function(data) {
|
||||
test.fail("shouldn't get stderr");
|
||||
},
|
||||
done: function() {
|
||||
test.assert(gotStdout, "got stdout before finished");
|
||||
test.done();
|
||||
},
|
||||
mergeStderr: false
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
exports.testWindowsStderr = function (test) {
|
||||
test.waitUntilDone();
|
||||
let gotStderr = false;
|
||||
|
||||
var p = subprocess.call({
|
||||
command: env.get('ComSpec'),
|
||||
arguments: ['/C', 'nonexistent'],
|
||||
|
||||
stdout: function(data) {
|
||||
test.fail("shouldn't get stdout");
|
||||
},
|
||||
stderr: function(data) {
|
||||
test.assert(!gotStderr,"don't get stderr twice");
|
||||
test.assertEqual(
|
||||
data,
|
||||
"'nonexistent' is not recognized as an internal or external command,\r\n" +
|
||||
"operable program or batch file.\r\n",
|
||||
"stderr contains the error message"
|
||||
);
|
||||
gotStderr = true;
|
||||
},
|
||||
done: function() {
|
||||
test.assert(gotStderr, "got stderr before finished");
|
||||
test.done();
|
||||
},
|
||||
mergeStderr: false
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (env.get('USER') && env.get('SHELL')) {
|
||||
|
||||
exports.testUnix = function (test) {
|
||||
test.waitUntilDone();
|
||||
let envTestValue = "OK";
|
||||
let gotStdout = false;
|
||||
|
||||
var p = subprocess.call({
|
||||
command: '/bin/sh',
|
||||
// Print stdin and our env variable
|
||||
//arguments: ['-c', 'echo $@ $ENV_TEST'],
|
||||
environment: ['ENV_TEST='+envTestValue],
|
||||
|
||||
stdin: function(stdin) {
|
||||
stdin.write("echo $ENV_TEST");
|
||||
stdin.close();
|
||||
},
|
||||
stdout: function(data) {
|
||||
test.assert(!gotStdout,"don't get stdout twice");
|
||||
test.assertEqual(data,envTestValue+"\n","stdout contains the environment variable");
|
||||
gotStdout = true;
|
||||
},
|
||||
stderr: function(data) {
|
||||
test.fail("shouldn't get stderr");
|
||||
},
|
||||
done: function() {
|
||||
test.assert(gotStdout, "got stdout before finished");
|
||||
test.done();
|
||||
},
|
||||
mergeStderr: false
|
||||
});
|
||||
}
|
||||
|
||||
exports.testUnixStderr = function (test) {
|
||||
test.waitUntilDone();
|
||||
let gotStderr = false;
|
||||
|
||||
var p = subprocess.call({
|
||||
// Hope that we don't have to give absolute path on linux ...
|
||||
command: '/bin/sh',
|
||||
arguments: ['nonexistent'],
|
||||
|
||||
stdout: function(data) {
|
||||
test.fail("shouldn't get stdout");
|
||||
},
|
||||
stderr: function(data) {
|
||||
test.assert(!gotStderr,"don't get stderr twice");
|
||||
// There is two variant of error message
|
||||
if (data == "/bin/sh: 0: Can't open nonexistent\n")
|
||||
test.pass("stderr containes the expected error message");
|
||||
else
|
||||
test.assertEqual(data, "/bin/sh: nonexistent: No such file or directory\n",
|
||||
"stderr contains the error message");
|
||||
gotStderr = true;
|
||||
},
|
||||
done: function() {
|
||||
test.assert(gotStderr, "got stderr before finished");
|
||||
test.done();
|
||||
},
|
||||
mergeStderr: false
|
||||
});
|
||||
}
|
||||
|
||||
}
|
Загрузка…
Ссылка в новой задаче