зеркало из https://github.com/mozilla/gecko-dev.git
672 строки
18 KiB
JavaScript
672 строки
18 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
/* eslint-disable max-nested-callbacks */
|
|
|
|
"use strict";
|
|
|
|
/**
|
|
* Test simple requests using the protocol helpers.
|
|
*/
|
|
var protocol = require("devtools/shared/protocol");
|
|
var { types, Arg, RetVal } = protocol;
|
|
|
|
var EventEmitter = require("devtools/shared/event-emitter");
|
|
|
|
function simpleHello() {
|
|
return {
|
|
from: "root",
|
|
applicationType: "xpcshell-tests",
|
|
traits: [],
|
|
};
|
|
}
|
|
|
|
// Predeclaring the actor type so that it can be used in the
|
|
// implementation of the child actor.
|
|
types.addActorType("childActor");
|
|
|
|
const childSpec = protocol.generateActorSpec({
|
|
typeName: "childActor",
|
|
|
|
events: {
|
|
event1: {
|
|
a: Arg(0),
|
|
b: Arg(1),
|
|
c: Arg(2),
|
|
},
|
|
event2: {
|
|
a: Arg(0),
|
|
b: Arg(1),
|
|
c: Arg(2),
|
|
},
|
|
"named-event": {
|
|
type: "namedEvent",
|
|
a: Arg(0),
|
|
b: Arg(1),
|
|
c: Arg(2),
|
|
},
|
|
"object-event": {
|
|
type: "objectEvent",
|
|
detail: Arg(0, "childActor#actorid"),
|
|
},
|
|
"array-object-event": {
|
|
type: "arrayObjectEvent",
|
|
detail: Arg(0, "array:childActor#actorid"),
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
echo: {
|
|
request: { str: Arg(0) },
|
|
response: { str: RetVal("string") },
|
|
},
|
|
getDetail1: {
|
|
response: {
|
|
child: RetVal("childActor#actorid"),
|
|
},
|
|
},
|
|
getDetail2: {
|
|
response: {
|
|
child: RetVal("childActor#actorid"),
|
|
},
|
|
},
|
|
getIDDetail: {
|
|
response: {
|
|
idDetail: RetVal("childActor#actorid"),
|
|
},
|
|
},
|
|
getIntArray: {
|
|
request: { inputArray: Arg(0, "array:number") },
|
|
response: RetVal("array:number"),
|
|
},
|
|
getSibling: {
|
|
request: { id: Arg(0) },
|
|
response: { sibling: RetVal("childActor") },
|
|
},
|
|
emitEvents: {
|
|
response: { value: RetVal("string") },
|
|
},
|
|
release: {
|
|
release: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
var ChildActor = protocol.ActorClassWithSpec(childSpec, {
|
|
// Actors returned by this actor should be owned by the root actor.
|
|
marshallPool: function() {
|
|
return this.parent();
|
|
},
|
|
|
|
toString: function() {
|
|
return "[ChildActor " + this.childID + "]";
|
|
},
|
|
|
|
initialize: function(conn, id) {
|
|
protocol.Actor.prototype.initialize.call(this, conn);
|
|
this.childID = id;
|
|
},
|
|
|
|
destroy: function() {
|
|
protocol.Actor.prototype.destroy.call(this);
|
|
this.destroyed = true;
|
|
},
|
|
|
|
form: function() {
|
|
return {
|
|
actor: this.actorID,
|
|
childID: this.childID,
|
|
};
|
|
},
|
|
|
|
echo: function(str) {
|
|
return str;
|
|
},
|
|
|
|
getDetail1: function() {
|
|
return this;
|
|
},
|
|
|
|
getDetail2: function() {
|
|
return this;
|
|
},
|
|
|
|
getIDDetail: function() {
|
|
return this;
|
|
},
|
|
|
|
getIntArray: function(inputArray) {
|
|
// Test that protocol.js converts an iterator to an array.
|
|
const f = function*() {
|
|
for (const i of inputArray) {
|
|
yield 2 * i;
|
|
}
|
|
};
|
|
return f();
|
|
},
|
|
|
|
getSibling: function(id) {
|
|
return this.parent().getChild(id);
|
|
},
|
|
|
|
emitEvents: function() {
|
|
EventEmitter.emit(this, "event1", 1, 2, 3);
|
|
EventEmitter.emit(this, "event2", 4, 5, 6);
|
|
EventEmitter.emit(this, "named-event", 1, 2, 3);
|
|
EventEmitter.emit(this, "object-event", this);
|
|
EventEmitter.emit(this, "array-object-event", [this]);
|
|
return "correct response";
|
|
},
|
|
|
|
release: function() {},
|
|
});
|
|
|
|
class ChildFront extends protocol.FrontClassWithSpec(childSpec) {
|
|
constructor(client) {
|
|
super(client);
|
|
|
|
this.before("event1", this.onEvent1.bind(this));
|
|
this.before("event2", this.onEvent2a.bind(this));
|
|
this.on("event2", this.onEvent2b.bind(this));
|
|
}
|
|
|
|
destroy() {
|
|
this.destroyed = true;
|
|
super.destroy();
|
|
}
|
|
|
|
marshallPool() {
|
|
return this.parent();
|
|
}
|
|
|
|
toString() {
|
|
return "[child front " + this.childID + "]";
|
|
}
|
|
|
|
form(form) {
|
|
this.childID = form.childID;
|
|
}
|
|
|
|
onEvent1(a, b, c) {
|
|
this.event1arg3 = c;
|
|
}
|
|
|
|
onEvent2a(a, b, c) {
|
|
return Promise.resolve().then(() => {
|
|
this.event2arg3 = c;
|
|
});
|
|
}
|
|
|
|
onEvent2b(a, b, c) {
|
|
this.event2arg2 = b;
|
|
}
|
|
}
|
|
protocol.registerFront(ChildFront);
|
|
|
|
types.addDictType("manyChildrenDict", {
|
|
child5: "childActor",
|
|
more: "array:childActor",
|
|
});
|
|
|
|
types.addLifetime("temp", "_temporaryHolder");
|
|
|
|
const rootSpec = protocol.generateActorSpec({
|
|
typeName: "root",
|
|
|
|
methods: {
|
|
getChild: {
|
|
request: { str: Arg(0) },
|
|
response: { actor: RetVal("childActor") },
|
|
},
|
|
getChildren: {
|
|
request: { ids: Arg(0, "array:string") },
|
|
response: { children: RetVal("array:childActor") },
|
|
},
|
|
getChildren2: {
|
|
request: { ids: Arg(0, "array:childActor") },
|
|
response: { children: RetVal("array:childActor") },
|
|
},
|
|
getManyChildren: {
|
|
response: RetVal("manyChildrenDict"),
|
|
},
|
|
getTemporaryChild: {
|
|
request: { id: Arg(0) },
|
|
response: { child: RetVal("temp:childActor") },
|
|
},
|
|
clearTemporaryChildren: {},
|
|
},
|
|
});
|
|
|
|
var rootActor = null;
|
|
var RootActor = protocol.ActorClassWithSpec(rootSpec, {
|
|
toString: function() {
|
|
return "[root actor]";
|
|
},
|
|
|
|
initialize: function(conn) {
|
|
rootActor = this;
|
|
this.actorID = "root";
|
|
this._children = {};
|
|
protocol.Actor.prototype.initialize.call(this, conn);
|
|
},
|
|
|
|
sayHello: simpleHello,
|
|
|
|
getChild: function(id) {
|
|
if (id in this._children) {
|
|
return this._children[id];
|
|
}
|
|
const child = new ChildActor(this.conn, id);
|
|
this._children[id] = child;
|
|
return child;
|
|
},
|
|
|
|
getChildren: function(ids) {
|
|
return ids.map(id => this.getChild(id));
|
|
},
|
|
|
|
getChildren2: function(ids) {
|
|
const f = function*() {
|
|
for (const c of ids) {
|
|
yield c;
|
|
}
|
|
};
|
|
return f();
|
|
},
|
|
|
|
getManyChildren: function() {
|
|
return {
|
|
// note that this isn't in the specialization array.
|
|
foo: "bar",
|
|
child5: this.getChild("child5"),
|
|
more: [this.getChild("child6"), this.getChild("child7")],
|
|
};
|
|
},
|
|
|
|
// This should remind you of a pause actor.
|
|
getTemporaryChild: function(id) {
|
|
if (!this._temporaryHolder) {
|
|
this._temporaryHolder = new protocol.Actor(this.conn);
|
|
this.manage(this._temporaryHolder);
|
|
}
|
|
return new ChildActor(this.conn, id);
|
|
},
|
|
|
|
clearTemporaryChildren: function(id) {
|
|
if (!this._temporaryHolder) {
|
|
return;
|
|
}
|
|
this._temporaryHolder.destroy();
|
|
delete this._temporaryHolder;
|
|
},
|
|
});
|
|
|
|
class RootFront extends protocol.FrontClassWithSpec(rootSpec) {
|
|
constructor(client) {
|
|
super(client);
|
|
this.actorID = "root";
|
|
// Root actor owns itself.
|
|
this.manage(this);
|
|
}
|
|
|
|
toString() {
|
|
return "[root front]";
|
|
}
|
|
|
|
getTemporaryChild(id) {
|
|
if (!this._temporaryHolder) {
|
|
this._temporaryHolder = new protocol.Front(this.conn);
|
|
this._temporaryHolder.actorID = this.actorID + "_temp";
|
|
this.manage(this._temporaryHolder);
|
|
}
|
|
return super.getTemporaryChild(id);
|
|
}
|
|
|
|
clearTemporaryChildren() {
|
|
if (!this._temporaryHolder) {
|
|
return Promise.resolve(undefined);
|
|
}
|
|
this._temporaryHolder.destroy();
|
|
delete this._temporaryHolder;
|
|
return super.clearTemporaryChildren();
|
|
}
|
|
}
|
|
|
|
function run_test() {
|
|
DebuggerServer.createRootActor = conn => {
|
|
return RootActor(conn);
|
|
};
|
|
DebuggerServer.init();
|
|
|
|
const trace = connectPipeTracing();
|
|
const client = new DebuggerClient(trace);
|
|
client.connect().then(([applicationType, traits]) => {
|
|
trace.expectReceive({
|
|
from: "<actorid>",
|
|
applicationType: "xpcshell-tests",
|
|
traits: [],
|
|
});
|
|
Assert.equal(applicationType, "xpcshell-tests");
|
|
|
|
const rootFront = new RootFront(client);
|
|
let childFront = null;
|
|
|
|
const expectRootChildren = size => {
|
|
Assert.equal(rootActor._poolMap.size, size);
|
|
Assert.equal(rootFront._poolMap.size, size + 1);
|
|
if (childFront) {
|
|
Assert.equal(childFront._poolMap.size, 0);
|
|
}
|
|
};
|
|
|
|
rootFront
|
|
.getChild("child1")
|
|
.then(ret => {
|
|
trace.expectSend({ type: "getChild", str: "child1", to: "<actorid>" });
|
|
trace.expectReceive({ actor: "<actorid>", from: "<actorid>" });
|
|
|
|
childFront = ret;
|
|
Assert.ok(childFront instanceof ChildFront);
|
|
Assert.equal(childFront.childID, "child1");
|
|
expectRootChildren(1);
|
|
})
|
|
.then(() => {
|
|
// Request the child again, make sure the same is returned.
|
|
return rootFront.getChild("child1");
|
|
})
|
|
.then(ret => {
|
|
trace.expectSend({ type: "getChild", str: "child1", to: "<actorid>" });
|
|
trace.expectReceive({ actor: "<actorid>", from: "<actorid>" });
|
|
|
|
expectRootChildren(1);
|
|
Assert.ok(ret === childFront);
|
|
})
|
|
.then(() => {
|
|
return childFront.echo("hello");
|
|
})
|
|
.then(ret => {
|
|
trace.expectSend({ type: "echo", str: "hello", to: "<actorid>" });
|
|
trace.expectReceive({ str: "hello", from: "<actorid>" });
|
|
|
|
Assert.equal(ret, "hello");
|
|
})
|
|
.then(() => {
|
|
return childFront.getDetail1();
|
|
})
|
|
.then(ret => {
|
|
trace.expectSend({ type: "getDetail1", to: "<actorid>" });
|
|
trace.expectReceive({ child: childFront.actorID, from: "<actorid>" });
|
|
Assert.ok(ret === childFront);
|
|
})
|
|
.then(() => {
|
|
return childFront.getDetail2();
|
|
})
|
|
.then(ret => {
|
|
trace.expectSend({ type: "getDetail2", to: "<actorid>" });
|
|
trace.expectReceive({ child: childFront.actorID, from: "<actorid>" });
|
|
Assert.ok(ret === childFront);
|
|
})
|
|
.then(() => {
|
|
return childFront.getIDDetail();
|
|
})
|
|
.then(ret => {
|
|
trace.expectSend({ type: "getIDDetail", to: "<actorid>" });
|
|
trace.expectReceive({
|
|
idDetail: childFront.actorID,
|
|
from: "<actorid>",
|
|
});
|
|
Assert.ok(ret === childFront);
|
|
})
|
|
.then(() => {
|
|
return childFront.getSibling("siblingID");
|
|
})
|
|
.then(ret => {
|
|
trace.expectSend({
|
|
type: "getSibling",
|
|
id: "siblingID",
|
|
to: "<actorid>",
|
|
});
|
|
trace.expectReceive({
|
|
sibling: { actor: "<actorid>", childID: "siblingID" },
|
|
from: "<actorid>",
|
|
});
|
|
|
|
expectRootChildren(2);
|
|
})
|
|
.then(ret => {
|
|
return rootFront.getTemporaryChild("temp1").then(temp1 => {
|
|
trace.expectSend({
|
|
type: "getTemporaryChild",
|
|
id: "temp1",
|
|
to: "<actorid>",
|
|
});
|
|
trace.expectReceive({
|
|
child: { actor: "<actorid>", childID: "temp1" },
|
|
from: "<actorid>",
|
|
});
|
|
|
|
// At this point we expect two direct children, plus the temporary holder
|
|
// which should hold 1 itself.
|
|
Assert.equal(rootActor._temporaryHolder.__poolMap.size, 1);
|
|
Assert.equal(rootFront._temporaryHolder.__poolMap.size, 1);
|
|
|
|
expectRootChildren(3);
|
|
return rootFront.getTemporaryChild("temp2").then(temp2 => {
|
|
trace.expectSend({
|
|
type: "getTemporaryChild",
|
|
id: "temp2",
|
|
to: "<actorid>",
|
|
});
|
|
trace.expectReceive({
|
|
child: { actor: "<actorid>", childID: "temp2" },
|
|
from: "<actorid>",
|
|
});
|
|
|
|
// Same amount of direct children, and an extra in the temporary holder.
|
|
expectRootChildren(3);
|
|
Assert.equal(rootActor._temporaryHolder.__poolMap.size, 2);
|
|
Assert.equal(rootFront._temporaryHolder.__poolMap.size, 2);
|
|
|
|
// Get the children of the temporary holder...
|
|
const checkActors = rootActor._temporaryHolder.__poolMap.values();
|
|
|
|
// Now release the temporary holders and expect them to drop again.
|
|
return rootFront.clearTemporaryChildren().then(() => {
|
|
trace.expectSend({
|
|
type: "clearTemporaryChildren",
|
|
to: "<actorid>",
|
|
});
|
|
trace.expectReceive({ from: "<actorid>" });
|
|
|
|
expectRootChildren(2);
|
|
Assert.ok(!rootActor._temporaryHolder);
|
|
Assert.ok(!rootFront._temporaryHolder);
|
|
for (const checkActor of checkActors) {
|
|
Assert.ok(checkActor.destroyed);
|
|
Assert.ok(checkActor.destroyed);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
})
|
|
.then(ret => {
|
|
return rootFront.getChildren(["child1", "child2"]);
|
|
})
|
|
.then(ret => {
|
|
trace.expectSend({
|
|
type: "getChildren",
|
|
ids: ["child1", "child2"],
|
|
to: "<actorid>",
|
|
});
|
|
trace.expectReceive({
|
|
children: [
|
|
{ actor: "<actorid>", childID: "child1" },
|
|
{ actor: "<actorid>", childID: "child2" },
|
|
],
|
|
from: "<actorid>",
|
|
});
|
|
|
|
expectRootChildren(3);
|
|
Assert.ok(ret[0] === childFront);
|
|
Assert.ok(ret[1] !== childFront);
|
|
Assert.ok(ret[1] instanceof ChildFront);
|
|
|
|
// On both children, listen to events. We're only
|
|
// going to trigger events on the first child, so an event
|
|
// triggered on the second should cause immediate failures.
|
|
|
|
const set = new Set([
|
|
"event1",
|
|
"event2",
|
|
"named-event",
|
|
"object-event",
|
|
"array-object-event",
|
|
]);
|
|
|
|
childFront.on("event1", (a, b, c) => {
|
|
Assert.equal(a, 1);
|
|
Assert.equal(b, 2);
|
|
Assert.equal(c, 3);
|
|
// Verify that the pre-event handler was called.
|
|
Assert.equal(childFront.event1arg3, 3);
|
|
set.delete("event1");
|
|
});
|
|
childFront.on("event2", (a, b, c) => {
|
|
Assert.equal(a, 4);
|
|
Assert.equal(b, 5);
|
|
Assert.equal(c, 6);
|
|
// Verify that the async pre-event handler was called,
|
|
// setting the property before this handler was called.
|
|
Assert.equal(childFront.event2arg3, 6);
|
|
// And check that the sync preEvent with the same name is also
|
|
// executed
|
|
Assert.equal(childFront.event2arg2, 5);
|
|
set.delete("event2");
|
|
});
|
|
childFront.on("named-event", (a, b, c) => {
|
|
Assert.equal(a, 1);
|
|
Assert.equal(b, 2);
|
|
Assert.equal(c, 3);
|
|
set.delete("named-event");
|
|
});
|
|
childFront.on("object-event", obj => {
|
|
Assert.ok(obj === childFront);
|
|
set.delete("object-event");
|
|
});
|
|
childFront.on("array-object-event", array => {
|
|
Assert.ok(array[0] === childFront);
|
|
set.delete("array-object-event");
|
|
});
|
|
|
|
const fail = function() {
|
|
do_throw("Unexpected event");
|
|
};
|
|
ret[1].on("event1", fail);
|
|
ret[1].on("event2", fail);
|
|
ret[1].on("named-event", fail);
|
|
ret[1].on("object-event", fail);
|
|
ret[1].on("array-object-event", fail);
|
|
|
|
return childFront.emitEvents().then(() => {
|
|
trace.expectSend({ type: "emitEvents", to: "<actorid>" });
|
|
trace.expectReceive({
|
|
type: "event1",
|
|
a: 1,
|
|
b: 2,
|
|
c: 3,
|
|
from: "<actorid>",
|
|
});
|
|
trace.expectReceive({
|
|
type: "event2",
|
|
a: 4,
|
|
b: 5,
|
|
c: 6,
|
|
from: "<actorid>",
|
|
});
|
|
trace.expectReceive({
|
|
type: "namedEvent",
|
|
a: 1,
|
|
b: 2,
|
|
c: 3,
|
|
from: "<actorid>",
|
|
});
|
|
trace.expectReceive({
|
|
type: "objectEvent",
|
|
detail: childFront.actorID,
|
|
from: "<actorid>",
|
|
});
|
|
trace.expectReceive({
|
|
type: "arrayObjectEvent",
|
|
detail: [childFront.actorID],
|
|
from: "<actorid>",
|
|
});
|
|
trace.expectReceive({ value: "correct response", from: "<actorid>" });
|
|
|
|
Assert.equal(set.size, 0);
|
|
});
|
|
})
|
|
.then(ret => {
|
|
return rootFront.getManyChildren();
|
|
})
|
|
.then(ret => {
|
|
trace.expectSend({ type: "getManyChildren", to: "<actorid>" });
|
|
trace.expectReceive({
|
|
foo: "bar",
|
|
child5: { actor: "<actorid>", childID: "child5" },
|
|
more: [
|
|
{ actor: "<actorid>", childID: "child6" },
|
|
{ actor: "<actorid>", childID: "child7" },
|
|
],
|
|
from: "<actorid>",
|
|
});
|
|
|
|
// Check all the crazy stuff we did in getManyChildren
|
|
Assert.equal(ret.foo, "bar");
|
|
Assert.equal(ret.child5.childID, "child5");
|
|
Assert.equal(ret.more[0].childID, "child6");
|
|
Assert.equal(ret.more[1].childID, "child7");
|
|
})
|
|
.then(() => {
|
|
// Test accepting a generator.
|
|
const f = function*() {
|
|
for (const i of [1, 2, 3, 4, 5]) {
|
|
yield i;
|
|
}
|
|
};
|
|
return childFront.getIntArray(f());
|
|
})
|
|
.then(ret => {
|
|
Assert.equal(ret.length, 5);
|
|
const expected = [2, 4, 6, 8, 10];
|
|
for (let i = 0; i < 5; ++i) {
|
|
Assert.equal(ret[i], expected[i]);
|
|
}
|
|
})
|
|
.then(() => {
|
|
return rootFront.getChildren(["child1", "child2"]);
|
|
})
|
|
.then(ids => {
|
|
const f = function*() {
|
|
for (const id of ids) {
|
|
yield id;
|
|
}
|
|
};
|
|
return rootFront.getChildren2(f());
|
|
})
|
|
.then(ret => {
|
|
Assert.equal(ret.length, 2);
|
|
Assert.ok(ret[0] === childFront);
|
|
Assert.ok(ret[1] !== childFront);
|
|
Assert.ok(ret[1] instanceof ChildFront);
|
|
})
|
|
.then(() => {
|
|
client.close().then(() => {
|
|
do_test_finished();
|
|
});
|
|
})
|
|
.catch(err => {
|
|
do_report_unexpected_exception(err, "Failure executing test");
|
|
});
|
|
});
|
|
do_test_pending();
|
|
}
|