123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560 |
- /* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
- /**
- * Test simple requests using the protocol helpers.
- */
- var protocol = require("devtools/shared/protocol");
- var {preEvent, types, Arg, Option, RetVal} = protocol;
- var events = require("sdk/event/core");
- function simpleHello() {
- return {
- from: "root",
- applicationType: "xpcshell-tests",
- traits: [],
- };
- }
- var testTypes = {};
- // 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#detail1"),
- },
- "array-object-event": {
- type: "arrayObjectEvent",
- detail: Arg(0, "array:childActor#detail2"),
- }
- },
- methods: {
- echo: {
- request: { str: Arg(0) },
- response: { str: RetVal("string") },
- },
- getDetail1: {
- // This also exercises return-value-as-packet.
- response: RetVal("childActor#detail1"),
- },
- getDetail2: {
- // This also exercises return-value-as-packet.
- response: RetVal("childActor#detail2"),
- },
- 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: "correct response" },
- },
- 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 (detail) {
- if (detail === "actorid") {
- return this.actorID;
- }
- return {
- actor: this.actorID,
- childID: this.childID,
- detail: detail
- };
- },
- 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.
- let f = function* () {
- for (let i of inputArray) {
- yield 2 * i;
- }
- };
- return f();
- },
- getSibling: function (id) {
- return this.parent().getChild(id);
- },
- emitEvents: function () {
- events.emit(this, "event1", 1, 2, 3);
- events.emit(this, "event2", 4, 5, 6);
- events.emit(this, "named-event", 1, 2, 3);
- events.emit(this, "object-event", this);
- events.emit(this, "array-object-event", [this]);
- },
- release: function () { },
- });
- var ChildFront = protocol.FrontClassWithSpec(childSpec, {
- initialize: function (client, form) {
- protocol.Front.prototype.initialize.call(this, client, form);
- },
- destroy: function () {
- this.destroyed = true;
- protocol.Front.prototype.destroy.call(this);
- },
- marshallPool: function () { return this.parent(); },
- toString: function () { return "[child front " + this.childID + "]"; },
- form: function (form, detail) {
- if (detail === "actorid") {
- return;
- }
- this.childID = form.childID;
- this.detail = form.detail;
- },
- onEvent1: preEvent("event1", function (a, b, c) {
- this.event1arg3 = c;
- }),
- onEvent2a: preEvent("event2", function (a, b, c) {
- return promise.resolve().then(() => this.event2arg3 = c);
- }),
- onEvent2b: preEvent("event2", function (a, b, c) {
- this.event2arg2 = b;
- }),
- });
- 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);
- // Root actor owns itself.
- this.manage(this);
- },
- sayHello: simpleHello,
- getChild: function (id) {
- if (id in this._children) {
- return this._children[id];
- }
- let 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) {
- let f = function* () {
- for (let c of ids) {
- yield c;
- }
- };
- return f();
- },
- getManyChildren: function () {
- return {
- foo: "bar", // note that this isn't in the specialization array.
- 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 = this.manage(new protocol.Actor(this.conn));
- }
- return new ChildActor(this.conn, id);
- },
- clearTemporaryChildren: function (id) {
- if (!this._temporaryHolder) {
- return;
- }
- this._temporaryHolder.destroy();
- delete this._temporaryHolder;
- }
- });
- var RootFront = protocol.FrontClassWithSpec(rootSpec, {
- toString: function () { return "[root front]"; },
- initialize: function (client) {
- this.actorID = "root";
- protocol.Front.prototype.initialize.call(this, client);
- // Root actor owns itself.
- this.manage(this);
- },
- getTemporaryChild: protocol.custom(function (id) {
- if (!this._temporaryHolder) {
- this._temporaryHolder = protocol.Front(this.conn);
- this._temporaryHolder.actorID = this.actorID + "_temp";
- this._temporaryHolder = this.manage(this._temporaryHolder);
- }
- return this._getTemporaryChild(id);
- }, {
- impl: "_getTemporaryChild"
- }),
- clearTemporaryChildren: protocol.custom(function () {
- if (!this._temporaryHolder) {
- return promise.resolve(undefined);
- }
- this._temporaryHolder.destroy();
- delete this._temporaryHolder;
- return this._clearTemporaryChildren();
- }, {
- impl: "_clearTemporaryChildren"
- })
- });
- function run_test()
- {
- DebuggerServer.createRootActor = (conn => {
- return RootActor(conn);
- });
- DebuggerServer.init();
- let trace = connectPipeTracing();
- let client = new DebuggerClient(trace);
- client.connect().then(([applicationType, traits]) => {
- trace.expectReceive({"from":"<actorid>", "applicationType":"xpcshell-tests", "traits":[]});
- do_check_eq(applicationType, "xpcshell-tests");
- let rootFront = RootFront(client);
- let childFront = null;
- let expectRootChildren = size => {
- do_check_eq(rootActor._poolMap.size, size + 1);
- do_check_eq(rootFront._poolMap.size, size + 1);
- if (childFront) {
- do_check_eq(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;
- do_check_true(childFront instanceof ChildFront);
- do_check_eq(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);
- do_check_true(ret === childFront);
- }).then(() => {
- return childFront.echo("hello");
- }).then(ret => {
- trace.expectSend({"type":"echo", "str":"hello", "to":"<actorid>"});
- trace.expectReceive({"str":"hello", "from":"<actorid>"});
- do_check_eq(ret, "hello");
- }).then(() => {
- return childFront.getDetail1();
- }).then(ret => {
- trace.expectSend({"type":"getDetail1", "to":"<actorid>"});
- trace.expectReceive({"actor":"<actorid>", "childID":"child1", "detail":"detail1", "from":"<actorid>"});
- do_check_true(ret === childFront);
- do_check_eq(childFront.detail, "detail1");
- }).then(() => {
- return childFront.getDetail2();
- }).then(ret => {
- trace.expectSend({"type":"getDetail2", "to":"<actorid>"});
- trace.expectReceive({"actor":"<actorid>", "childID":"child1", "detail":"detail2", "from":"<actorid>"});
- do_check_true(ret === childFront);
- do_check_eq(childFront.detail, "detail2");
- }).then(() => {
- return childFront.getIDDetail();
- }).then(ret => {
- trace.expectSend({"type":"getIDDetail", "to":"<actorid>"});
- trace.expectReceive({"idDetail": childFront.actorID, "from":"<actorid>"});
- do_check_true(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.
- do_check_eq(rootActor._temporaryHolder.__poolMap.size, 1);
- do_check_eq(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);
- do_check_eq(rootActor._temporaryHolder.__poolMap.size, 2);
- do_check_eq(rootFront._temporaryHolder.__poolMap.size, 2);
- // Get the children of the temporary holder...
- let checkActors = rootActor._temporaryHolder.__poolMap.values();
- let checkFronts = rootFront._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);
- do_check_false(!!rootActor._temporaryHolder);
- do_check_false(!!rootFront._temporaryHolder);
- for (let checkActor of checkActors) {
- do_check_true(checkActor.destroyed);
- do_check_true(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);
- do_check_true(ret[0] === childFront);
- do_check_true(ret[1] !== childFront);
- do_check_true(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.
- let set = new Set(["event1", "event2", "named-event", "object-event", "array-object-event"]);
- childFront.on("event1", (a, b, c) => {
- do_check_eq(a, 1);
- do_check_eq(b, 2);
- do_check_eq(c, 3);
- // Verify that the pre-event handler was called.
- do_check_eq(childFront.event1arg3, 3);
- set.delete("event1");
- });
- childFront.on("event2", (a, b, c) => {
- do_check_eq(a, 4);
- do_check_eq(b, 5);
- do_check_eq(c, 6);
- // Verify that the async pre-event handler was called,
- // setting the property before this handler was called.
- do_check_eq(childFront.event2arg3, 6);
- // And check that the sync preEvent with the same name is also
- // executed
- do_check_eq(childFront.event2arg2, 5);
- set.delete("event2");
- });
- childFront.on("named-event", (a, b, c) => {
- do_check_eq(a, 1);
- do_check_eq(b, 2);
- do_check_eq(c, 3);
- set.delete("named-event");
- });
- childFront.on("object-event", (obj) => {
- do_check_true(obj === childFront);
- do_check_eq(childFront.detail, "detail1");
- set.delete("object-event");
- });
- childFront.on("array-object-event", (array) => {
- do_check_true(array[0] === childFront);
- do_check_eq(childFront.detail, "detail2");
- set.delete("array-object-event");
- });
- let 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":{"actor":"<actorid>", "childID":"child1", "detail":"detail1"}, "from":"<actorid>"});
- trace.expectReceive({"type":"arrayObjectEvent", "detail":[{"actor":"<actorid>", "childID":"child1", "detail":"detail2"}], "from":"<actorid>"});
- trace.expectReceive({"value":"correct response", "from":"<actorid>"});
- do_check_eq(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
- do_check_eq(ret.foo, "bar");
- do_check_eq(ret.child5.childID, "child5");
- do_check_eq(ret.more[0].childID, "child6");
- do_check_eq(ret.more[1].childID, "child7");
- }).then(() => {
- // Test accepting a generator.
- let f = function* () {
- for (let i of [1, 2, 3, 4, 5]) {
- yield i;
- }
- };
- return childFront.getIntArray(f());
- }).then((ret) => {
- do_check_eq(ret.length, 5);
- let expected = [2, 4, 6, 8, 10];
- for (let i = 0; i < 5; ++i) {
- do_check_eq(ret[i], expected[i]);
- }
- }).then(() => {
- return rootFront.getChildren(["child1", "child2"]);
- }).then(ids => {
- let f = function* () {
- for (let id of ids) {
- yield id;
- }
- };
- return rootFront.getChildren2(f());
- }).then(ret => {
- do_check_eq(ret.length, 2);
- do_check_true(ret[0] === childFront);
- do_check_true(ret[1] !== childFront);
- do_check_true(ret[1] instanceof ChildFront);
- }).then(() => {
- client.close().then(() => {
- do_test_finished();
- });
- }).then(null, err => {
- do_report_unexpected_exception(err, "Failure executing test");
- });
- });
- do_test_pending();
- }
|