1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/devtools/server/tests/unit/test_protocol_children.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,451 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +/** 1.8 + * Test simple requests using the protocol helpers. 1.9 + */ 1.10 +let protocol = devtools.require("devtools/server/protocol"); 1.11 +let {method, preEvent, types, Arg, Option, RetVal} = protocol; 1.12 + 1.13 +let events = devtools.require("sdk/event/core"); 1.14 + 1.15 +function simpleHello() { 1.16 + return { 1.17 + from: "root", 1.18 + applicationType: "xpcshell-tests", 1.19 + traits: [], 1.20 + } 1.21 +} 1.22 + 1.23 +let testTypes = {}; 1.24 + 1.25 +// Predeclaring the actor type so that it can be used in the 1.26 +// implementation of the child actor. 1.27 +types.addActorType("childActor"); 1.28 + 1.29 +let ChildActor = protocol.ActorClass({ 1.30 + typeName: "childActor", 1.31 + 1.32 + // Actors returned by this actor should be owned by the root actor. 1.33 + marshallPool: function() { return this.parent() }, 1.34 + 1.35 + toString: function() "[ChildActor " + this.childID + "]", 1.36 + 1.37 + initialize: function(conn, id) { 1.38 + protocol.Actor.prototype.initialize.call(this, conn); 1.39 + this.childID = id; 1.40 + }, 1.41 + 1.42 + destroy: function() { 1.43 + protocol.Actor.prototype.destroy.call(this); 1.44 + this.destroyed = true; 1.45 + }, 1.46 + 1.47 + form: function(detail) { 1.48 + if (detail === "actorid") { 1.49 + return this.actorID; 1.50 + } 1.51 + return { 1.52 + actor: this.actorID, 1.53 + childID: this.childID, 1.54 + detail: detail 1.55 + }; 1.56 + }, 1.57 + 1.58 + echo: method(function(str) { 1.59 + return str; 1.60 + }, { 1.61 + request: { str: Arg(0) }, 1.62 + response: { str: RetVal("string") }, 1.63 + telemetry: "ECHO" 1.64 + }), 1.65 + 1.66 + getDetail1: method(function() { 1.67 + return this; 1.68 + }, { 1.69 + // This also exercises return-value-as-packet. 1.70 + response: RetVal("childActor#detail1"), 1.71 + }), 1.72 + 1.73 + getDetail2: method(function() { 1.74 + return this; 1.75 + }, { 1.76 + // This also exercises return-value-as-packet. 1.77 + response: RetVal("childActor#detail2"), 1.78 + }), 1.79 + 1.80 + getIDDetail: method(function() { 1.81 + return this; 1.82 + }, { 1.83 + response: { 1.84 + idDetail: RetVal("childActor#actorid") 1.85 + } 1.86 + }), 1.87 + 1.88 + getSibling: method(function(id) { 1.89 + return this.parent().getChild(id); 1.90 + }, { 1.91 + request: { id: Arg(0) }, 1.92 + response: { sibling: RetVal("childActor") } 1.93 + }), 1.94 + 1.95 + emitEvents: method(function() { 1.96 + events.emit(this, "event1", 1, 2, 3); 1.97 + events.emit(this, "named-event", 1, 2, 3); 1.98 + events.emit(this, "object-event", this); 1.99 + events.emit(this, "array-object-event", [this]); 1.100 + }, { 1.101 + response: { value: "correct response" }, 1.102 + }), 1.103 + 1.104 + release: method(function() { }, { release: true }), 1.105 + 1.106 + events: { 1.107 + "event1" : { 1.108 + a: Arg(0), 1.109 + b: Arg(1), 1.110 + c: Arg(2) 1.111 + }, 1.112 + "named-event": { 1.113 + type: "namedEvent", 1.114 + a: Arg(0), 1.115 + b: Arg(1), 1.116 + c: Arg(2) 1.117 + }, 1.118 + "object-event": { 1.119 + type: "objectEvent", 1.120 + detail: Arg(0, "childActor#detail1"), 1.121 + }, 1.122 + "array-object-event": { 1.123 + type: "arrayObjectEvent", 1.124 + detail: Arg(0, "array:childActor#detail2"), 1.125 + } 1.126 + } 1.127 +}); 1.128 + 1.129 +let ChildFront = protocol.FrontClass(ChildActor, { 1.130 + initialize: function(client, form) { 1.131 + protocol.Front.prototype.initialize.call(this, client, form); 1.132 + }, 1.133 + 1.134 + destroy: function() { 1.135 + this.destroyed = true; 1.136 + protocol.Front.prototype.destroy.call(this); 1.137 + }, 1.138 + 1.139 + marshallPool: function() { return this.parent() }, 1.140 + 1.141 + toString: function() "[child front " + this.childID + "]", 1.142 + 1.143 + form: function(form, detail) { 1.144 + if (detail === "actorid") { 1.145 + return; 1.146 + } 1.147 + this.childID = form.childID; 1.148 + this.detail = form.detail; 1.149 + }, 1.150 + 1.151 + onEvent1: preEvent("event1", function(a, b, c) { 1.152 + this.event1arg3 = c; 1.153 + }), 1.154 +}); 1.155 + 1.156 +types.addDictType("manyChildrenDict", { 1.157 + child5: "childActor", 1.158 + more: "array:childActor", 1.159 +}); 1.160 + 1.161 +types.addLifetime("temp", "_temporaryHolder"); 1.162 + 1.163 +let rootActor = null; 1.164 +let RootActor = protocol.ActorClass({ 1.165 + typeName: "root", 1.166 + 1.167 + toString: function() "[root actor]", 1.168 + 1.169 + initialize: function(conn) { 1.170 + rootActor = this; 1.171 + this.actorID = "root"; 1.172 + this._children = {}; 1.173 + protocol.Actor.prototype.initialize.call(this, conn); 1.174 + // Root actor owns itself. 1.175 + this.manage(this); 1.176 + }, 1.177 + 1.178 + sayHello: simpleHello, 1.179 + 1.180 + getChild: method(function(id) { 1.181 + if (id in this._children) { 1.182 + return this._children[id]; 1.183 + } 1.184 + let child = new ChildActor(this.conn, id); 1.185 + this._children[id] = child; 1.186 + return child; 1.187 + }, { 1.188 + request: { str: Arg(0) }, 1.189 + response: { actor: RetVal("childActor") }, 1.190 + }), 1.191 + 1.192 + getChildren: method(function(ids) { 1.193 + return [this.getChild(id) for (id of ids)]; 1.194 + }, { 1.195 + request: { ids: Arg(0, "array:string") }, 1.196 + response: { children: RetVal("array:childActor") }, 1.197 + }), 1.198 + 1.199 + getManyChildren: method(function() { 1.200 + return { 1.201 + foo: "bar", // note that this isn't in the specialization array. 1.202 + child5: this.getChild("child5"), 1.203 + more: [ this.getChild("child6"), this.getChild("child7") ] 1.204 + } 1.205 + }, { 1.206 + response: RetVal("manyChildrenDict") 1.207 + }), 1.208 + 1.209 + // This should remind you of a pause actor. 1.210 + getTemporaryChild: method(function(id) { 1.211 + if (!this._temporaryHolder) { 1.212 + this._temporaryHolder = this.manage(new protocol.Actor(this.conn)); 1.213 + } 1.214 + return new ChildActor(this.conn, id); 1.215 + }, { 1.216 + request: { id: Arg(0) }, 1.217 + response: { child: RetVal("temp:childActor") } 1.218 + }), 1.219 + 1.220 + clearTemporaryChildren: method(function(id) { 1.221 + if (!this._temporaryHolder) { 1.222 + return; 1.223 + } 1.224 + this._temporaryHolder.destroy(); 1.225 + delete this._temporaryHolder; 1.226 + }) 1.227 +}); 1.228 + 1.229 +let RootFront = protocol.FrontClass(RootActor, { 1.230 + toString: function() "[root front]", 1.231 + initialize: function(client) { 1.232 + this.actorID = "root"; 1.233 + protocol.Front.prototype.initialize.call(this, client); 1.234 + // Root actor owns itself. 1.235 + this.manage(this); 1.236 + }, 1.237 + 1.238 + getTemporaryChild: protocol.custom(function(id) { 1.239 + if (!this._temporaryHolder) { 1.240 + this._temporaryHolder = this.manage(new protocol.Front(this.conn, {actor: this.actorID + "_temp"})); 1.241 + } 1.242 + return this._getTemporaryChild(id); 1.243 + },{ 1.244 + impl: "_getTemporaryChild" 1.245 + }), 1.246 + 1.247 + clearTemporaryChildren: protocol.custom(function() { 1.248 + if (!this._temporaryHolder) { 1.249 + return promise.resolve(undefined); 1.250 + } 1.251 + this._temporaryHolder.destroy(); 1.252 + delete this._temporaryHolder; 1.253 + return this._clearTemporaryChildren(); 1.254 + }, { 1.255 + impl: "_clearTemporaryChildren" 1.256 + }) 1.257 +}); 1.258 + 1.259 +function run_test() 1.260 +{ 1.261 + DebuggerServer.createRootActor = (conn => { 1.262 + return RootActor(conn); 1.263 + }); 1.264 + DebuggerServer.init(() => true); 1.265 + 1.266 + let trace = connectPipeTracing(); 1.267 + let client = new DebuggerClient(trace); 1.268 + client.connect((applicationType, traits) => { 1.269 + trace.expectReceive({"from":"<actorid>","applicationType":"xpcshell-tests","traits":[]}) 1.270 + do_check_eq(applicationType, "xpcshell-tests"); 1.271 + 1.272 + let rootFront = RootFront(client); 1.273 + let childFront = null; 1.274 + 1.275 + let expectRootChildren = size => { 1.276 + do_check_eq(rootActor._poolMap.size, size + 1); 1.277 + do_check_eq(rootFront._poolMap.size, size + 1); 1.278 + if (childFront) { 1.279 + do_check_eq(childFront._poolMap.size, 0); 1.280 + } 1.281 + }; 1.282 + 1.283 + rootFront.getChild("child1").then(ret => { 1.284 + trace.expectSend({"type":"getChild","str":"child1","to":"<actorid>"}) 1.285 + trace.expectReceive({"actor":"<actorid>","from":"<actorid>"}) 1.286 + 1.287 + childFront = ret; 1.288 + do_check_true(childFront instanceof ChildFront); 1.289 + do_check_eq(childFront.childID, "child1"); 1.290 + expectRootChildren(1); 1.291 + }).then(() => { 1.292 + // Request the child again, make sure the same is returned. 1.293 + return rootFront.getChild("child1"); 1.294 + }).then(ret => { 1.295 + trace.expectSend({"type":"getChild","str":"child1","to":"<actorid>"}) 1.296 + trace.expectReceive({"actor":"<actorid>","from":"<actorid>"}) 1.297 + 1.298 + expectRootChildren(1); 1.299 + do_check_true(ret === childFront); 1.300 + }).then(() => { 1.301 + return childFront.echo("hello"); 1.302 + }).then(ret => { 1.303 + trace.expectSend({"type":"echo","str":"hello","to":"<actorid>"}) 1.304 + trace.expectReceive({"str":"hello","from":"<actorid>"}) 1.305 + 1.306 + do_check_eq(ret, "hello"); 1.307 + }).then(() => { 1.308 + return childFront.getDetail1(); 1.309 + }).then(ret => { 1.310 + trace.expectSend({"type":"getDetail1","to":"<actorid>"}); 1.311 + trace.expectReceive({"actor":"<actorid>","childID":"child1","detail":"detail1","from":"<actorid>"}); 1.312 + do_check_true(ret === childFront); 1.313 + do_check_eq(childFront.detail, "detail1"); 1.314 + }).then(() => { 1.315 + return childFront.getDetail2(); 1.316 + }).then(ret => { 1.317 + trace.expectSend({"type":"getDetail2","to":"<actorid>"}); 1.318 + trace.expectReceive({"actor":"<actorid>","childID":"child1","detail":"detail2","from":"<actorid>"}); 1.319 + do_check_true(ret === childFront); 1.320 + do_check_eq(childFront.detail, "detail2"); 1.321 + }).then(() => { 1.322 + return childFront.getIDDetail(); 1.323 + }).then(ret => { 1.324 + trace.expectSend({"type":"getIDDetail","to":"<actorid>"}); 1.325 + trace.expectReceive({"idDetail": childFront.actorID,"from":"<actorid>"}); 1.326 + do_check_true(ret === childFront); 1.327 + }).then(() => { 1.328 + return childFront.getSibling("siblingID"); 1.329 + }).then(ret => { 1.330 + trace.expectSend({"type":"getSibling","id":"siblingID","to":"<actorid>"}); 1.331 + trace.expectReceive({"sibling":{"actor":"<actorid>","childID":"siblingID"},"from":"<actorid>"}); 1.332 + 1.333 + expectRootChildren(2); 1.334 + }).then(ret => { 1.335 + return rootFront.getTemporaryChild("temp1").then(temp1 => { 1.336 + trace.expectSend({"type":"getTemporaryChild","id":"temp1","to":"<actorid>"}); 1.337 + trace.expectReceive({"child":{"actor":"<actorid>","childID":"temp1"},"from":"<actorid>"}); 1.338 + 1.339 + // At this point we expect two direct children, plus the temporary holder 1.340 + // which should hold 1 itself. 1.341 + do_check_eq(rootActor._temporaryHolder.__poolMap.size, 1); 1.342 + do_check_eq(rootFront._temporaryHolder.__poolMap.size, 1); 1.343 + 1.344 + expectRootChildren(3); 1.345 + return rootFront.getTemporaryChild("temp2").then(temp2 => { 1.346 + trace.expectSend({"type":"getTemporaryChild","id":"temp2","to":"<actorid>"}); 1.347 + trace.expectReceive({"child":{"actor":"<actorid>","childID":"temp2"},"from":"<actorid>"}); 1.348 + 1.349 + // Same amount of direct children, and an extra in the temporary holder. 1.350 + expectRootChildren(3); 1.351 + do_check_eq(rootActor._temporaryHolder.__poolMap.size, 2); 1.352 + do_check_eq(rootFront._temporaryHolder.__poolMap.size, 2); 1.353 + 1.354 + // Get the children of the temporary holder... 1.355 + let checkActors = [entry[1] for (entry of rootActor._temporaryHolder.__poolMap)]; 1.356 + let checkFronts = [entry[1] for (entry of rootFront._temporaryHolder.__poolMap)]; 1.357 + 1.358 + // Now release the temporary holders and expect them to drop again. 1.359 + return rootFront.clearTemporaryChildren().then(() => { 1.360 + trace.expectSend({"type":"clearTemporaryChildren","to":"<actorid>"}); 1.361 + trace.expectReceive({"from":"<actorid>"}); 1.362 + 1.363 + expectRootChildren(2); 1.364 + do_check_false(!!rootActor._temporaryHolder); 1.365 + do_check_false(!!rootFront._temporaryHolder); 1.366 + for (let checkActor of checkActors) { 1.367 + do_check_true(checkActor.destroyed); 1.368 + do_check_true(checkActor.destroyed); 1.369 + } 1.370 + }); 1.371 + }); 1.372 + }) 1.373 + }).then(ret => { 1.374 + return rootFront.getChildren(["child1", "child2"]); 1.375 + }).then(ret => { 1.376 + trace.expectSend({"type":"getChildren","ids":["child1","child2"],"to":"<actorid>"}); 1.377 + trace.expectReceive({"children":[{"actor":"<actorid>","childID":"child1"},{"actor":"<actorid>","childID":"child2"}],"from":"<actorid>"}); 1.378 + 1.379 + expectRootChildren(3); 1.380 + do_check_true(ret[0] === childFront); 1.381 + do_check_true(ret[1] !== childFront); 1.382 + do_check_true(ret[1] instanceof ChildFront); 1.383 + 1.384 + // On both children, listen to events. We're only 1.385 + // going to trigger events on the first child, so an event 1.386 + // triggered on the second should cause immediate failures. 1.387 + 1.388 + let set = new Set(["event1", "named-event", "object-event", "array-object-event"]); 1.389 + 1.390 + childFront.on("event1", (a, b, c) => { 1.391 + do_check_eq(a, 1); 1.392 + do_check_eq(b, 2); 1.393 + do_check_eq(c, 3); 1.394 + // Verify that the pre-event handler was called. 1.395 + do_check_eq(childFront.event1arg3, 3); 1.396 + set.delete("event1"); 1.397 + }); 1.398 + childFront.on("named-event", (a, b, c) => { 1.399 + do_check_eq(a, 1); 1.400 + do_check_eq(b, 2); 1.401 + do_check_eq(c, 3); 1.402 + set.delete("named-event"); 1.403 + }); 1.404 + childFront.on("object-event", (obj) => { 1.405 + do_check_true(obj === childFront); 1.406 + do_check_eq(childFront.detail, "detail1"); 1.407 + set.delete("object-event"); 1.408 + }); 1.409 + childFront.on("array-object-event", (array) => { 1.410 + do_check_true(array[0] === childFront); 1.411 + do_check_eq(childFront.detail, "detail2"); 1.412 + set.delete("array-object-event"); 1.413 + }); 1.414 + 1.415 + let fail = function() { 1.416 + do_throw("Unexpected event"); 1.417 + } 1.418 + ret[1].on("event1", fail); 1.419 + ret[1].on("named-event", fail); 1.420 + ret[1].on("object-event", fail); 1.421 + ret[1].on("array-object-event", fail); 1.422 + 1.423 + return childFront.emitEvents().then(() => { 1.424 + trace.expectSend({"type":"emitEvents","to":"<actorid>"}); 1.425 + trace.expectReceive({"type":"event1","a":1,"b":2,"c":3,"from":"<actorid>"}); 1.426 + trace.expectReceive({"type":"namedEvent","a":1,"b":2,"c":3,"from":"<actorid>"}); 1.427 + trace.expectReceive({"type":"objectEvent","detail":{"actor":"<actorid>","childID":"child1","detail":"detail1"},"from":"<actorid>"}); 1.428 + trace.expectReceive({"type":"arrayObjectEvent","detail":[{"actor":"<actorid>","childID":"child1","detail":"detail2"}],"from":"<actorid>"}); 1.429 + trace.expectReceive({"value":"correct response","from":"<actorid>"}); 1.430 + 1.431 + 1.432 + do_check_eq(set.size, 0); 1.433 + }); 1.434 + }).then(ret => { 1.435 + return rootFront.getManyChildren(); 1.436 + }).then(ret => { 1.437 + trace.expectSend({"type":"getManyChildren","to":"<actorid>"}); 1.438 + trace.expectReceive({"foo":"bar","child5":{"actor":"<actorid>","childID":"child5"},"more":[{"actor":"<actorid>","childID":"child6"},{"actor":"<actorid>","childID":"child7"}],"from":"<actorid>"}); 1.439 + 1.440 + // Check all the crazy stuff we did in getManyChildren 1.441 + do_check_eq(ret.foo, "bar"); 1.442 + do_check_eq(ret.child5.childID, "child5"); 1.443 + do_check_eq(ret.more[0].childID, "child6"); 1.444 + do_check_eq(ret.more[1].childID, "child7"); 1.445 + }).then(() => { 1.446 + client.close(() => { 1.447 + do_test_finished(); 1.448 + }); 1.449 + }).then(null, err => { 1.450 + do_report_unexpected_exception(err, "Failure executing test"); 1.451 + }); 1.452 + }); 1.453 + do_test_pending(); 1.454 +}