diff options
Diffstat (limited to 'netwerk/test/unit/test_net_addr.js')
-rw-r--r-- | netwerk/test/unit/test_net_addr.js | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/netwerk/test/unit/test_net_addr.js b/netwerk/test/unit/test_net_addr.js new file mode 100644 index 0000000000..331fa9bc74 --- /dev/null +++ b/netwerk/test/unit/test_net_addr.js @@ -0,0 +1,216 @@ +"use strict"; + +var CC = Components.Constructor; + +const ServerSocket = CC( + "@mozilla.org/network/server-socket;1", + "nsIServerSocket", + "init" +); + +/** + * TestServer: A single instance of this is created as |serv|. When created, + * it starts listening on the loopback address on port |serv.port|. Tests will + * connect to it after setting |serv.acceptCallback|, which is invoked after it + * accepts a connection. + * + * Within |serv.acceptCallback|, various properties of |serv| can be used to + * run checks. After the callback, the connection is closed, but the server + * remains listening until |serv.stop| + * + * Note: TestServer can only handle a single connection at a time. Tests + * should use run_next_test at the end of |serv.acceptCallback| to start the + * following test which creates a connection. + */ +function TestServer() { + this.reset(); + + // start server. + // any port (-1), loopback only (true), default backlog (-1) + this.listener = ServerSocket(-1, true, -1); + this.port = this.listener.port; + info("server: listening on " + this.port); + this.listener.asyncListen(this); +} + +TestServer.prototype = { + onSocketAccepted(socket, trans) { + info("server: got client connection"); + + // one connection at a time. + if (this.input !== null) { + try { + socket.close(); + } catch (ignore) {} + do_throw("Test written to handle one connection at a time."); + } + + try { + this.input = trans.openInputStream(0, 0, 0); + this.output = trans.openOutputStream(0, 0, 0); + this.selfAddr = trans.getScriptableSelfAddr(); + this.peerAddr = trans.getScriptablePeerAddr(); + + this.acceptCallback(); + } catch (e) { + /* In a native callback such as onSocketAccepted, exceptions might not + * get output correctly or logged to test output. Send them through + * do_throw, which fails the test immediately. */ + do_report_unexpected_exception(e, "in TestServer.onSocketAccepted"); + } + + this.reset(); + }, + + onStopListening(socket) {}, + + /** + * Called to close a connection and clean up properties. + */ + reset() { + if (this.input) { + try { + this.input.close(); + } catch (ignore) {} + } + if (this.output) { + try { + this.output.close(); + } catch (ignore) {} + } + + this.input = null; + this.output = null; + this.acceptCallback = null; + this.selfAddr = null; + this.peerAddr = null; + }, + + /** + * Cleanup for TestServer and this test case. + */ + stop() { + this.reset(); + try { + this.listener.close(); + } catch (ignore) {} + }, +}; + +/** + * Helper function. + * Compares two nsINetAddr objects and ensures they are logically equivalent. + */ +function checkAddrEqual(lhs, rhs) { + Assert.equal(lhs.family, rhs.family); + + if (lhs.family === Ci.nsINetAddr.FAMILY_INET) { + Assert.equal(lhs.address, rhs.address); + Assert.equal(lhs.port, rhs.port); + } + + /* TODO: fully support ipv6 and local */ +} + +/** + * An instance of SocketTransportService, used to create connections. + */ +var sts; + +/** + * Single instance of TestServer + */ +var serv; + +/** + * A place for individual tests to place Objects of importance for access + * throughout asynchronous testing. Particularly important for any output or + * input streams opened, as cleanup of those objects (by the garbage collector) + * causes the stream to close and may have other side effects. + */ +var testDataStore = null; + +/** + * IPv4 test. + */ +function testIpv4() { + testDataStore = { + transport: null, + ouput: null, + }; + + serv.acceptCallback = function () { + // disable the timeoutCallback + serv.timeoutCallback = function () {}; + + var selfAddr = testDataStore.transport.getScriptableSelfAddr(); + var peerAddr = testDataStore.transport.getScriptablePeerAddr(); + + // check peerAddr against expected values + Assert.equal(peerAddr.family, Ci.nsINetAddr.FAMILY_INET); + Assert.equal(peerAddr.port, testDataStore.transport.port); + Assert.equal(peerAddr.port, serv.port); + Assert.equal(peerAddr.address, "127.0.0.1"); + + // check selfAddr against expected values + Assert.equal(selfAddr.family, Ci.nsINetAddr.FAMILY_INET); + Assert.equal(selfAddr.address, "127.0.0.1"); + + // check that selfAddr = server.peerAddr and vice versa. + checkAddrEqual(selfAddr, serv.peerAddr); + checkAddrEqual(peerAddr, serv.selfAddr); + + testDataStore = null; + executeSoon(run_next_test); + }; + + // Useful timeout for debugging test hangs + /*serv.timeoutCallback = function(tname) { + if (tname === 'testIpv4') + do_throw('testIpv4 never completed a connection to TestServ'); + }; + do_timeout(connectTimeout, function(){ serv.timeoutCallback('testIpv4'); });*/ + + testDataStore.transport = sts.createTransport( + [], + "127.0.0.1", + serv.port, + null, + null + ); + /* + * Need to hold |output| so that the output stream doesn't close itself and + * the associated connection. + */ + testDataStore.output = testDataStore.transport.openOutputStream( + Ci.nsITransport.OPEN_BLOCKING, + 0, + 0 + ); + + /* NEXT: + * openOutputStream -> onSocketAccepted -> acceptedCallback -> run_next_test + * OR (if the above timeout is uncommented) + * <connectTimeout lapses> -> timeoutCallback -> do_throw + */ +} + +/** + * Running the tests. + */ +function run_test() { + sts = Cc["@mozilla.org/network/socket-transport-service;1"].getService( + Ci.nsISocketTransportService + ); + serv = new TestServer(); + + registerCleanupFunction(function () { + serv.stop(); + }); + + add_test(testIpv4); + /* TODO: testIpv6 */ + /* TODO: testLocal */ + + run_next_test(); +} |