Bug 1272522 Handle stderr of native app r=kmag

MozReview-Commit-ID: 5qGw83uTYTu

--HG--
extra : rebase_source : 8cefe9f2661782b45000ff704db2ea7aaa46ac68
This commit is contained in:
Andrew Swan 2016-07-06 14:57:56 -07:00
Родитель b5739bf710
Коммит 5b775d5271
5 изменённых файлов: 109 добавлений и 1 удалений

Просмотреть файл

@ -202,6 +202,7 @@ this.NativeApp = class extends EventEmitter {
command: command, command: command,
arguments: [hostInfo.path], arguments: [hostInfo.path],
workdir: OS.Path.dirname(command), workdir: OS.Path.dirname(command),
stderr: "pipe",
}; };
return Subprocess.call(subprocessOpts); return Subprocess.call(subprocessOpts);
}).then(proc => { }).then(proc => {
@ -209,6 +210,7 @@ this.NativeApp = class extends EventEmitter {
this.proc = proc; this.proc = proc;
this._startRead(); this._startRead();
this._startWrite(); this._startWrite();
this._startStderrRead();
}).catch(err => { }).catch(err => {
this.startupPromise = null; this.startupPromise = null;
Cu.reportError(err.message); Cu.reportError(err.message);
@ -268,6 +270,32 @@ this.NativeApp = class extends EventEmitter {
}); });
} }
_startStderrRead() {
let proc = this.proc;
let app = this.name;
Task.spawn(function* () {
let partial = "";
while (true) {
let data = yield proc.stderr.readString();
if (data.length == 0) {
// We have hit EOF, just stop reading
if (partial) {
Services.console.logStringMessage(`stderr output from native app ${app}: ${partial}`);
}
break;
}
let lines = data.split(/\r?\n/);
lines[0] = partial + lines[0];
partial = lines.pop();
for (let line of lines) {
Services.console.logStringMessage(`stderr output from native app ${app}: ${line}`);
}
}
});
}
send(msg) { send(msg) {
if (this._isDisconnected) { if (this._isDisconnected) {
throw new this.context.cloneScope.Error("Attempt to postMessage on disconnected port"); throw new this.context.cloneScope.Error("Attempt to postMessage on disconnected port");

Просмотреть файл

@ -9,6 +9,7 @@
"sendAsyncMessage": false, "sendAsyncMessage": false,
"waitForLoad": true, "waitForLoad": true,
"promiseConsoleOutput": true,
"ExtensionTestUtils": false, "ExtensionTestUtils": false,
"NetUtil": true, "NetUtil": true,

Просмотреть файл

@ -1,5 +1,6 @@
[DEFAULT] [DEFAULT]
support-files = support-files =
head.js
file_download.html file_download.html
file_download.txt file_download.txt
interruptible.sjs interruptible.sjs

Просмотреть файл

@ -10,3 +10,4 @@ function waitForLoad(win) {
}, true); }, true);
}); });
} }

Просмотреть файл

@ -6,7 +6,6 @@
<script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script> <script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
<script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script> <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script> <script type="text/javascript" src="head.js"></script>
<script type="text/javascript" src="test_constants.js"></script>
<link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/> <link rel="stylesheet" href="chrome://mochikit/contents/tests/SimpleTest/test.css"/>
</head> </head>
<body> <body>
@ -23,11 +22,40 @@ Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/osfile.jsm"); Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
let {Subprocess, SubprocessImpl} = Cu.import("resource://gre/modules/Subprocess.jsm"); let {Subprocess, SubprocessImpl} = Cu.import("resource://gre/modules/Subprocess.jsm");
Components.utils.import("resource://gre/modules/Task.jsm");
if (AppConstants.platform == "win") { if (AppConstants.platform == "win") {
Cu.import("resource://testing-common/MockRegistry.jsm"); Cu.import("resource://testing-common/MockRegistry.jsm");
} }
var promiseConsoleOutput = Task.async(function* (task) {
const DONE = "=== extension test console listener done ===";
let listener;
let messages = [];
let awaitListener = new Promise(resolve => {
listener = msg => {
if (msg == DONE) {
resolve();
} else if (msg instanceof Ci.nsIConsoleMessage) {
messages.push(msg.message);
}
};
});
Services.console.registerListener(listener);
try {
let result = yield task();
Services.console.logStringMessage(DONE);
yield awaitListener;
return {messages, result};
} finally {
Services.console.unregisterListener(listener);
}
});
const PREF_MAX_READ = "webextensions.native-messaging.max-input-message-bytes"; const PREF_MAX_READ = "webextensions.native-messaging.max-input-message-bytes";
const PREF_MAX_WRITE = "webextensions.native-messaging.max-output-message-bytes"; const PREF_MAX_WRITE = "webextensions.native-messaging.max-output-message-bytes";
@ -96,6 +124,21 @@ while True:
sys.stdout.write(msg) sys.stdout.write(msg)
`; `;
const STDERR_LINES = ["hello stderr", "this should be a separate line"];
let STDERR_MSG = STDERR_LINES.join("\\n");
// Python apparently line-buffers stderr even with the -u arg on
// Windows 7. Dealing with that is more hassle than its worth but
// on other platforms, we want to keep testing partial lines.
if (AppConstants.isPlatformAndVersionAtMost("win", "7")) {
STDERR_MSG += "\\n";
}
const STDERR_BODY = String.raw`
import sys
sys.stderr.write("${STDERR_MSG}")
`;
const SCRIPTS = [ const SCRIPTS = [
{ {
name: "echo", name: "echo",
@ -112,6 +155,11 @@ const SCRIPTS = [
description: "a native app that does not exit when stdin closes or on SIGTERM", description: "a native app that does not exit when stdin closes or on SIGTERM",
script: WONTDIE_BODY, script: WONTDIE_BODY,
}, },
{
name: "stderr",
description: "a native app that writes to stderr and then exits",
script: STDERR_BODY,
},
]; ];
add_task(function* setup() { add_task(function* setup() {
@ -610,6 +658,35 @@ add_task(function* test_unresponsive_native_app() {
is(procCount, 0, "subprocess was succesfully killed"); is(procCount, 0, "subprocess was succesfully killed");
}); });
add_task(function* test_stderr() {
function background() {
let port = browser.runtime.connectNative("stderr");
port.onDisconnect.addListener(() => {
browser.test.sendMessage("finished");
});
}
let {messages} = yield promiseConsoleOutput(function* () {
let extension = ExtensionTestUtils.loadExtension({
background: `(${background})()`,
manifest: {
permissions: ["nativeMessaging"],
},
}, ID);
yield extension.startup();
yield extension.awaitMessage("finished");
yield extension.unload();
});
let lines = STDERR_LINES.map(line => messages.findIndex(msg => msg.includes(line)));
isnot(lines[0], -1, "Saw first line of stderr output on the console");
isnot(lines[1], -1, "Saw second line of stderr output on the console");
isnot(lines[0], lines[1], "Stderr output lines are separated in the console");
yield waitForSubprocessExit();
});
</script> </script>
</body> </body>