/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const { Preferences } = ChromeUtils.import(
  "resource://gre/modules/Preferences.jsm"
);

// To fully test the Remote Agent's capabilities an instance of the interface
// also needs to be used.
const remoteAgentInstance = Cc["@mozilla.org/remote/agent;1"].createInstance(
  Ci.nsIRemoteAgent
);

const URL = "http://localhost:0";

// set up test conditions and clean up
function add_agent_task(originalTask) {
  const task = async function() {
    try {
      await RemoteAgent.close();
      await originalTask();
    } finally {
      Preferences.reset("remote.force-local");
    }
  };
  Object.defineProperty(task, "name", {
    value: originalTask.name,
    writable: false,
  });
  add_plain_task(task);
}

add_agent_task(async function debuggerAddress() {
  const port = getNonAtomicFreePort();

  await RemoteAgent.listen(`http://localhost:${port}`);
  is(
    remoteAgentInstance.debuggerAddress,
    `localhost:${port}`,
    "debuggerAddress set"
  );
});

add_agent_task(async function listening() {
  is(remoteAgentInstance.listening, false, "Agent is not listening");
  await RemoteAgent.listen(URL);
  is(remoteAgentInstance.listening, true, "Agent is listening");
});

add_agent_task(async function remoteListeningNotification() {
  let active;
  const port = getNonAtomicFreePort();

  function observer(subject, topic, data) {
    Services.obs.removeObserver(observer, topic);

    active = data;
  }

  Services.obs.addObserver(observer, "remote-listening");
  await RemoteAgent.listen("http://localhost:" + port);
  is(active, "true", "remote-listening observer notified enabled state");

  Services.obs.addObserver(observer, "remote-listening");
  await RemoteAgent.close();
  is(active, null, "remote-listening observer notified disabled state");
});

// TODO(ato): https://bugzil.la/1590829
add_agent_task(async function listenTakesString() {
  await RemoteAgent.listen("http://localhost:0");
  await RemoteAgent.close();
});

// TODO(ato): https://bugzil.la/1590829
add_agent_task(async function listenNonURL() {
  try {
    await RemoteAgent.listen("foobar");
    fail("listen() did not reject non-URL");
  } catch (e) {
    is(e.result, Cr.NS_ERROR_MALFORMED_URI);
  }
});

add_agent_task(async function listenRestrictedToLoopbackDevice() {
  try {
    await RemoteAgent.listen("http://0.0.0.0:0");
    fail("listen() did not reject non-loopback device");
  } catch (e) {
    is(e.result, Cr.NS_ERROR_ILLEGAL_VALUE);
    is(e.message, "Restricted to loopback devices");
  }
});

add_agent_task(async function listenNonLoopbackDevice() {
  Preferences.set("remote.force-local", false);
  await RemoteAgent.listen("http://0.0.0.0:0");
});

add_agent_task(async function test_close() {
  await RemoteAgent.listen(URL);
  await RemoteAgent.close();
  // no-op when not listening
  await RemoteAgent.close();
});

function getNonAtomicFreePort() {
  const so = Cc["@mozilla.org/network/server-socket;1"].createInstance(
    Ci.nsIServerSocket
  );
  try {
    so.init(-1, true /* aLoopbackOnly */, -1 /* aBackLog */);
    return so.port;
  } finally {
    so.close();
  }
}
