Source code

Revision control

Copy as Markdown

Other Tools

Test Info:

/* Any copyright is dedicated to the Public Domain.
/* eslint-disable mozilla/no-arbitrary-setTimeout */
"use strict";
const { require } = ChromeUtils.importESModule(
"resource://devtools/shared/loader/Loader.sys.mjs"
);
const EventEmitter = require("resource://devtools/shared/event-emitter.js");
const discovery = require("resource://devtools/shared/discovery/discovery.js");
const { setTimeout, clearTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
Services.prefs.setBoolPref("devtools.discovery.log", true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.discovery.log");
});
function log(msg) {
info("DISCOVERY: " + msg);
}
// Global map of actively listening ports to TestTransport instances
var gTestTransports = {};
/**
* Implements the same API as Transport in discovery.js. Here, no UDP sockets
* are used. Instead, messages are delivered immediately.
*/
function TestTransport(port) {
EventEmitter.decorate(this);
this.port = port;
gTestTransports[this.port] = this;
}
TestTransport.prototype = {
send(object, port) {
log("Send to " + port + ":\n" + JSON.stringify(object, null, 2));
if (!gTestTransports[port]) {
log("No listener on port " + port);
return;
}
const message = JSON.stringify(object);
gTestTransports[port].onPacketReceived(null, message);
},
destroy() {
delete gTestTransports[this.port];
},
// nsIUDPSocketListener
onPacketReceived(socket, message) {
const object = JSON.parse(message);
object.from = "localhost";
log("Recv on " + this.port + ":\n" + JSON.stringify(object, null, 2));
this.emit("message", object);
},
onStopListening() {},
};
// Use TestTransport instead of the usual Transport
discovery._factories.Transport = TestTransport;
// Ignore name generation on b2g and force a fixed value
Object.defineProperty(discovery.device, "name", {
get() {
return "test-device";
},
});
add_task(async function () {
// At startup, no remote devices are known
deepEqual(discovery.getRemoteDevicesWithService("devtools"), []);
deepEqual(discovery.getRemoteDevicesWithService("penguins"), []);
discovery.scan();
// No services added yet, still empty
deepEqual(discovery.getRemoteDevicesWithService("devtools"), []);
deepEqual(discovery.getRemoteDevicesWithService("penguins"), []);
discovery.addService("devtools", { port: 1234 });
// Changes not visible until next scan
deepEqual(discovery.getRemoteDevicesWithService("devtools"), []);
deepEqual(discovery.getRemoteDevicesWithService("penguins"), []);
await scanForChange("devtools", "added");
// Now we see the new service
deepEqual(discovery.getRemoteDevicesWithService("devtools"), ["test-device"]);
deepEqual(discovery.getRemoteDevicesWithService("penguins"), []);
discovery.addService("penguins", { tux: true });
await scanForChange("penguins", "added");
deepEqual(discovery.getRemoteDevicesWithService("devtools"), ["test-device"]);
deepEqual(discovery.getRemoteDevicesWithService("penguins"), ["test-device"]);
deepEqual(discovery.getRemoteDevices(), ["test-device"]);
deepEqual(discovery.getRemoteService("devtools", "test-device"), {
port: 1234,
host: "localhost",
});
deepEqual(discovery.getRemoteService("penguins", "test-device"), {
tux: true,
host: "localhost",
});
discovery.removeService("devtools");
await scanForChange("devtools", "removed");
discovery.addService("penguins", { tux: false });
await scanForChange("penguins", "updated");
// Scan again, but nothing should be removed
await scanForNoChange("penguins", "removed");
// Split the scanning side from the service side to simulate the machine with
// the service becoming unreachable
gTestTransports = {};
discovery.removeService("penguins");
await scanForChange("penguins", "removed");
});
function scanForChange(service, changeType) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new Error("Reply never arrived"));
}, discovery.replyTimeout + 500);
discovery.on(service + "-device-" + changeType, function onChange() {
discovery.off(service + "-device-" + changeType, onChange);
clearTimeout(timer);
resolve();
});
discovery.scan();
});
}
function scanForNoChange(service, changeType) {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
resolve();
}, discovery.replyTimeout + 500);
discovery.on(service + "-device-" + changeType, function onChange() {
discovery.off(service + "-device-" + changeType, onChange);
clearTimeout(timer);
reject(new Error("Unexpected change occurred"));
});
discovery.scan();
});
}