toolkit/devtools/server/tests/unit/test_forwardingprefix.js

changeset 0
6474c204b198
     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 +}

mercurial