dom/network/tests/unit/test_tcpserversocket.js

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial