1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/dom/network/tests/unit/test_tcpserversocket.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,303 @@ 1.4 +/** 1.5 + * Test TCPSocket.js by creating an XPCOM-style server socket, then sending 1.6 + * data in both directions and making sure each side receives their data 1.7 + * correctly and with the proper events. 1.8 + * 1.9 + * This test is derived from netwerk/test/unit/test_socks.js, except we don't 1.10 + * involve a subprocess. 1.11 + * 1.12 + * Future work: 1.13 + * - SSL. see https://bugzilla.mozilla.org/show_bug.cgi?id=466524 1.14 + * https://bugzilla.mozilla.org/show_bug.cgi?id=662180 1.15 + * Alternatively, mochitests could be used. 1.16 + * - Testing overflow logic. 1.17 + * 1.18 + **/ 1.19 + 1.20 +const Cc = Components.classes; 1.21 +const Ci = Components.interfaces; 1.22 +const Cr = Components.results; 1.23 +const Cu = Components.utils; 1.24 +const CC = Components.Constructor; 1.25 + 1.26 +/** 1.27 + * 1.28 + * Constants 1.29 + * 1.30 + */ 1.31 + 1.32 +// Test parameter. 1.33 +const PORT = 8085; 1.34 +const BACKLOG = -1; 1.35 + 1.36 +// Some binary data to send. 1.37 +const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0], 1.38 + DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length), 1.39 + TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER), 1.40 + HELLO_WORLD = "hlo wrld. ", 1.41 + BIG_ARRAY = new Array(65539); 1.42 + 1.43 +TYPED_DATA_ARRAY.set(DATA_ARRAY, 0); 1.44 + 1.45 +for (var i_big = 0; i_big < BIG_ARRAY.length; i_big++) { 1.46 + BIG_ARRAY[i_big] = Math.floor(Math.random() * 256); 1.47 +} 1.48 + 1.49 +const BIG_ARRAY_BUFFER = new ArrayBuffer(BIG_ARRAY.length); 1.50 +const BIG_TYPED_ARRAY = new Uint8Array(BIG_ARRAY_BUFFER); 1.51 +BIG_TYPED_ARRAY.set(BIG_ARRAY); 1.52 + 1.53 +const TCPSocket = new (CC("@mozilla.org/tcp-socket;1", 1.54 + "nsIDOMTCPSocket"))(); 1.55 + 1.56 +const gInChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime) 1.57 + .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT; 1.58 + 1.59 +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); 1.60 +Cu.import("resource://gre/modules/Services.jsm"); 1.61 + 1.62 +/** 1.63 + * 1.64 + * Helper functions 1.65 + * 1.66 + */ 1.67 + 1.68 + 1.69 +function makeSuccessCase(name) { 1.70 + return function() { 1.71 + do_print('got expected: ' + name); 1.72 + run_next_test(); 1.73 + }; 1.74 +} 1.75 + 1.76 +function makeJointSuccess(names) { 1.77 + let funcs = {}, successCount = 0; 1.78 + names.forEach(function(name) { 1.79 + funcs[name] = function() { 1.80 + do_print('got expected: ' + name); 1.81 + if (++successCount === names.length) 1.82 + run_next_test(); 1.83 + }; 1.84 + }); 1.85 + return funcs; 1.86 +} 1.87 + 1.88 +function makeFailureCase(name) { 1.89 + return function() { 1.90 + let argstr; 1.91 + if (arguments.length) { 1.92 + argstr = '(args: ' + 1.93 + Array.map(arguments, function(x) { return x.data + ""; }).join(" ") + ')'; 1.94 + } 1.95 + else { 1.96 + argstr = '(no arguments)'; 1.97 + } 1.98 + do_throw('got unexpected: ' + name + ' ' + argstr); 1.99 + }; 1.100 +} 1.101 + 1.102 +function makeExpectData(name, expectedData, fromEvent, callback) { 1.103 + let dataBuffer = fromEvent ? null : [], done = false; 1.104 + let dataBufferView = null; 1.105 + return function(receivedData) { 1.106 + if (receivedData.data) { 1.107 + receivedData = receivedData.data; 1.108 + } 1.109 + let recvLength = receivedData.byteLength !== undefined ? 1.110 + receivedData.byteLength : receivedData.length; 1.111 + 1.112 + if (fromEvent) { 1.113 + if (dataBuffer) { 1.114 + let newBuffer = new ArrayBuffer(dataBuffer.byteLength + recvLength); 1.115 + let newBufferView = new Uint8Array(newBuffer); 1.116 + newBufferView.set(dataBufferView, 0); 1.117 + newBufferView.set(receivedData, dataBuffer.byteLength); 1.118 + dataBuffer = newBuffer; 1.119 + dataBufferView = newBufferView; 1.120 + } 1.121 + else { 1.122 + dataBuffer = receivedData; 1.123 + dataBufferView = new Uint8Array(dataBuffer); 1.124 + } 1.125 + } 1.126 + else { 1.127 + dataBuffer = dataBuffer.concat(receivedData); 1.128 + } 1.129 + do_print(name + ' received ' + recvLength + ' bytes'); 1.130 + 1.131 + if (done) 1.132 + do_throw(name + ' Received data event when already done!'); 1.133 + 1.134 + let dataView = dataBuffer.byteLength !== undefined ? new Uint8Array(dataBuffer) : dataBuffer; 1.135 + if (dataView.length >= expectedData.length) { 1.136 + // check the bytes are equivalent 1.137 + for (let i = 0; i < expectedData.length; i++) { 1.138 + if (dataView[i] !== expectedData[i]) { 1.139 + do_throw(name + ' Received mismatched character at position ' + i); 1.140 + } 1.141 + } 1.142 + if (dataView.length > expectedData.length) 1.143 + do_throw(name + ' Received ' + dataView.length + ' bytes but only expected ' + 1.144 + expectedData.length + ' bytes.'); 1.145 + 1.146 + done = true; 1.147 + if (callback) { 1.148 + callback(); 1.149 + } else { 1.150 + run_next_test(); 1.151 + } 1.152 + } 1.153 + }; 1.154 +} 1.155 + 1.156 +var server = null, sock = null, connectedsock = null, failure_drain = null; 1.157 +var count = 0; 1.158 +/** 1.159 + * 1.160 + * Test functions 1.161 + * 1.162 + */ 1.163 + 1.164 +/** 1.165 + * Connect the socket to the server. This test is added as the first 1.166 + * test, and is also added after every test which results in the socket 1.167 + * being closed. 1.168 + */ 1.169 + 1.170 +function connectSock() { 1.171 + if (server) { 1.172 + server.close(); 1.173 + } 1.174 + 1.175 + var yayFuncs = makeJointSuccess(['serveropen', 'clientopen']); 1.176 + var options = { binaryType: 'arraybuffer' }; 1.177 + 1.178 + server = TCPSocket.listen(PORT, options, BACKLOG); 1.179 + server.onconnect = function(socket) { 1.180 + connectedsock = socket; 1.181 + connectedsock.ondata = makeFailureCase('serverdata'); 1.182 + connectedsock.onerror = makeFailureCase('servererror'); 1.183 + connectedsock.onclose = makeFailureCase('serverclose'); 1.184 + yayFuncs.serveropen(); 1.185 + }; 1.186 + server.onerror = makeFailureCase('error'); 1.187 + sock = TCPSocket.open( 1.188 + '127.0.0.1', PORT, options); 1.189 + sock.onopen = yayFuncs.clientopen; 1.190 + sock.ondrain = null; 1.191 + sock.ondata = makeFailureCase('data'); 1.192 + sock.onerror = makeFailureCase('error'); 1.193 + sock.onclose = makeFailureCase('close'); 1.194 +} 1.195 + 1.196 +/** 1.197 + * Connect the socket to the server after the server was closed. 1.198 + * This test is added after test to close the server was conducted. 1.199 + */ 1.200 +function openSockInClosingServer() { 1.201 + var success = makeSuccessCase('clientnotopen'); 1.202 + var options = { binaryType: 'arraybuffer' }; 1.203 + 1.204 + sock = TCPSocket.open( 1.205 + '127.0.0.1', PORT, options); 1.206 + 1.207 + sock.onopen = makeFailureCase('open'); 1.208 + sock.onerror = success; 1.209 +} 1.210 + 1.211 +/** 1.212 + * Test that sending a small amount of data works, and that buffering 1.213 + * does not take place for this small amount of data. 1.214 + */ 1.215 + 1.216 +function sendDataToServer() { 1.217 + connectedsock.ondata = makeExpectData('serverdata', DATA_ARRAY, true); 1.218 + if (!sock.send(DATA_ARRAY_BUFFER)) { 1.219 + do_throw("send should not have buffered such a small amount of data"); 1.220 + } 1.221 +} 1.222 + 1.223 +/** 1.224 + * Test that data sent from the server correctly fires the ondata 1.225 + * callback on the client side. 1.226 + */ 1.227 + 1.228 +function receiveDataFromServer() { 1.229 + connectedsock.ondata = makeFailureCase('serverdata'); 1.230 + sock.ondata = makeExpectData('data', DATA_ARRAY, true); 1.231 + 1.232 + connectedsock.send(DATA_ARRAY_BUFFER); 1.233 +} 1.234 + 1.235 +/** 1.236 + * Test that when the server closes the connection, the onclose callback 1.237 + * is fired on the client side. 1.238 + */ 1.239 + 1.240 +function serverCloses() { 1.241 + // we don't really care about the server's close event, but we do want to 1.242 + // make sure it happened for sequencing purposes. 1.243 + sock.ondata = makeFailureCase('data'); 1.244 + sock.onclose = makeFailureCase('close1'); 1.245 + connectedsock.onclose = makeFailureCase('close2'); 1.246 + 1.247 + server.close(); 1.248 + run_next_test(); 1.249 +} 1.250 + 1.251 +/** 1.252 + * Test that when the client closes the connection, the onclose callback 1.253 + * is fired on the server side. 1.254 + */ 1.255 + 1.256 +function cleanup() { 1.257 + do_print("Cleaning up"); 1.258 + sock.onclose = null; 1.259 + connectedsock.onclose = null; 1.260 + 1.261 + server.close(); 1.262 + sock.close(); 1.263 + if (count == 1){ 1.264 + if (!gInChild) 1.265 + Services.prefs.clearUserPref('dom.mozTCPSocket.enabled'); 1.266 + } 1.267 + count++; 1.268 + run_next_test(); 1.269 +} 1.270 +// - connect, data and events work both ways 1.271 +add_test(connectSock); 1.272 + 1.273 +add_test(sendDataToServer); 1.274 + 1.275 +add_test(receiveDataFromServer); 1.276 +// - server closes on us 1.277 +add_test(serverCloses); 1.278 + 1.279 +// - send and receive after closing server 1.280 +add_test(sendDataToServer); 1.281 +add_test(receiveDataFromServer); 1.282 +// - check a connection refused from client to server after closing server 1.283 +add_test(serverCloses); 1.284 + 1.285 +add_test(openSockInClosingServer); 1.286 + 1.287 +// - clean up 1.288 +add_test(cleanup); 1.289 + 1.290 +// - send and receive in reverse order for client and server 1.291 +add_test(connectSock); 1.292 + 1.293 +add_test(receiveDataFromServer); 1.294 + 1.295 +add_test(sendDataToServer); 1.296 + 1.297 +// - clean up 1.298 + 1.299 +add_test(cleanup); 1.300 + 1.301 +function run_test() { 1.302 + if (!gInChild) 1.303 + Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true); 1.304 + 1.305 + run_next_test(); 1.306 +}