summaryrefslogtreecommitdiffstats
path: root/devtools/server/tests/unit/test_forwardingprefix.js
blob: 885a99db897d79b42c2e6ad6e8f29f34167e0f9b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

/* Exercise prefix-based forwarding of packets to other transports. */

const { RootActor } = require("devtools/server/actors/root");

var gMainConnection, gMainTransport;
var gSubconnection1, gSubconnection2;
var gClient;

function run_test()
{
  DebuggerServer.init();

  add_test(createMainConnection);
  add_test(TestNoForwardingYet);
  add_test(createSubconnection1);
  add_test(TestForwardPrefix1OnlyRoot);
  add_test(createSubconnection2);
  add_test(TestForwardPrefix12OnlyRoot);
  add_test(TestForwardPrefix12WithActor1);
  add_test(TestForwardPrefix12WithActor12);
  run_next_test();
}

/*
 * Create a pipe connection, and return an object |{ conn, transport }|,
 * where |conn| is the new DebuggerServerConnection instance, and
 * |transport| is the client side of the transport on which it communicates
 * (that is, packets sent on |transport| go to the new connection, and
 * |transport|'s hooks receive replies).
 *
 * |aPrefix| is optional; if present, it's the prefix (minus the '/') for
 * actors in the new connection.
 */
function newConnection(aPrefix)
{
  var conn;
  DebuggerServer.createRootActor = function (aConn) {
    conn = aConn;
    return new RootActor(aConn, {});
  };

  var transport = DebuggerServer.connectPipe(aPrefix);

  return { conn: conn, transport: transport };
}

/* Create the main connection for these tests. */
function createMainConnection()
{
  ({ conn: gMainConnection, transport: gMainTransport } = newConnection());
  gClient = new DebuggerClient(gMainTransport);
  gClient.connect().then(([aType, aTraits]) => run_next_test());
}

/*
 * Exchange 'echo' messages with five actors:
 * - root
 * - prefix1/root
 * - prefix1/actor
 * - prefix2/root
 * - prefix2/actor
 *
 * Expect proper echos from those named in |aReachables|, and 'noSuchActor'
 * errors from the others. When we've gotten all our replies (errors or
 * otherwise), call |aCompleted|.
 *
 * To avoid deep stacks, we call aCompleted from the next tick.
 */
function tryActors(aReachables, aCompleted) {
  let count = 0;

  let outerActor;
  for (outerActor of [ "root",
                       "prefix1/root", "prefix1/actor",
                       "prefix2/root", "prefix2/actor" ]) {
    /*
     * Let each callback capture its own iteration's value; outerActor is
     * local to the whole loop, not to a single iteration.
     */
    let actor = outerActor;

    count++;

    gClient.request({ to: actor, type: "echo", value: "tango"}, // phone home
                    (aResponse) => {
                      if (aReachables.has(actor))
                        do_check_matches({ from: actor, to: actor, type: "echo", value: "tango" }, aResponse);
                      else
                        do_check_matches({ from: actor, error: "noSuchActor", message: "No such actor for ID: " + actor }, aResponse);

                      if (--count == 0)
                        do_execute_soon(aCompleted, "tryActors callback " + aCompleted.name);
                    });
  }
}

/*
 * With no forwarding established, sending messages to root should work,
 * but sending messages to prefixed actor names, or anyone else, should get
 * an error.
 */
function TestNoForwardingYet()
{
  tryActors(new Set(["root"]), run_next_test);
}

/*
 * Create a new pipe connection which forwards its reply packets to
 * gMainConnection's client, and to which gMainConnection forwards packets
 * directed to actors whose names begin with |aPrefix + '/'|, and.
 *
 * Return an object { conn, transport }, as for newConnection.
 */
function newSubconnection(aPrefix)
{
  let { conn, transport } = newConnection(aPrefix);
  transport.hooks = {
    onPacket: (aPacket) => gMainConnection.send(aPacket),
    onClosed: () => {}
  };
  gMainConnection.setForwarding(aPrefix, transport);

  return { conn: conn, transport: transport };
}

/* Create a second root actor, to which we can forward things. */
function createSubconnection1()
{
  let { conn, transport } = newSubconnection("prefix1");
  gSubconnection1 = conn;
  transport.ready();
  gClient.expectReply("prefix1/root", (aReply) => run_next_test());
}

// Establish forwarding, but don't put any actors in that server.
function TestForwardPrefix1OnlyRoot()
{
  tryActors(new Set(["root", "prefix1/root"]), run_next_test);
}

/* Create a third root actor, to which we can forward things. */
function createSubconnection2()
{
  let { conn, transport } = newSubconnection("prefix2");
  gSubconnection2 = conn;
  transport.ready();
  gClient.expectReply("prefix2/root", (aReply) => run_next_test());
}

function TestForwardPrefix12OnlyRoot()
{
  tryActors(new Set(["root", "prefix1/root", "prefix2/root"]), run_next_test);
}

// A dumb actor that implements 'echo'.
//
// It's okay that both subconnections' actors behave identically, because
// the reply-sending code attaches the replying actor's name to the packet,
// so simply matching the 'from' field in the reply ensures that we heard
// from the right actor.
function EchoActor(aConnection)
{
  this.conn = aConnection;
}
EchoActor.prototype.actorPrefix = "EchoActor";
EchoActor.prototype.onEcho = function (aRequest) {
  /*
   * Request packets are frozen. Copy aRequest, so that
   * DebuggerServerConnection.onPacket can attach a 'from' property.
   */
  return JSON.parse(JSON.stringify(aRequest));
};
EchoActor.prototype.requestTypes = {
  "echo": EchoActor.prototype.onEcho
};

function TestForwardPrefix12WithActor1()
{
  let actor = new EchoActor(gSubconnection1);
  actor.actorID = "prefix1/actor";
  gSubconnection1.addActor(actor);

  tryActors(new Set(["root", "prefix1/root", "prefix1/actor", "prefix2/root"]), run_next_test);
}

function TestForwardPrefix12WithActor12()
{
  let actor = new EchoActor(gSubconnection2);
  actor.actorID = "prefix2/actor";
  gSubconnection2.addActor(actor);

  tryActors(new Set(["root", "prefix1/root", "prefix1/actor", "prefix2/root", "prefix2/actor"]), run_next_test);
}