|
1 /* Any copyright is dedicated to the Public Domain. |
|
2 http://creativecommons.org/publicdomain/zero/1.0/ */ |
|
3 |
|
4 /* Exercise prefix-based forwarding of packets to other transports. */ |
|
5 |
|
6 var gMainConnection, gMainTransport; |
|
7 var gSubconnection1, gSubconnection2; |
|
8 var gClient; |
|
9 |
|
10 function run_test() |
|
11 { |
|
12 DebuggerServer.init(); |
|
13 |
|
14 add_test(createMainConnection); |
|
15 add_test(TestNoForwardingYet); |
|
16 add_test(createSubconnection1); |
|
17 add_test(TestForwardPrefix1OnlyRoot); |
|
18 add_test(createSubconnection2); |
|
19 add_test(TestForwardPrefix12OnlyRoot); |
|
20 add_test(TestForwardPrefix12WithActor1); |
|
21 add_test(TestForwardPrefix12WithActor12); |
|
22 run_next_test(); |
|
23 } |
|
24 |
|
25 /* |
|
26 * Create a pipe connection, and return an object |{ conn, transport }|, |
|
27 * where |conn| is the new DebuggerServerConnection instance, and |
|
28 * |transport| is the client side of the transport on which it communicates |
|
29 * (that is, packets sent on |transport| go to the new connection, and |
|
30 * |transport|'s hooks receive replies). |
|
31 * |
|
32 * |aPrefix| is optional; if present, it's the prefix (minus the ':') for |
|
33 * actors in the new connection. |
|
34 */ |
|
35 function newConnection(aPrefix) |
|
36 { |
|
37 var conn; |
|
38 DebuggerServer.createRootActor = function (aConn) { |
|
39 conn = aConn; |
|
40 return new DebuggerServer.RootActor(aConn, {}); |
|
41 }; |
|
42 |
|
43 var transport = DebuggerServer.connectPipe(aPrefix); |
|
44 |
|
45 return { conn: conn, transport: transport }; |
|
46 } |
|
47 |
|
48 /* Create the main connection for these tests. */ |
|
49 function createMainConnection() |
|
50 { |
|
51 ({ conn: gMainConnection, transport: gMainTransport }) = newConnection(); |
|
52 gClient = new DebuggerClient(gMainTransport); |
|
53 gClient.connect((aType, aTraits) => run_next_test()); |
|
54 } |
|
55 |
|
56 /* |
|
57 * Exchange 'echo' messages with five actors: |
|
58 * - root |
|
59 * - prefix1:root |
|
60 * - prefix1:actor |
|
61 * - prefix2:root |
|
62 * - prefix2:actor |
|
63 * |
|
64 * Expect proper echos from those named in |aReachables|, and 'noSuchActor' |
|
65 * errors from the others. When we've gotten all our replies (errors or |
|
66 * otherwise), call |aCompleted|. |
|
67 * |
|
68 * To avoid deep stacks, we call aCompleted from the next tick. |
|
69 */ |
|
70 function tryActors(aReachables, aCompleted) { |
|
71 let count = 0; |
|
72 |
|
73 let outerActor; |
|
74 for (outerActor of [ 'root', |
|
75 'prefix1:root', 'prefix1:actor', |
|
76 'prefix2:root', 'prefix2:actor' ]) { |
|
77 /* |
|
78 * Let each callback capture its own iteration's value; outerActor is |
|
79 * local to the whole loop, not to a single iteration. |
|
80 */ |
|
81 let actor = outerActor; |
|
82 |
|
83 count++; |
|
84 |
|
85 gClient.request({ to: actor, type: 'echo', value: 'tango'}, // phone home |
|
86 (aResponse) => { |
|
87 if (aReachables.has(actor)) |
|
88 do_check_matches({ from: actor, type: 'echo', value: 'tango' }, aResponse); |
|
89 else |
|
90 do_check_matches({ from: actor, error: 'noSuchActor' }, aResponse); |
|
91 |
|
92 if (--count == 0) |
|
93 do_execute_soon(aCompleted, "tryActors callback " + aCompleted.name); |
|
94 }); |
|
95 } |
|
96 } |
|
97 |
|
98 /* |
|
99 * With no forwarding established, sending messages to root should work, |
|
100 * but sending messages to prefixed actor names, or anyone else, should get |
|
101 * an error. |
|
102 */ |
|
103 function TestNoForwardingYet() |
|
104 { |
|
105 tryActors(Set(['root']), run_next_test); |
|
106 } |
|
107 |
|
108 /* |
|
109 * Create a new pipe connection which forwards its reply packets to |
|
110 * gMainConnection's client, and to which gMainConnection forwards packets |
|
111 * directed to actors whose names begin with |aPrefix + ':'|, and. |
|
112 * |
|
113 * Return an object { conn, transport }, as for newConnection. |
|
114 */ |
|
115 function newSubconnection(aPrefix) |
|
116 { |
|
117 let { conn, transport } = newConnection(aPrefix); |
|
118 transport.hooks = { |
|
119 onPacket: (aPacket) => gMainConnection.send(aPacket), |
|
120 onClosed: () => {} |
|
121 } |
|
122 gMainConnection.setForwarding(aPrefix, transport); |
|
123 |
|
124 return { conn: conn, transport: transport }; |
|
125 } |
|
126 |
|
127 /* Create a second root actor, to which we can forward things. */ |
|
128 function createSubconnection1() |
|
129 { |
|
130 let { conn, transport } = newSubconnection('prefix1'); |
|
131 gSubconnection1 = conn; |
|
132 transport.ready(); |
|
133 gClient.expectReply('prefix1:root', (aReply) => run_next_test()); |
|
134 } |
|
135 |
|
136 // Establish forwarding, but don't put any actors in that server. |
|
137 function TestForwardPrefix1OnlyRoot() |
|
138 { |
|
139 tryActors(Set(['root', 'prefix1:root']), run_next_test); |
|
140 } |
|
141 |
|
142 /* Create a third root actor, to which we can forward things. */ |
|
143 function createSubconnection2() |
|
144 { |
|
145 let { conn, transport } = newSubconnection('prefix2'); |
|
146 gSubconnection2 = conn; |
|
147 transport.ready(); |
|
148 gClient.expectReply('prefix2:root', (aReply) => run_next_test()); |
|
149 } |
|
150 |
|
151 function TestForwardPrefix12OnlyRoot() |
|
152 { |
|
153 tryActors(Set(['root', 'prefix1:root', 'prefix2:root']), run_next_test); |
|
154 } |
|
155 |
|
156 // A dumb actor that implements 'echo'. |
|
157 // |
|
158 // It's okay that both subconnections' actors behave identically, because |
|
159 // the reply-sending code attaches the replying actor's name to the packet, |
|
160 // so simply matching the 'from' field in the reply ensures that we heard |
|
161 // from the right actor. |
|
162 function EchoActor(aConnection) |
|
163 { |
|
164 this.conn = aConnection; |
|
165 } |
|
166 EchoActor.prototype.actorPrefix = "EchoActor"; |
|
167 EchoActor.prototype.onEcho = function (aRequest) { |
|
168 /* |
|
169 * Request packets are frozen. Copy aRequest, so that |
|
170 * DebuggerServerConnection.onPacket can attach a 'from' property. |
|
171 */ |
|
172 return JSON.parse(JSON.stringify(aRequest)); |
|
173 }; |
|
174 EchoActor.prototype.requestTypes = { |
|
175 "echo": EchoActor.prototype.onEcho |
|
176 }; |
|
177 |
|
178 function TestForwardPrefix12WithActor1() |
|
179 { |
|
180 let actor = new EchoActor(gSubconnection1) |
|
181 actor.actorID = 'prefix1:actor'; |
|
182 gSubconnection1.addActor(actor); |
|
183 |
|
184 tryActors(Set(['root', 'prefix1:root', 'prefix1:actor', 'prefix2:root']), run_next_test); |
|
185 } |
|
186 |
|
187 function TestForwardPrefix12WithActor12() |
|
188 { |
|
189 let actor = new EchoActor(gSubconnection2) |
|
190 actor.actorID = 'prefix2:actor'; |
|
191 gSubconnection2.addActor(actor); |
|
192 |
|
193 tryActors(Set(['root', 'prefix1:root', 'prefix1:actor', 'prefix2:root', 'prefix2:actor']), run_next_test); |
|
194 } |