1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/devtools/server/tests/unit/test_forwardingprefix.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,194 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +/* Exercise prefix-based forwarding of packets to other transports. */ 1.8 + 1.9 +var gMainConnection, gMainTransport; 1.10 +var gSubconnection1, gSubconnection2; 1.11 +var gClient; 1.12 + 1.13 +function run_test() 1.14 +{ 1.15 + DebuggerServer.init(); 1.16 + 1.17 + add_test(createMainConnection); 1.18 + add_test(TestNoForwardingYet); 1.19 + add_test(createSubconnection1); 1.20 + add_test(TestForwardPrefix1OnlyRoot); 1.21 + add_test(createSubconnection2); 1.22 + add_test(TestForwardPrefix12OnlyRoot); 1.23 + add_test(TestForwardPrefix12WithActor1); 1.24 + add_test(TestForwardPrefix12WithActor12); 1.25 + run_next_test(); 1.26 +} 1.27 + 1.28 +/* 1.29 + * Create a pipe connection, and return an object |{ conn, transport }|, 1.30 + * where |conn| is the new DebuggerServerConnection instance, and 1.31 + * |transport| is the client side of the transport on which it communicates 1.32 + * (that is, packets sent on |transport| go to the new connection, and 1.33 + * |transport|'s hooks receive replies). 1.34 + * 1.35 + * |aPrefix| is optional; if present, it's the prefix (minus the ':') for 1.36 + * actors in the new connection. 1.37 + */ 1.38 +function newConnection(aPrefix) 1.39 +{ 1.40 + var conn; 1.41 + DebuggerServer.createRootActor = function (aConn) { 1.42 + conn = aConn; 1.43 + return new DebuggerServer.RootActor(aConn, {}); 1.44 + }; 1.45 + 1.46 + var transport = DebuggerServer.connectPipe(aPrefix); 1.47 + 1.48 + return { conn: conn, transport: transport }; 1.49 +} 1.50 + 1.51 +/* Create the main connection for these tests. */ 1.52 +function createMainConnection() 1.53 +{ 1.54 + ({ conn: gMainConnection, transport: gMainTransport }) = newConnection(); 1.55 + gClient = new DebuggerClient(gMainTransport); 1.56 + gClient.connect((aType, aTraits) => run_next_test()); 1.57 +} 1.58 + 1.59 +/* 1.60 + * Exchange 'echo' messages with five actors: 1.61 + * - root 1.62 + * - prefix1:root 1.63 + * - prefix1:actor 1.64 + * - prefix2:root 1.65 + * - prefix2:actor 1.66 + * 1.67 + * Expect proper echos from those named in |aReachables|, and 'noSuchActor' 1.68 + * errors from the others. When we've gotten all our replies (errors or 1.69 + * otherwise), call |aCompleted|. 1.70 + * 1.71 + * To avoid deep stacks, we call aCompleted from the next tick. 1.72 + */ 1.73 +function tryActors(aReachables, aCompleted) { 1.74 + let count = 0; 1.75 + 1.76 + let outerActor; 1.77 + for (outerActor of [ 'root', 1.78 + 'prefix1:root', 'prefix1:actor', 1.79 + 'prefix2:root', 'prefix2:actor' ]) { 1.80 + /* 1.81 + * Let each callback capture its own iteration's value; outerActor is 1.82 + * local to the whole loop, not to a single iteration. 1.83 + */ 1.84 + let actor = outerActor; 1.85 + 1.86 + count++; 1.87 + 1.88 + gClient.request({ to: actor, type: 'echo', value: 'tango'}, // phone home 1.89 + (aResponse) => { 1.90 + if (aReachables.has(actor)) 1.91 + do_check_matches({ from: actor, type: 'echo', value: 'tango' }, aResponse); 1.92 + else 1.93 + do_check_matches({ from: actor, error: 'noSuchActor' }, aResponse); 1.94 + 1.95 + if (--count == 0) 1.96 + do_execute_soon(aCompleted, "tryActors callback " + aCompleted.name); 1.97 + }); 1.98 + } 1.99 +} 1.100 + 1.101 +/* 1.102 + * With no forwarding established, sending messages to root should work, 1.103 + * but sending messages to prefixed actor names, or anyone else, should get 1.104 + * an error. 1.105 + */ 1.106 +function TestNoForwardingYet() 1.107 +{ 1.108 + tryActors(Set(['root']), run_next_test); 1.109 +} 1.110 + 1.111 +/* 1.112 + * Create a new pipe connection which forwards its reply packets to 1.113 + * gMainConnection's client, and to which gMainConnection forwards packets 1.114 + * directed to actors whose names begin with |aPrefix + ':'|, and. 1.115 + * 1.116 + * Return an object { conn, transport }, as for newConnection. 1.117 + */ 1.118 +function newSubconnection(aPrefix) 1.119 +{ 1.120 + let { conn, transport } = newConnection(aPrefix); 1.121 + transport.hooks = { 1.122 + onPacket: (aPacket) => gMainConnection.send(aPacket), 1.123 + onClosed: () => {} 1.124 + } 1.125 + gMainConnection.setForwarding(aPrefix, transport); 1.126 + 1.127 + return { conn: conn, transport: transport }; 1.128 +} 1.129 + 1.130 +/* Create a second root actor, to which we can forward things. */ 1.131 +function createSubconnection1() 1.132 +{ 1.133 + let { conn, transport } = newSubconnection('prefix1'); 1.134 + gSubconnection1 = conn; 1.135 + transport.ready(); 1.136 + gClient.expectReply('prefix1:root', (aReply) => run_next_test()); 1.137 +} 1.138 + 1.139 +// Establish forwarding, but don't put any actors in that server. 1.140 +function TestForwardPrefix1OnlyRoot() 1.141 +{ 1.142 + tryActors(Set(['root', 'prefix1:root']), run_next_test); 1.143 +} 1.144 + 1.145 +/* Create a third root actor, to which we can forward things. */ 1.146 +function createSubconnection2() 1.147 +{ 1.148 + let { conn, transport } = newSubconnection('prefix2'); 1.149 + gSubconnection2 = conn; 1.150 + transport.ready(); 1.151 + gClient.expectReply('prefix2:root', (aReply) => run_next_test()); 1.152 +} 1.153 + 1.154 +function TestForwardPrefix12OnlyRoot() 1.155 +{ 1.156 + tryActors(Set(['root', 'prefix1:root', 'prefix2:root']), run_next_test); 1.157 +} 1.158 + 1.159 +// A dumb actor that implements 'echo'. 1.160 +// 1.161 +// It's okay that both subconnections' actors behave identically, because 1.162 +// the reply-sending code attaches the replying actor's name to the packet, 1.163 +// so simply matching the 'from' field in the reply ensures that we heard 1.164 +// from the right actor. 1.165 +function EchoActor(aConnection) 1.166 +{ 1.167 + this.conn = aConnection; 1.168 +} 1.169 +EchoActor.prototype.actorPrefix = "EchoActor"; 1.170 +EchoActor.prototype.onEcho = function (aRequest) { 1.171 + /* 1.172 + * Request packets are frozen. Copy aRequest, so that 1.173 + * DebuggerServerConnection.onPacket can attach a 'from' property. 1.174 + */ 1.175 + return JSON.parse(JSON.stringify(aRequest)); 1.176 +}; 1.177 +EchoActor.prototype.requestTypes = { 1.178 + "echo": EchoActor.prototype.onEcho 1.179 +}; 1.180 + 1.181 +function TestForwardPrefix12WithActor1() 1.182 +{ 1.183 + let actor = new EchoActor(gSubconnection1) 1.184 + actor.actorID = 'prefix1:actor'; 1.185 + gSubconnection1.addActor(actor); 1.186 + 1.187 + tryActors(Set(['root', 'prefix1:root', 'prefix1:actor', 'prefix2:root']), run_next_test); 1.188 +} 1.189 + 1.190 +function TestForwardPrefix12WithActor12() 1.191 +{ 1.192 + let actor = new EchoActor(gSubconnection2) 1.193 + actor.actorID = 'prefix2:actor'; 1.194 + gSubconnection2.addActor(actor); 1.195 + 1.196 + tryActors(Set(['root', 'prefix1:root', 'prefix1:actor', 'prefix2:root', 'prefix2:actor']), run_next_test); 1.197 +}