Bug 1290598: Move slower native messaging tests to a separate unit. r?aswan draft
authorKris Maglione <maglione.k@gmail.com>
Fri, 29 Jul 2016 14:35:04 -0700
changeset 394688 5afc118a7b27ae71c9a5b34360180ef270b014a3
parent 394687 12ce3e486fbad367b30b595657e51f18d10b6810
child 526860 6ab40227ae74981cdd877d990d6dacd48c28d11d
push id24624
push usermaglione.k@gmail.com
push dateSat, 30 Jul 2016 19:15:18 +0000
reviewersaswan
bugs1290598
milestone50.0a1
Bug 1290598: Move slower native messaging tests to a separate unit. r?aswan MozReview-Commit-ID: 2kXbNae1bmR
toolkit/components/extensions/test/xpcshell/native_messaging.ini
toolkit/components/extensions/test/xpcshell/test_ext_native_messaging.js
toolkit/components/extensions/test/xpcshell/test_ext_native_messaging_unresponsive.js
--- a/toolkit/components/extensions/test/xpcshell/native_messaging.ini
+++ b/toolkit/components/extensions/test/xpcshell/native_messaging.ini
@@ -4,8 +4,9 @@ tail =
 firefox-appdir = browser
 skip-if = toolkit == 'gonk' || appname == "thunderbird" || os == "android"
 subprocess = true
 support-files =
   data/**
 
 [test_ext_native_messaging.js]
 [test_ext_native_messaging_perf.js]
+[test_ext_native_messaging_unresponsive.js]
--- a/toolkit/components/extensions/test/xpcshell/test_ext_native_messaging.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_native_messaging.js
@@ -29,43 +29,16 @@ const INFO_BODY = String.raw`
   import sys
 
   msg = json.dumps({"args": sys.argv, "cwd": os.getcwd()})
   sys.stdout.write(struct.pack('@I', len(msg)))
   sys.stdout.write(msg)
   sys.exit(0)
 `;
 
-const WONTDIE_BODY = String.raw`
-  import signal
-  import struct
-  import sys
-  import time
-
-  signal.signal(signal.SIGTERM, signal.SIG_IGN)
-
-  def spin():
-      while True:
-          try:
-              signal.pause()
-          except AttributeError:
-              time.sleep(5)
-
-  while True:
-      rawlen = sys.stdin.read(4)
-      if len(rawlen) == 0:
-          spin()
-
-      msglen = struct.unpack('@I', rawlen)[0]
-      msg = sys.stdin.read(msglen)
-
-      sys.stdout.write(struct.pack('@I', msglen))
-      sys.stdout.write(msg)
-`;
-
 const STDERR_LINES = ["hello stderr", "this should be a separate line"];
 let STDERR_MSG = STDERR_LINES.join("\\n");
 
 const STDERR_BODY = String.raw`
   import sys
   sys.stderr.write("${STDERR_MSG}")
 `;
 
@@ -76,21 +49,16 @@ const SCRIPTS = [
     script: ECHO_BODY.replace(/^ {2}/gm, ""),
   },
   {
     name: "info",
     description: "a native app that gives some info about how it was started",
     script: INFO_BODY.replace(/^ {2}/gm, ""),
   },
   {
-    name: "wontdie",
-    description: "a native app that does not exit when stdin closes or on SIGTERM",
-    script: WONTDIE_BODY.replace(/^ {2}/gm, ""),
-  },
-  {
     name: "stderr",
     description: "a native app that writes to stderr and then exits",
     script: STDERR_BODY.replace(/^ {2}/gm, ""),
   },
 ];
 
 add_task(function* setup() {
   yield setupHosts(SCRIPTS);
@@ -458,54 +426,16 @@ add_task(function* test_child_process() 
   equal(msg.cwd.replace(/^\/private\//, ""), tmpDir.path,
         "Working directory is the directory containing the native appliation");
 
   let exitPromise = waitForSubprocessExit();
   yield extension.unload();
   yield exitPromise;
 });
 
-// Test that an unresponsive native application still gets killed eventually
-add_task(function* test_unresponsive_native_app() {
-  // XXX expose GRACEFUL_SHUTDOWN_TIME as a pref and reduce it
-  // just for this test?
-
-  function background() {
-    let port = browser.runtime.connectNative("wontdie");
-
-    const MSG = "echo me";
-    // bounce a message to make sure the process actually starts
-    port.onMessage.addListener(msg => {
-      browser.test.assertEq(msg, MSG, "Received echoed message");
-      browser.test.sendMessage("ready");
-    });
-    port.postMessage(MSG);
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-  yield extension.awaitMessage("ready");
-
-  let procCount = yield getSubprocessCount();
-  equal(procCount, 1, "subprocess is running");
-
-  let exitPromise = waitForSubprocessExit();
-  yield extension.unload();
-  yield exitPromise;
-
-  procCount = yield getSubprocessCount();
-  equal(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");
     });
   }
 
copy from toolkit/components/extensions/test/xpcshell/test_ext_native_messaging.js
copy to toolkit/components/extensions/test/xpcshell/test_ext_native_messaging_unresponsive.js
--- a/toolkit/components/extensions/test/xpcshell/test_ext_native_messaging.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_native_messaging_unresponsive.js
@@ -1,44 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-/* globals chrome */
-
-const PREF_MAX_READ = "webextensions.native-messaging.max-input-message-bytes";
-const PREF_MAX_WRITE = "webextensions.native-messaging.max-output-message-bytes";
-
-const ECHO_BODY = String.raw`
-  import struct
-  import sys
-
-  while True:
-      rawlen = sys.stdin.read(4)
-      if len(rawlen) == 0:
-          sys.exit(0)
-      msglen = struct.unpack('@I', rawlen)[0]
-      msg = sys.stdin.read(msglen)
-
-      sys.stdout.write(struct.pack('@I', msglen))
-      sys.stdout.write(msg)
-`;
-
-const INFO_BODY = String.raw`
-  import json
-  import os
-  import struct
-  import sys
-
-  msg = json.dumps({"args": sys.argv, "cwd": os.getcwd()})
-  sys.stdout.write(struct.pack('@I', len(msg)))
-  sys.stdout.write(msg)
-  sys.exit(0)
-`;
-
 const WONTDIE_BODY = String.raw`
   import signal
   import struct
   import sys
   import time
 
   signal.signal(signal.SIGTERM, signal.SIG_IGN)
 
@@ -56,417 +24,28 @@ const WONTDIE_BODY = String.raw`
 
       msglen = struct.unpack('@I', rawlen)[0]
       msg = sys.stdin.read(msglen)
 
       sys.stdout.write(struct.pack('@I', msglen))
       sys.stdout.write(msg)
 `;
 
-const STDERR_LINES = ["hello stderr", "this should be a separate line"];
-let STDERR_MSG = STDERR_LINES.join("\\n");
-
-const STDERR_BODY = String.raw`
-  import sys
-  sys.stderr.write("${STDERR_MSG}")
-`;
-
 const SCRIPTS = [
   {
-    name: "echo",
-    description: "a native app that echoes back messages it receives",
-    script: ECHO_BODY.replace(/^ {2}/gm, ""),
-  },
-  {
-    name: "info",
-    description: "a native app that gives some info about how it was started",
-    script: INFO_BODY.replace(/^ {2}/gm, ""),
-  },
-  {
     name: "wontdie",
     description: "a native app that does not exit when stdin closes or on SIGTERM",
     script: WONTDIE_BODY.replace(/^ {2}/gm, ""),
   },
-  {
-    name: "stderr",
-    description: "a native app that writes to stderr and then exits",
-    script: STDERR_BODY.replace(/^ {2}/gm, ""),
-  },
 ];
 
 add_task(function* setup() {
   yield setupHosts(SCRIPTS);
 });
 
-// Test the basic operation of native messaging with a simple
-// script that echoes back whatever message is sent to it.
-add_task(function* test_happy_path() {
-  function background() {
-    let port = browser.runtime.connectNative("echo");
-    port.onMessage.addListener(msg => {
-      browser.test.sendMessage("message", msg);
-    });
-    browser.test.onMessage.addListener((what, payload) => {
-      if (what == "send") {
-        if (payload._json) {
-          let json = payload._json;
-          payload.toJSON = () => json;
-          delete payload._json;
-        }
-        port.postMessage(payload);
-      }
-    });
-    browser.test.sendMessage("ready");
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-  yield extension.awaitMessage("ready");
-  const tests = [
-    {
-      data: "this is a string",
-      what: "simple string",
-    },
-    {
-      data: "Это юникода",
-      what: "unicode string",
-    },
-    {
-      data: {test: "hello"},
-      what: "simple object",
-    },
-    {
-      data: {
-        what: "An object with a few properties",
-        number: 123,
-        bool: true,
-        nested: {what: "another object"},
-      },
-      what: "object with several properties",
-    },
-
-    {
-      data: {
-        ignoreme: true,
-        _json: {data: "i have a tojson method"},
-      },
-      expected: {data: "i have a tojson method"},
-      what: "object with toJSON() method",
-    },
-  ];
-  for (let test of tests) {
-    extension.sendMessage("send", test.data);
-    let response = yield extension.awaitMessage("message");
-    let expected = test.expected || test.data;
-    deepEqual(response, expected, `Echoed a message of type ${test.what}`);
-  }
-
-  let procCount = yield getSubprocessCount();
-  equal(procCount, 1, "subprocess is still running");
-  let exitPromise = waitForSubprocessExit();
-  yield extension.unload();
-  yield exitPromise;
-});
-
-if (AppConstants.platform == "win") {
-  // "relative.echo" has a relative path in the host manifest.
-  add_task(function* test_relative_path() {
-    function background() {
-      let port = browser.runtime.connectNative("relative.echo");
-      let MSG = "test relative echo path";
-      port.onMessage.addListener(msg => {
-        browser.test.assertEq(MSG, msg, "Got expected message back");
-        browser.test.sendMessage("done");
-      });
-      port.postMessage(MSG);
-    }
-
-    let extension = ExtensionTestUtils.loadExtension({
-      background,
-      manifest: {
-        permissions: ["nativeMessaging"],
-      },
-    }, ID);
-
-    yield extension.startup();
-    yield extension.awaitMessage("done");
-
-    let procCount = yield getSubprocessCount();
-    equal(procCount, 1, "subprocess is still running");
-    let exitPromise = waitForSubprocessExit();
-    yield extension.unload();
-    yield exitPromise;
-  });
-}
-
-// Test sendNativeMessage()
-add_task(function* test_sendNativeMessage() {
-  function background() {
-    let MSG = {test: "hello world"};
-
-    // Check error handling
-    browser.runtime.sendNativeMessage("nonexistent", MSG).then(() => {
-      browser.test.fail("sendNativeMessage() to a nonexistent app should have failed");
-    }, err => {
-      browser.test.succeed("sendNativeMessage() to a nonexistent app failed");
-    }).then(() => {
-      // Check regular message exchange
-      return browser.runtime.sendNativeMessage("echo", MSG);
-    }).then(reply => {
-      let expected = JSON.stringify(MSG);
-      let received = JSON.stringify(reply);
-      browser.test.assertEq(expected, received, "Received echoed native message");
-      browser.test.sendMessage("finished");
-    });
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-  yield extension.awaitMessage("finished");
-
-  // With sendNativeMessage(), the subprocess should be disconnected
-  // after exchanging a single message.
-  yield waitForSubprocessExit();
-
-  yield extension.unload();
-});
-
-// Test calling Port.disconnect()
-add_task(function* test_disconnect() {
-  function background() {
-    let port = browser.runtime.connectNative("echo");
-    port.onMessage.addListener(msg => {
-      browser.test.sendMessage("message", msg);
-    });
-    browser.test.onMessage.addListener((what, payload) => {
-      if (what == "send") {
-        if (payload._json) {
-          let json = payload._json;
-          payload.toJSON = () => json;
-          delete payload._json;
-        }
-        port.postMessage(payload);
-      } else if (what == "disconnect") {
-        try {
-          port.disconnect();
-          browser.test.sendMessage("disconnect-result", {success: true});
-        } catch (err) {
-          browser.test.sendMessage("disconnect-result", {
-            success: false,
-            errmsg: err.message,
-          });
-        }
-      }
-    });
-    browser.test.sendMessage("ready");
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-  yield extension.awaitMessage("ready");
-
-  extension.sendMessage("send", "test");
-  let response = yield extension.awaitMessage("message");
-  equal(response, "test", "Echoed a string");
-
-  let procCount = yield getSubprocessCount();
-  equal(procCount, 1, "subprocess is running");
-
-  extension.sendMessage("disconnect");
-  response = yield extension.awaitMessage("disconnect-result");
-  equal(response.success, true, "disconnect succeeded");
-
-  do_print("waiting for subprocess to exit");
-  yield waitForSubprocessExit();
-  procCount = yield getSubprocessCount();
-  equal(procCount, 0, "subprocess is no longer running");
-
-  extension.sendMessage("disconnect");
-  response = yield extension.awaitMessage("disconnect-result");
-  equal(response.success, false, "second call to disconnect failed");
-  ok(/already disconnected/.test(response.errmsg), "disconnect error message is reasonable");
-
-  yield extension.unload();
-});
-
-// Test the limit on message size for writing
-add_task(function* test_write_limit() {
-  Services.prefs.setIntPref(PREF_MAX_WRITE, 10);
-  function clearPref() {
-    Services.prefs.clearUserPref(PREF_MAX_WRITE);
-  }
-  do_register_cleanup(clearPref);
-
-  function background() {
-    const PAYLOAD = "0123456789A";
-    let port = browser.runtime.connectNative("echo");
-    try {
-      port.postMessage(PAYLOAD);
-      browser.test.sendMessage("result", null);
-    } catch (ex) {
-      browser.test.sendMessage("result", ex.message);
-    }
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-
-  let errmsg = yield extension.awaitMessage("result");
-  notEqual(errmsg, null, "native postMessage() failed for overly large message");
-
-  yield extension.unload();
-  yield waitForSubprocessExit();
-
-  clearPref();
-});
-
-// Test the limit on message size for reading
-add_task(function* test_read_limit() {
-  Services.prefs.setIntPref(PREF_MAX_READ, 10);
-  function clearPref() {
-    Services.prefs.clearUserPref(PREF_MAX_READ);
-  }
-  do_register_cleanup(clearPref);
-
-  function background() {
-    const PAYLOAD = "0123456789A";
-    let port = browser.runtime.connectNative("echo");
-    port.onDisconnect.addListener(() => {
-      browser.test.sendMessage("result", "disconnected");
-    });
-    port.onMessage.addListener(msg => {
-      browser.test.sendMessage("result", "message");
-    });
-    port.postMessage(PAYLOAD);
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-
-  let result = yield extension.awaitMessage("result");
-  equal(result, "disconnected", "native port disconnected on receiving large message");
-
-  yield extension.unload();
-  yield waitForSubprocessExit();
-
-  clearPref();
-});
-
-// Test that an extension without the nativeMessaging permission cannot
-// use native messaging.
-add_task(function* test_ext_permission() {
-  function background() {
-    browser.test.assertFalse("connectNative" in chrome.runtime, "chrome.runtime.connectNative does not exist without nativeMessaging permission");
-    browser.test.assertFalse("connectNative" in browser.runtime, "browser.runtime.connectNative does not exist without nativeMessaging permission");
-    browser.test.assertFalse("sendNativeMessage" in chrome.runtime, "chrome.runtime.sendNativeMessage does not exist without nativeMessaging permission");
-    browser.test.assertFalse("sendNativeMessage" in browser.runtime, "browser.runtime.sendNativeMessage does not exist without nativeMessaging permission");
-    browser.test.sendMessage("finished");
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {},
-  });
-
-  yield extension.startup();
-  yield extension.awaitMessage("finished");
-  yield extension.unload();
-});
-
-// Test that an extension that is not listed in allowed_extensions for
-// a native application cannot use that application.
-add_task(function* test_app_permission() {
-  function background() {
-    let port = browser.runtime.connectNative("echo");
-    port.onDisconnect.addListener(() => {
-      browser.test.sendMessage("result", "disconnected");
-    });
-    port.onMessage.addListener(msg => {
-      browser.test.sendMessage("result", "message");
-    });
-    port.postMessage({test: "test"});
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, "somethingelse@tests.mozilla.org");
-
-  yield extension.startup();
-
-  let result = yield extension.awaitMessage("result");
-  equal(result, "disconnected", "connectNative() failed without native app permission");
-
-  yield extension.unload();
-
-  let procCount = yield getSubprocessCount();
-  equal(procCount, 0, "No child process was started");
-});
-
-// Test that the command-line arguments and working directory for the
-// native application are as expected.
-add_task(function* test_child_process() {
-  function background() {
-    let port = browser.runtime.connectNative("info");
-    port.onMessage.addListener(msg => {
-      browser.test.sendMessage("result", msg);
-    });
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: ["nativeMessaging"],
-    },
-  }, ID);
-
-  yield extension.startup();
-
-  let msg = yield extension.awaitMessage("result");
-  equal(msg.args.length, 2, "Received one command line argument");
-  equal(msg.args[1], getPath("info.json"), "Command line argument is the path to the native host manifest");
-  equal(msg.cwd.replace(/^\/private\//, ""), tmpDir.path,
-        "Working directory is the directory containing the native appliation");
-
-  let exitPromise = waitForSubprocessExit();
-  yield extension.unload();
-  yield exitPromise;
-});
 
 // Test that an unresponsive native application still gets killed eventually
 add_task(function* test_unresponsive_native_app() {
   // XXX expose GRACEFUL_SHUTDOWN_TIME as a pref and reduce it
   // just for this test?
 
   function background() {
     let port = browser.runtime.connectNative("wontdie");
@@ -495,37 +74,8 @@ add_task(function* test_unresponsive_nat
 
   let exitPromise = waitForSubprocessExit();
   yield extension.unload();
   yield exitPromise;
 
   procCount = yield getSubprocessCount();
   equal(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,
-      manifest: {
-        permissions: ["nativeMessaging"],
-      },
-    }, ID);
-
-    yield extension.startup();
-    yield extension.awaitMessage("finished");
-    yield extension.unload();
-
-    yield waitForSubprocessExit();
-  });
-
-  let lines = STDERR_LINES.map(line => messages.findIndex(msg => msg.message.includes(line)));
-  notEqual(lines[0], -1, "Saw first line of stderr output on the console");
-  notEqual(lines[1], -1, "Saw second line of stderr output on the console");
-  notEqual(lines[0], lines[1], "Stderr output lines are separated in the console");
-});