226 lines
6.4 KiB
JavaScript
226 lines
6.4 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
/* Exercise prefix-based forwarding of packets to other transports. */
|
|
|
|
const { RootActor } = require("resource://devtools/server/actors/root.js");
|
|
|
|
var gMainConnection, gMainTransport;
|
|
var gSubconnection1, gSubconnection2;
|
|
var gClient;
|
|
|
|
function run_test() {
|
|
DevToolsServer.init();
|
|
|
|
add_test(createMainConnection);
|
|
add_test(TestNoForwardingYet);
|
|
add_test(createSubconnection1);
|
|
add_test(TestForwardPrefix1OnlyRoot);
|
|
add_test(createSubconnection2);
|
|
add_test(TestForwardPrefix12OnlyRoot);
|
|
add_test(TestForwardPrefix12WithActor1);
|
|
add_test(TestForwardPrefix12WithActor12);
|
|
run_next_test();
|
|
}
|
|
|
|
/*
|
|
* Create a pipe connection, and return an object |{ conn, transport }|,
|
|
* where |conn| is the new DevToolsServerConnection instance, and
|
|
* |transport| is the client side of the transport on which it communicates
|
|
* (that is, packets sent on |transport| go to the new connection, and
|
|
* |transport|'s hooks receive replies).
|
|
*
|
|
* |prefix| is optional; if present, it's the prefix (minus the '/') for
|
|
* actors in the new connection.
|
|
*/
|
|
function newConnection(prefix) {
|
|
let conn;
|
|
DevToolsServer.createRootActor = function (connection) {
|
|
conn = connection;
|
|
return new RootActor(connection, {});
|
|
};
|
|
|
|
const transport = DevToolsServer.connectPipe(prefix);
|
|
|
|
return { conn, transport };
|
|
}
|
|
|
|
/* Create the main connection for these tests. */
|
|
function createMainConnection() {
|
|
({ conn: gMainConnection, transport: gMainTransport } = newConnection());
|
|
gClient = new DevToolsClient(gMainTransport);
|
|
gClient.connect().then(() => run_next_test());
|
|
}
|
|
|
|
/*
|
|
* Exchange 'echo' messages with five actors:
|
|
* - root
|
|
* - prefix1/root
|
|
* - prefix1/actor
|
|
* - prefix2/root
|
|
* - prefix2/actor
|
|
*
|
|
* Expect proper echos from those named in |reachables|, and 'noSuchActor'
|
|
* errors from the others. When we've gotten all our replies (errors or
|
|
* otherwise), call |completed|.
|
|
*
|
|
* To avoid deep stacks, we call completed from the next tick.
|
|
*/
|
|
async function tryActors(reachables, completed) {
|
|
for (const actor of [
|
|
"root",
|
|
"prefix1/root",
|
|
"prefix1/actor",
|
|
"prefix2/root",
|
|
"prefix2/actor",
|
|
]) {
|
|
let response;
|
|
try {
|
|
if (actor.endsWith("root")) {
|
|
// Root actor doesn't expose any echo method,
|
|
// so fallback on getRoot which returns `{ from: "root" }`.
|
|
// For the top level root actor, we have to use its front.
|
|
if (actor == "root") {
|
|
response = await gClient.mainRoot.getRoot();
|
|
} else {
|
|
response = await gClient.request({ to: actor, type: "getRoot" });
|
|
}
|
|
} else {
|
|
response = await gClient.request({
|
|
to: actor,
|
|
type: "echo",
|
|
value: "tango",
|
|
});
|
|
}
|
|
} catch (e) {
|
|
response = e;
|
|
}
|
|
if (reachables.has(actor)) {
|
|
if (actor.endsWith("root")) {
|
|
// RootActor's getRoot response is almost empty on xpcshell
|
|
Assert.deepEqual({ from: actor }, response);
|
|
} else {
|
|
Assert.deepEqual(
|
|
{ from: actor, to: actor, type: "echo", value: "tango" },
|
|
response
|
|
);
|
|
}
|
|
} else {
|
|
Assert.deepEqual(
|
|
{
|
|
from: actor,
|
|
error: "noSuchActor",
|
|
message: "No such actor for ID: " + actor,
|
|
},
|
|
response
|
|
);
|
|
}
|
|
}
|
|
executeSoon(completed, "tryActors callback " + completed.name);
|
|
}
|
|
|
|
/*
|
|
* With no forwarding established, sending messages to root should work,
|
|
* but sending messages to prefixed actor names, or anyone else, should get
|
|
* an error.
|
|
*/
|
|
function TestNoForwardingYet() {
|
|
tryActors(new Set(["root"]), run_next_test);
|
|
}
|
|
|
|
/*
|
|
* Create a new pipe connection which forwards its reply packets to
|
|
* gMainConnection's client, and to which gMainConnection forwards packets
|
|
* directed to actors whose names begin with |prefix + '/'|, and.
|
|
*
|
|
* Return an object { conn, transport }, as for newConnection.
|
|
*/
|
|
function newSubconnection(prefix) {
|
|
const { conn, transport } = newConnection(prefix);
|
|
transport.hooks = {
|
|
onPacket: packet => gMainConnection.send(packet),
|
|
};
|
|
gMainConnection.setForwarding(prefix, transport);
|
|
|
|
return { conn, transport };
|
|
}
|
|
|
|
/* Create a second root actor, to which we can forward things. */
|
|
function createSubconnection1() {
|
|
const { conn, transport } = newSubconnection("prefix1");
|
|
gSubconnection1 = conn;
|
|
transport.ready();
|
|
gClient.expectReply("prefix1/root", () => run_next_test());
|
|
}
|
|
|
|
// Establish forwarding, but don't put any actors in that server.
|
|
function TestForwardPrefix1OnlyRoot() {
|
|
tryActors(new Set(["root", "prefix1/root"]), run_next_test);
|
|
}
|
|
|
|
/* Create a third root actor, to which we can forward things. */
|
|
function createSubconnection2() {
|
|
const { conn, transport } = newSubconnection("prefix2");
|
|
gSubconnection2 = conn;
|
|
transport.ready();
|
|
gClient.expectReply("prefix2/root", () => run_next_test());
|
|
}
|
|
|
|
function TestForwardPrefix12OnlyRoot() {
|
|
tryActors(new Set(["root", "prefix1/root", "prefix2/root"]), run_next_test);
|
|
}
|
|
|
|
// A dumb actor that implements 'echo'.
|
|
//
|
|
// It's okay that both subconnections' actors behave identically, because
|
|
// the reply-sending code attaches the replying actor's name to the packet,
|
|
// so simply matching the 'from' field in the reply ensures that we heard
|
|
// from the right actor.
|
|
const { Actor } = require("resource://devtools/shared/protocol/Actor.js");
|
|
class EchoActor extends Actor {
|
|
constructor(conn) {
|
|
super(conn, { typeName: "EchoActor", methods: [] });
|
|
|
|
this.requestTypes = {
|
|
echo: EchoActor.prototype.onEcho,
|
|
};
|
|
}
|
|
|
|
onEcho(request) {
|
|
/*
|
|
* Request packets are frozen. Copy request, so that
|
|
* DevToolsServerConnection.onPacket can attach a 'from' property.
|
|
*/
|
|
return JSON.parse(JSON.stringify(request));
|
|
}
|
|
}
|
|
|
|
function TestForwardPrefix12WithActor1() {
|
|
const actor = new EchoActor(gSubconnection1);
|
|
actor.actorID = "prefix1/actor";
|
|
gSubconnection1.addActor(actor);
|
|
|
|
tryActors(
|
|
new Set(["root", "prefix1/root", "prefix1/actor", "prefix2/root"]),
|
|
run_next_test
|
|
);
|
|
}
|
|
|
|
function TestForwardPrefix12WithActor12() {
|
|
const actor = new EchoActor(gSubconnection2);
|
|
actor.actorID = "prefix2/actor";
|
|
gSubconnection2.addActor(actor);
|
|
|
|
tryActors(
|
|
new Set([
|
|
"root",
|
|
"prefix1/root",
|
|
"prefix1/actor",
|
|
"prefix2/root",
|
|
"prefix2/actor",
|
|
]),
|
|
run_next_test
|
|
);
|
|
}
|