Wed, 31 Dec 2014 06:55:46 +0100
Added tag TORBROWSER_REPLICA for changeset 6474c204b198
1 const CC = Components.Constructor;
3 const ServerSocket = CC("@mozilla.org/network/server-socket;1",
4 "nsIServerSocket",
5 "init");
7 /**
8 * TestServer: A single instance of this is created as |serv|. When created,
9 * it starts listening on the loopback address on port |serv.port|. Tests will
10 * connect to it after setting |serv.acceptCallback|, which is invoked after it
11 * accepts a connection.
12 *
13 * Within |serv.acceptCallback|, various properties of |serv| can be used to
14 * run checks. After the callback, the connection is closed, but the server
15 * remains listening until |serv.stop|
16 *
17 * Note: TestServer can only handle a single connection at a time. Tests
18 * should use run_next_test at the end of |serv.acceptCallback| to start the
19 * following test which creates a connection.
20 */
21 function TestServer() {
22 this.reset();
24 // start server.
25 // any port (-1), loopback only (true), default backlog (-1)
26 this.listener = ServerSocket(-1, true, -1);
27 this.port = this.listener.port;
28 do_print('server: listening on', this.port);
29 this.listener.asyncListen(this);
30 }
32 TestServer.prototype = {
33 onSocketAccepted: function(socket, trans) {
34 do_print('server: got client connection');
36 // one connection at a time.
37 if (this.input !== null) {
38 try { socket.close(); } catch(ignore) {}
39 do_throw("Test written to handle one connection at a time.");
40 }
42 try {
43 this.input = trans.openInputStream(0, 0, 0);
44 this.output = trans.openOutputStream(0, 0, 0);
45 this.selfAddr = trans.getScriptableSelfAddr();
46 this.peerAddr = trans.getScriptablePeerAddr();
48 this.acceptCallback();
49 } catch(e) {
50 /* In a native callback such as onSocketAccepted, exceptions might not
51 * get output correctly or logged to test output. Send them through
52 * do_throw, which fails the test immediately. */
53 do_report_unexpected_exception(e, "in TestServer.onSocketAccepted");
54 }
56 this.reset();
57 } ,
59 onStopListening: function(socket) {} ,
61 /**
62 * Called to close a connection and clean up properties.
63 */
64 reset: function() {
65 if (this.input)
66 try { this.input.close(); } catch(ignore) {}
67 if (this.output)
68 try { this.output.close(); } catch(ignore) {}
70 this.input = null;
71 this.output = null;
72 this.acceptCallback = null;
73 this.selfAddr = null;
74 this.peerAddr = null;
75 } ,
77 /**
78 * Cleanup for TestServer and this test case.
79 */
80 stop: function() {
81 this.reset();
82 try { this.listener.close(); } catch(ignore) {}
83 }
84 };
87 /**
88 * Helper function.
89 * Compares two nsINetAddr objects and ensures they are logically equivalent.
90 */
91 function checkAddrEqual(lhs, rhs) {
92 do_check_eq(lhs.family, rhs.family);
94 if (lhs.family === Ci.nsINetAddr.FAMILY_INET) {
95 do_check_eq(lhs.address, rhs.address);
96 do_check_eq(lhs.port, rhs.port);
97 }
99 /* TODO: fully support ipv6 and local */
100 }
103 /**
104 * An instance of SocketTransportService, used to create connections.
105 */
106 var sts;
108 /**
109 * Single instance of TestServer
110 */
111 var serv;
113 /**
114 * Connections have 5 seconds to be made, or a timeout function fails this
115 * test. This prevents the test from hanging and bringing down the entire
116 * xpcshell test chain.
117 */
118 var connectTimeout = 5*1000;
120 /**
121 * A place for individual tests to place Objects of importance for access
122 * throughout asynchronous testing. Particularly important for any output or
123 * input streams opened, as cleanup of those objects (by the garbage collector)
124 * causes the stream to close and may have other side effects.
125 */
126 var testDataStore = null;
128 /**
129 * IPv4 test.
130 */
131 function testIpv4() {
132 testDataStore = {
133 transport : null ,
134 ouput : null
135 }
137 serv.acceptCallback = function() {
138 // disable the timeoutCallback
139 serv.timeoutCallback = function(){};
141 var selfAddr = testDataStore.transport.getScriptableSelfAddr();
142 var peerAddr = testDataStore.transport.getScriptablePeerAddr();
144 // check peerAddr against expected values
145 do_check_eq(peerAddr.family, Ci.nsINetAddr.FAMILY_INET);
146 do_check_eq(peerAddr.port, testDataStore.transport.port);
147 do_check_eq(peerAddr.port, serv.port);
148 do_check_eq(peerAddr.address, "127.0.0.1");
150 // check selfAddr against expected values
151 do_check_eq(selfAddr.family, Ci.nsINetAddr.FAMILY_INET);
152 do_check_eq(selfAddr.address, "127.0.0.1");
154 // check that selfAddr = server.peerAddr and vice versa.
155 checkAddrEqual(selfAddr, serv.peerAddr);
156 checkAddrEqual(peerAddr, serv.selfAddr);
158 testDataStore = null;
159 do_execute_soon(run_next_test);
160 };
162 // Useful timeout for debugging test hangs
163 /*serv.timeoutCallback = function(tname) {
164 if (tname === 'testIpv4')
165 do_throw('testIpv4 never completed a connection to TestServ');
166 };
167 do_timeout(connectTimeout, function(){ serv.timeoutCallback('testIpv4'); });*/
169 testDataStore.transport = sts.createTransport(null, 0, '127.0.0.1', serv.port, null);
170 /*
171 * Need to hold |output| so that the output stream doesn't close itself and
172 * the associated connection.
173 */
174 testDataStore.output = testDataStore.transport.openOutputStream(Ci.nsITransport.OPEN_BLOCKING,0,0);
176 /* NEXT:
177 * openOutputStream -> onSocketAccepted -> acceptedCallback -> run_next_test
178 * OR (if the above timeout is uncommented)
179 * <connectTimeout lapses> -> timeoutCallback -> do_throw
180 */
181 }
184 /**
185 * Running the tests.
186 */
187 function run_test() {
188 sts = Cc["@mozilla.org/network/socket-transport-service;1"]
189 .getService(Ci.nsISocketTransportService);
190 serv = new TestServer();
192 do_register_cleanup(function(){ serv.stop(); });
194 add_test(testIpv4);
195 /* TODO: testIpv6 */
196 /* TODO: testLocal */
198 run_next_test();
199 }