Chromium Code Reviews| Index: tests/standalone/io/raw_synchronous_socket_test.dart |
| diff --git a/tests/standalone/io/raw_synchronous_socket_test.dart b/tests/standalone/io/raw_synchronous_socket_test.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..578bb93cafb0c9448a7b910268153bd9a9566ac1 |
| --- /dev/null |
| +++ b/tests/standalone/io/raw_synchronous_socket_test.dart |
| @@ -0,0 +1,458 @@ |
| +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
|
zra
2017/04/07 16:29:42
2017
bkonyi
2017/04/10 19:20:06
Done.
|
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| +// |
| +// VMOptions= |
|
zra
2017/04/07 16:29:41
Remove this if there aren't any.
bkonyi
2017/04/10 19:20:06
Done.
|
| + |
| +import "dart:async"; |
| +import "dart:io"; |
| +import "dart:isolate"; |
| +import "dart:math"; |
| + |
| +import "package:async_helper/async_helper.dart"; |
| +import "package:expect/expect.dart"; |
| + |
| +const String LOOPBACK_IP_V4_STRING = "127.0.0.1"; |
| + |
| +void testArguments() { |
|
zra
2017/04/07 16:29:42
How about null?
bkonyi
2017/04/10 19:20:07
Done.
|
| + Expect.throws( |
| + () => RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, 65536)); |
| + Expect.throws( |
| + () => RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, -1)); |
| + Expect.throws(() => |
| + RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, 0, backlog: -1)); |
|
zra
2017/04/07 16:29:42
what is backlog?
bkonyi
2017/04/10 19:20:06
Something that came from the RawSocket tests. I co
|
| +} |
|
zra
2017/04/07 16:29:42
We should probably have more tests with bogus argu
bkonyi
2017/04/10 19:20:07
Done.
|
| + |
| +void testInvalidConnect() { |
| + // Connect to an unknown DNS name. |
| + try { |
| + var socket = RawSynchronousSocket.connectSync("ko.faar.__hest__", 0); |
| + Expect.fail("Failure expected"); |
| + } catch (e) { |
| + Expect.isTrue(e is SocketException); |
| + } |
| + |
| + // Connect to an unavaliable IP-address. |
| + try { |
| + var socket = RawSynchronousSocket.connectSync("1.2.3.4", 0); |
| + Expect.fail("Failure expected"); |
| + } catch (e) { |
| + Expect.isTrue(e is SocketException); |
| + } |
| + ; |
| +} |
| + |
| +void testSimpleConnect() { |
| + asyncStart(); |
| + RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) { |
| + var socket = |
| + RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port); |
| + server.listen((serverSocket) { |
| + Expect.equals(socket.address, serverSocket.remoteAddress); |
| + Expect.equals(socket.port, serverSocket.remotePort); |
| + Expect.equals(socket.remoteAddress, server.address); |
| + Expect.equals(socket.remotePort, server.port); |
| + socket.closeSync(); |
| + server.close(); |
| + asyncEnd(); |
| + }); |
| + }); |
| +} |
| + |
| +void testServerListenAfterConnect() { |
| + asyncStart(); |
| + RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) { |
| + Expect.isTrue(server.port > 0); |
| + var client = |
| + RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port); |
| + server.listen((socket) { |
| + client.closeSync(); |
| + server.close(); |
| + socket.close(); |
| + asyncEnd(); |
| + }); |
| + }); |
| +} |
| + |
| +const messageSize = 1000; |
| +// Configuration fields for the EchoServer. |
| +enum EchoServerTypes { |
| + // Max accumulated connections to server before close. Defaults to 1. |
| + CONNECTION_COUNT, |
| + // Sets the range of the fields to check in the list generated by |
| + // createTestData(). |
| + OFFSET_END, |
| + OFFSET_START, |
| + // The port used to communicate with an isolate. |
| + ISOLATE_SEND_PORT, |
| + // The port of the newly created echo server. |
| + SERVER_PORT |
| +} |
| + |
| +List<int> createTestData() { |
| + return new List<int>.generate(messageSize, (index) => index & 0xff); |
| +} |
| + |
| +// Consumes data generated by a test and compares it against the original test |
| +// data. The optional fields, start and end, are used to compare against |
| +// segments of the original test data list. In other words, data.length == (end |
| +// - start). |
| +void verifyTestData(List<int> data, [int start = 0, int end]) { |
| + assert(data != null); |
| + List<int> expected = createTestData(); |
| + if (end == null) { |
| + end = data.length; |
| + } |
| + end = min(messageSize, end); |
| + Expect.equals(end - start, data.length); |
| + for (int i = 0; i < (end - start); i++) { |
| + Expect.equals(expected[start + i], data[i]); |
| + } |
| +} |
| + |
| +// The echo server is spawned in a new isolate and is used to test various |
| +// synchronous read/write operations by echoing any data received back to the |
| +// sender. The server should shutdown automatically after a specified number of |
| +// socket disconnections (default: 1). |
| +Future echoServer(var sendPort) async { |
| + RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) async { |
| + ReceivePort receivePort = new ReceivePort(); |
| + Map response = { |
| + EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort, |
| + EchoServerTypes.SERVER_PORT: server.port |
| + }; |
| + sendPort.send(response); |
| + Map limits = await receivePort.first; |
| + int start = limits[EchoServerTypes.OFFSET_START]; |
| + int end = limits[EchoServerTypes.OFFSET_END]; |
| + int length = end - start; |
| + int connection_count = limits[EchoServerTypes.CONNECTION_COUNT] ?? 1; |
| + int connections = 0; |
| + sendPort = limits[EchoServerTypes.ISOLATE_SEND_PORT]; |
| + server.listen((client) { |
| + int bytesRead = 0; |
| + int bytesWritten = 0; |
| + bool closedEventReceived = false; |
| + List<int> data = new List<int>(length); |
| + client.writeEventsEnabled = false; |
| + client.listen((event) { |
| + switch (event) { |
| + case RawSocketEvent.READ: |
| + Expect.isTrue(bytesWritten == 0); |
| + Expect.isTrue(client.available() > 0); |
| + var buffer = client.read(client.available()); |
| + data.setRange(bytesRead, bytesRead + buffer.length, buffer); |
| + bytesRead += buffer.length; |
| + // Once we've read all the data, we can echo it back. Otherwise, |
| + // keep waiting for more bytes. |
| + if (bytesRead >= length) { |
| + verifyTestData(data, start, end); |
| + client.writeEventsEnabled = true; |
| + } |
| + break; |
| + case RawSocketEvent.WRITE: |
| + Expect.isFalse(client.writeEventsEnabled); |
| + bytesWritten += |
| + client.write(data, bytesWritten, data.length - bytesWritten); |
| + if (bytesWritten < length) { |
| + client.writeEventsEnabled = true; |
| + } else if (bytesWritten == length) { |
| + // Close the socket for writing from the server since we're done |
| + // writing to this socket. The connection is closed completely |
| + // after the client closes the socket for reading from the server. |
| + client.shutdown(SocketDirection.SEND); |
| + } |
| + break; |
| + case RawSocketEvent.READ_CLOSED: |
| + client.close(); |
| + break; |
| + case RawSocketEvent.CLOSED: |
| + Expect.isFalse(closedEventReceived); |
| + closedEventReceived = true; |
| + break; |
| + default: |
| + throw "Unexpected event $event"; |
| + } |
| + }, onDone: () { |
| + Expect.isTrue(closedEventReceived); |
| + connections++; |
| + if (connections >= connection_count) { |
| + server.close(); |
| + } |
| + }); |
| + }, onDone: () { |
| + // Let the client know we're shutting down then kill the isolate. |
| + sendPort.send(null); |
| + kill(); |
| + }); |
| + }); |
| +} |
| + |
| +Future testSimpleReadWrite({bool dropReads}) async { |
| + asyncStart(); |
| + // This test creates an server and a client connects. The client writes data |
| + // to the socket and the server echos it back. The client confirms the data it |
| + // reads is the same as the data sent, then closes the socket, resulting in |
| + // the closing of the server, which responds on receivePort with null to |
| + // specify the echo server isolate is about to be killed. If an error occurs |
| + // in the echo server, the exception and stack trace are sent to receivePort, |
| + // which prints the exception and stack trace before eventually throwing an |
| + // error. |
| + ReceivePort receivePort = new ReceivePort(); |
| + Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort); |
| + |
| + Map response = await receivePort.first; |
| + SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT]; |
| + int serverInternetPort = response[EchoServerTypes.SERVER_PORT]; |
| + |
| + receivePort = new ReceivePort(); |
| + echo.addErrorListener(receivePort.sendPort); |
| + |
| + Map limits = { |
| + EchoServerTypes.OFFSET_START: 0, |
| + EchoServerTypes.OFFSET_END: messageSize, |
| + EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort |
| + }; |
| + sendPort.send(limits); |
| + |
| + try { |
| + var socket = RawSynchronousSocket.connectSync( |
| + LOOPBACK_IP_V4_STRING, serverInternetPort); |
| + List<int> data = createTestData(); |
| + socket.writeFromSync(data); |
| + List<int> result = socket.readSync(data.length); |
| + verifyTestData(result); |
| + socket.shutdown(SocketDirection.SEND); |
| + socket.closeSync(); |
| + } catch (e, stack) { |
|
zra
2017/04/07 16:29:42
print("Echo test failed in the client");
rethrow;
bkonyi
2017/04/10 19:20:06
Done.
|
| + throw "Echo test failed in client!\nError: ${e}\nStack trace: ${stack}"; |
| + } |
| + // Wait for the server to shutdown before finishing the test. |
| + var result = await receivePort.first; |
| + if (result != null) { |
| + throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" + |
| + " ${result[1]}"; |
| + } |
| + asyncEnd(); |
| +} |
| + |
| +Future testPartialRead() async { |
| + asyncStart(); |
| + // This test creates an server and a client connects. The client writes data |
| + // to the socket and the server echos it back. The client confirms the data it |
| + // reads is the same as the data sent, then closes the socket, resulting in |
| + // the closing of the server, which responds on receivePort with null to |
| + // specify the echo server isolate is about to be killed. If an error occurs |
| + // in the echo server, the exception and stack trace are sent to receivePort, |
| + // which prints the exception and stack trace before eventually throwing an |
| + // error. |
| + |
| + ReceivePort receivePort = new ReceivePort(); |
| + Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort); |
| + |
| + Map response = await receivePort.first; |
| + SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT]; |
| + int serverInternetPort = response[EchoServerTypes.SERVER_PORT]; |
| + List<int> data = createTestData(); |
| + |
| + receivePort = new ReceivePort(); |
| + echo.addErrorListener(receivePort.sendPort); |
| + |
| + Map limits = { |
| + EchoServerTypes.OFFSET_START: 0, |
| + EchoServerTypes.OFFSET_END: 1000, |
| + EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort |
| + }; |
| + sendPort.send(limits); |
| + |
| + try { |
| + var socket = RawSynchronousSocket.connectSync( |
| + LOOPBACK_IP_V4_STRING, serverInternetPort); |
| + int half_length = (data.length / 2).toInt(); |
| + |
| + // Send the full data list to the server. |
| + socket.writeFromSync(data); |
| + List<int> result = new List<int>(data.length); |
| + |
| + // Read half at a time and check that there's still more bytes available. |
| + socket.readIntoSync(result, 0, half_length); |
| + verifyTestData(result.sublist(0, half_length), 0, half_length); |
| + Expect.isTrue(socket.available() == (data.length - half_length)); |
| + |
| + // Read the second half and verify again. |
| + socket.readIntoSync(result, half_length); |
| + verifyTestData(result); |
| + Expect.isTrue(socket.available() == 0); |
| + |
| + socket.closeSync(); |
| + } catch (e, stack) { |
| + throw "Echo test failed in client!\nError: ${e}\nStack trace: ${stack}"; |
| + } |
| + // Wait for the server to shutdown before finishing the test. |
| + var result = await receivePort.first; |
| + if (result != null) { |
| + throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" + |
| + " ${result[1]}"; |
| + } |
| + asyncEnd(); |
| +} |
| + |
| +Future testPartialWrite() async { |
| + asyncStart(); |
| + // This test creates an server and a client connects. The client writes data |
| + // to the socket and the server echos it back. The client confirms the data it |
| + // reads is the same as the data sent, then closes the socket, resulting in |
| + // the closing of the server, which responds on receivePort with null to |
| + // specify the echo server isolate is about to be killed. If an error occurs |
| + // in the echo server, the exception and stack trace are sent to receivePort, |
| + // which prints the exception and stack trace before eventually throwing an |
| + // error. |
| + |
| + ReceivePort receivePort = new ReceivePort(); |
| + Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort); |
| + |
| + Map response = await receivePort.first; |
| + List<int> data = createTestData(); |
| + SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT]; |
| + int startOffset = 32; |
| + int endOffset = (data.length / 2).toInt(); |
| + int serverInternetPort = response[EchoServerTypes.SERVER_PORT]; |
| + |
| + receivePort = new ReceivePort(); |
| + echo.addErrorListener(receivePort.sendPort); |
| + |
| + Map limits = { |
| + EchoServerTypes.OFFSET_START: startOffset, |
| + EchoServerTypes.OFFSET_END: endOffset, |
| + EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort |
| + }; |
| + sendPort.send(limits); |
| + try { |
| + var socket = RawSynchronousSocket.connectSync( |
| + LOOPBACK_IP_V4_STRING, serverInternetPort); |
| + List<int> data = createTestData(); |
| + |
| + // Write a subset of data to the server. |
| + socket.writeFromSync(data, startOffset, endOffset); |
| + |
| + // Grab the response and verify it's correct. |
| + List<int> result = new List<int>(endOffset - startOffset); |
| + socket.readIntoSync(result); |
| + |
| + Expect.equals(result.length, endOffset - startOffset); |
| + verifyTestData(result, startOffset, endOffset); |
| + socket.closeSync(); |
| + } catch (e, stack) { |
| + throw "Echo test failed in client!\nError: ${e}\nStack trace: ${stack}"; |
| + } |
| + |
| + // Wait for the server to shutdown before finishing the test. |
| + var result = await receivePort.first; |
| + if (result != null) { |
| + throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" + |
| + " ${result[1]}"; |
| + } |
| + asyncEnd(); |
| +} |
| + |
| +Future testShutdown() async { |
| + asyncStart(); |
| + // This test creates an server and a client connects. The client writes data |
| + // to the socket and the server echos it back. The client confirms the data it |
| + // reads is the same as the data sent, then closes the socket, resulting in |
| + // the closing of the server, which responds on receivePort with null to |
| + // specify the echo server isolate is about to be killed. If an error occurs |
| + // in the echo server, the exception and stack trace are sent to receivePort, |
| + // which prints the exception and stack trace before eventually throwing an |
| + // error. |
| + |
| + ReceivePort receivePort = new ReceivePort(); |
| + Isolate echo = await Isolate.spawn(echoServer, receivePort.sendPort); |
| + |
| + Map response = await receivePort.first; |
| + SendPort sendPort = response[EchoServerTypes.ISOLATE_SEND_PORT]; |
| + int serverInternetPort = response[EchoServerTypes.SERVER_PORT]; |
| + List<int> data = createTestData(); |
| + |
| + receivePort = new ReceivePort(); |
| + echo.addErrorListener(receivePort.sendPort); |
| + |
| + Map limits = { |
| + EchoServerTypes.OFFSET_START: 0, |
| + EchoServerTypes.OFFSET_END: data.length, |
| + EchoServerTypes.ISOLATE_SEND_PORT: receivePort.sendPort, |
| + // Tell the server to shutdown after 3 sockets disconnect. |
| + EchoServerTypes.CONNECTION_COUNT: 3 |
| + }; |
| + sendPort.send(limits); |
| + |
| + try { |
| + var socket = RawSynchronousSocket.connectSync( |
| + LOOPBACK_IP_V4_STRING, serverInternetPort); |
| + |
| + // Close from both directions. Shouldn't be able to read/write to the |
| + // socket. |
| + socket.shutdown(SocketDirection.BOTH); |
| + socket.writeFromSync(data); // Should this work? |
|
zra
2017/04/07 16:29:41
It's weird that this doesn't throw when closed in
bkonyi
2017/04/10 19:20:07
I've gone ahead and changed all the read/write ope
|
| + Expect.isTrue(socket.readSync(data.length) == null); |
| + socket.closeSync(); |
| + |
| + // Close the socket for reading, do a write, and see if we can get any |
| + // response from the server (we shouldn't be able to). |
| + socket = RawSynchronousSocket.connectSync( |
| + LOOPBACK_IP_V4_STRING, serverInternetPort); |
| + socket.shutdown(SocketDirection.RECEIVE); |
| + socket.writeFromSync(data); |
| + // Returns null when the socket is closed for RECEIVE. |
| + Expect.isTrue(socket.readSync(data.length) == null); |
| + Expect.isTrue(socket.available() == 0); |
| + socket.closeSync(); |
| + |
| + // Close the socket for writing and try to do a write. This should cause an |
| + // OSError to be throw as the pipe is closed for writing. |
| + socket = RawSynchronousSocket.connectSync( |
| + LOOPBACK_IP_V4_STRING, serverInternetPort); |
| + socket.shutdown(SocketDirection.SEND); |
| + Expect.throws(() => socket.writeFromSync(data), (e) => e is OSError); |
| + socket.closeSync(); |
| + } catch (e, stack) { |
| + throw "Echo test failed in client!\nError: ${e}\nStack trace: ${stack}"; |
| + } |
| + // Wait for the server to shutdown before finishing the test. |
| + var result = await receivePort.first; |
| + if (result != null) { |
| + throw "Echo test failed in server!\nError: ${result[0]}\nStack trace:" + |
| + " ${result[1]}"; |
| + } |
| + asyncEnd(); |
| +} |
| + |
| +void testClosedError() { |
| + asyncStart(); |
| + RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) { |
| + server.listen((socket) { |
| + socket.close(); |
| + }); |
| + var socket = |
| + RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port); |
| + server.close(); |
| + socket.closeSync(); |
| + Expect.throws(() => socket.remotePort, (e) => e is SocketException); |
| + Expect.throws(() => socket.remoteAddress, (e) => e is SocketException); |
| + asyncEnd(); |
| + }); |
| +} |
| + |
| +main() async { |
| + asyncStart(); |
| + testArguments(); |
| + testInvalidConnect(); |
| + await testShutdown(); |
| + testSimpleConnect(); |
| + testServerListenAfterConnect(); |
| + await testSimpleReadWrite(); |
| + await testPartialRead(); |
| + await testPartialWrite(); |
| + testClosedError(); |
| + asyncEnd(); |
| +} |