dom/network/tests/unit/test_tcpserversocket.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /**
michael@0 2 * Test TCPSocket.js by creating an XPCOM-style server socket, then sending
michael@0 3 * data in both directions and making sure each side receives their data
michael@0 4 * correctly and with the proper events.
michael@0 5 *
michael@0 6 * This test is derived from netwerk/test/unit/test_socks.js, except we don't
michael@0 7 * involve a subprocess.
michael@0 8 *
michael@0 9 * Future work:
michael@0 10 * - SSL. see https://bugzilla.mozilla.org/show_bug.cgi?id=466524
michael@0 11 * https://bugzilla.mozilla.org/show_bug.cgi?id=662180
michael@0 12 * Alternatively, mochitests could be used.
michael@0 13 * - Testing overflow logic.
michael@0 14 *
michael@0 15 **/
michael@0 16
michael@0 17 const Cc = Components.classes;
michael@0 18 const Ci = Components.interfaces;
michael@0 19 const Cr = Components.results;
michael@0 20 const Cu = Components.utils;
michael@0 21 const CC = Components.Constructor;
michael@0 22
michael@0 23 /**
michael@0 24 *
michael@0 25 * Constants
michael@0 26 *
michael@0 27 */
michael@0 28
michael@0 29 // Test parameter.
michael@0 30 const PORT = 8085;
michael@0 31 const BACKLOG = -1;
michael@0 32
michael@0 33 // Some binary data to send.
michael@0 34 const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0],
michael@0 35 DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length),
michael@0 36 TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER),
michael@0 37 HELLO_WORLD = "hlo wrld. ",
michael@0 38 BIG_ARRAY = new Array(65539);
michael@0 39
michael@0 40 TYPED_DATA_ARRAY.set(DATA_ARRAY, 0);
michael@0 41
michael@0 42 for (var i_big = 0; i_big < BIG_ARRAY.length; i_big++) {
michael@0 43 BIG_ARRAY[i_big] = Math.floor(Math.random() * 256);
michael@0 44 }
michael@0 45
michael@0 46 const BIG_ARRAY_BUFFER = new ArrayBuffer(BIG_ARRAY.length);
michael@0 47 const BIG_TYPED_ARRAY = new Uint8Array(BIG_ARRAY_BUFFER);
michael@0 48 BIG_TYPED_ARRAY.set(BIG_ARRAY);
michael@0 49
michael@0 50 const TCPSocket = new (CC("@mozilla.org/tcp-socket;1",
michael@0 51 "nsIDOMTCPSocket"))();
michael@0 52
michael@0 53 const gInChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
michael@0 54 .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
michael@0 55
michael@0 56 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 57 Cu.import("resource://gre/modules/Services.jsm");
michael@0 58
michael@0 59 /**
michael@0 60 *
michael@0 61 * Helper functions
michael@0 62 *
michael@0 63 */
michael@0 64
michael@0 65
michael@0 66 function makeSuccessCase(name) {
michael@0 67 return function() {
michael@0 68 do_print('got expected: ' + name);
michael@0 69 run_next_test();
michael@0 70 };
michael@0 71 }
michael@0 72
michael@0 73 function makeJointSuccess(names) {
michael@0 74 let funcs = {}, successCount = 0;
michael@0 75 names.forEach(function(name) {
michael@0 76 funcs[name] = function() {
michael@0 77 do_print('got expected: ' + name);
michael@0 78 if (++successCount === names.length)
michael@0 79 run_next_test();
michael@0 80 };
michael@0 81 });
michael@0 82 return funcs;
michael@0 83 }
michael@0 84
michael@0 85 function makeFailureCase(name) {
michael@0 86 return function() {
michael@0 87 let argstr;
michael@0 88 if (arguments.length) {
michael@0 89 argstr = '(args: ' +
michael@0 90 Array.map(arguments, function(x) { return x.data + ""; }).join(" ") + ')';
michael@0 91 }
michael@0 92 else {
michael@0 93 argstr = '(no arguments)';
michael@0 94 }
michael@0 95 do_throw('got unexpected: ' + name + ' ' + argstr);
michael@0 96 };
michael@0 97 }
michael@0 98
michael@0 99 function makeExpectData(name, expectedData, fromEvent, callback) {
michael@0 100 let dataBuffer = fromEvent ? null : [], done = false;
michael@0 101 let dataBufferView = null;
michael@0 102 return function(receivedData) {
michael@0 103 if (receivedData.data) {
michael@0 104 receivedData = receivedData.data;
michael@0 105 }
michael@0 106 let recvLength = receivedData.byteLength !== undefined ?
michael@0 107 receivedData.byteLength : receivedData.length;
michael@0 108
michael@0 109 if (fromEvent) {
michael@0 110 if (dataBuffer) {
michael@0 111 let newBuffer = new ArrayBuffer(dataBuffer.byteLength + recvLength);
michael@0 112 let newBufferView = new Uint8Array(newBuffer);
michael@0 113 newBufferView.set(dataBufferView, 0);
michael@0 114 newBufferView.set(receivedData, dataBuffer.byteLength);
michael@0 115 dataBuffer = newBuffer;
michael@0 116 dataBufferView = newBufferView;
michael@0 117 }
michael@0 118 else {
michael@0 119 dataBuffer = receivedData;
michael@0 120 dataBufferView = new Uint8Array(dataBuffer);
michael@0 121 }
michael@0 122 }
michael@0 123 else {
michael@0 124 dataBuffer = dataBuffer.concat(receivedData);
michael@0 125 }
michael@0 126 do_print(name + ' received ' + recvLength + ' bytes');
michael@0 127
michael@0 128 if (done)
michael@0 129 do_throw(name + ' Received data event when already done!');
michael@0 130
michael@0 131 let dataView = dataBuffer.byteLength !== undefined ? new Uint8Array(dataBuffer) : dataBuffer;
michael@0 132 if (dataView.length >= expectedData.length) {
michael@0 133 // check the bytes are equivalent
michael@0 134 for (let i = 0; i < expectedData.length; i++) {
michael@0 135 if (dataView[i] !== expectedData[i]) {
michael@0 136 do_throw(name + ' Received mismatched character at position ' + i);
michael@0 137 }
michael@0 138 }
michael@0 139 if (dataView.length > expectedData.length)
michael@0 140 do_throw(name + ' Received ' + dataView.length + ' bytes but only expected ' +
michael@0 141 expectedData.length + ' bytes.');
michael@0 142
michael@0 143 done = true;
michael@0 144 if (callback) {
michael@0 145 callback();
michael@0 146 } else {
michael@0 147 run_next_test();
michael@0 148 }
michael@0 149 }
michael@0 150 };
michael@0 151 }
michael@0 152
michael@0 153 var server = null, sock = null, connectedsock = null, failure_drain = null;
michael@0 154 var count = 0;
michael@0 155 /**
michael@0 156 *
michael@0 157 * Test functions
michael@0 158 *
michael@0 159 */
michael@0 160
michael@0 161 /**
michael@0 162 * Connect the socket to the server. This test is added as the first
michael@0 163 * test, and is also added after every test which results in the socket
michael@0 164 * being closed.
michael@0 165 */
michael@0 166
michael@0 167 function connectSock() {
michael@0 168 if (server) {
michael@0 169 server.close();
michael@0 170 }
michael@0 171
michael@0 172 var yayFuncs = makeJointSuccess(['serveropen', 'clientopen']);
michael@0 173 var options = { binaryType: 'arraybuffer' };
michael@0 174
michael@0 175 server = TCPSocket.listen(PORT, options, BACKLOG);
michael@0 176 server.onconnect = function(socket) {
michael@0 177 connectedsock = socket;
michael@0 178 connectedsock.ondata = makeFailureCase('serverdata');
michael@0 179 connectedsock.onerror = makeFailureCase('servererror');
michael@0 180 connectedsock.onclose = makeFailureCase('serverclose');
michael@0 181 yayFuncs.serveropen();
michael@0 182 };
michael@0 183 server.onerror = makeFailureCase('error');
michael@0 184 sock = TCPSocket.open(
michael@0 185 '127.0.0.1', PORT, options);
michael@0 186 sock.onopen = yayFuncs.clientopen;
michael@0 187 sock.ondrain = null;
michael@0 188 sock.ondata = makeFailureCase('data');
michael@0 189 sock.onerror = makeFailureCase('error');
michael@0 190 sock.onclose = makeFailureCase('close');
michael@0 191 }
michael@0 192
michael@0 193 /**
michael@0 194 * Connect the socket to the server after the server was closed.
michael@0 195 * This test is added after test to close the server was conducted.
michael@0 196 */
michael@0 197 function openSockInClosingServer() {
michael@0 198 var success = makeSuccessCase('clientnotopen');
michael@0 199 var options = { binaryType: 'arraybuffer' };
michael@0 200
michael@0 201 sock = TCPSocket.open(
michael@0 202 '127.0.0.1', PORT, options);
michael@0 203
michael@0 204 sock.onopen = makeFailureCase('open');
michael@0 205 sock.onerror = success;
michael@0 206 }
michael@0 207
michael@0 208 /**
michael@0 209 * Test that sending a small amount of data works, and that buffering
michael@0 210 * does not take place for this small amount of data.
michael@0 211 */
michael@0 212
michael@0 213 function sendDataToServer() {
michael@0 214 connectedsock.ondata = makeExpectData('serverdata', DATA_ARRAY, true);
michael@0 215 if (!sock.send(DATA_ARRAY_BUFFER)) {
michael@0 216 do_throw("send should not have buffered such a small amount of data");
michael@0 217 }
michael@0 218 }
michael@0 219
michael@0 220 /**
michael@0 221 * Test that data sent from the server correctly fires the ondata
michael@0 222 * callback on the client side.
michael@0 223 */
michael@0 224
michael@0 225 function receiveDataFromServer() {
michael@0 226 connectedsock.ondata = makeFailureCase('serverdata');
michael@0 227 sock.ondata = makeExpectData('data', DATA_ARRAY, true);
michael@0 228
michael@0 229 connectedsock.send(DATA_ARRAY_BUFFER);
michael@0 230 }
michael@0 231
michael@0 232 /**
michael@0 233 * Test that when the server closes the connection, the onclose callback
michael@0 234 * is fired on the client side.
michael@0 235 */
michael@0 236
michael@0 237 function serverCloses() {
michael@0 238 // we don't really care about the server's close event, but we do want to
michael@0 239 // make sure it happened for sequencing purposes.
michael@0 240 sock.ondata = makeFailureCase('data');
michael@0 241 sock.onclose = makeFailureCase('close1');
michael@0 242 connectedsock.onclose = makeFailureCase('close2');
michael@0 243
michael@0 244 server.close();
michael@0 245 run_next_test();
michael@0 246 }
michael@0 247
michael@0 248 /**
michael@0 249 * Test that when the client closes the connection, the onclose callback
michael@0 250 * is fired on the server side.
michael@0 251 */
michael@0 252
michael@0 253 function cleanup() {
michael@0 254 do_print("Cleaning up");
michael@0 255 sock.onclose = null;
michael@0 256 connectedsock.onclose = null;
michael@0 257
michael@0 258 server.close();
michael@0 259 sock.close();
michael@0 260 if (count == 1){
michael@0 261 if (!gInChild)
michael@0 262 Services.prefs.clearUserPref('dom.mozTCPSocket.enabled');
michael@0 263 }
michael@0 264 count++;
michael@0 265 run_next_test();
michael@0 266 }
michael@0 267 // - connect, data and events work both ways
michael@0 268 add_test(connectSock);
michael@0 269
michael@0 270 add_test(sendDataToServer);
michael@0 271
michael@0 272 add_test(receiveDataFromServer);
michael@0 273 // - server closes on us
michael@0 274 add_test(serverCloses);
michael@0 275
michael@0 276 // - send and receive after closing server
michael@0 277 add_test(sendDataToServer);
michael@0 278 add_test(receiveDataFromServer);
michael@0 279 // - check a connection refused from client to server after closing server
michael@0 280 add_test(serverCloses);
michael@0 281
michael@0 282 add_test(openSockInClosingServer);
michael@0 283
michael@0 284 // - clean up
michael@0 285 add_test(cleanup);
michael@0 286
michael@0 287 // - send and receive in reverse order for client and server
michael@0 288 add_test(connectSock);
michael@0 289
michael@0 290 add_test(receiveDataFromServer);
michael@0 291
michael@0 292 add_test(sendDataToServer);
michael@0 293
michael@0 294 // - clean up
michael@0 295
michael@0 296 add_test(cleanup);
michael@0 297
michael@0 298 function run_test() {
michael@0 299 if (!gInChild)
michael@0 300 Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true);
michael@0 301
michael@0 302 run_next_test();
michael@0 303 }

mercurial