Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1045)

Unified Diff: tests/standalone/io/raw_synchronous_socket_test.dart

Issue 2814143002: Various fixes for sync socket implementation. (Closed)
Patch Set: Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « sdk/lib/io/sync_socket.dart ('k') | tests/standalone/standalone.status » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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..af60a3b963226ee0874e3b9ad6273430982c7f71
--- /dev/null
+++ b/tests/standalone/io/raw_synchronous_socket_test.dart
@@ -0,0 +1,490 @@
+// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
+// 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.
+
+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() {
+ Expect.throws(() => RawSynchronousSocket.connectSync(null, 0));
+ Expect.throws(
+ () => RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, null));
+ 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));
+}
+
+/*
+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 a 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) {
+ print("Echo test failed in the client");
+ rethrow;
+ }
+ // 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 is based on testSimpleReadWrite, but instead of reading the
+ // entire echoed message at once, it reads it in two calls to readIntoSync.
+ 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) {
+ print("Echo test failed in the client.");
+ rethrow;
+ }
+ // 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 is based on testSimpleReadWrite, but instead of writing the
+ // entire data buffer at once, it writes different parts of the buffer over
+ // multiple calls to writeFromSync.
+ 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) {
+ print("Echo test failed in the client.");
+ rethrow;
+ }
+
+ // 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 a server and a client connects. The client then tries to
+ // perform various operations after being shutdown in a specific direction, to
+ // ensure reads or writes cannot be performed if the socket has been shutdown
+ // for reading or writing.
+ 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);
+ Expect.throws(
+ () => socket.writeFromSync(data), (e) => e is SocketException);
+ Expect.throws(
+ () => socket.readSync(data.length), (e) => e is SocketException);
+ 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);
+ // Throws exception when the socket is closed for RECEIVE.
+ Expect.throws(
+ () => socket.readSync(data.length), (e) => e is SocketException);
+ 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 SocketException);
+ socket.closeSync();
+ } catch (e, stack) {
+ print("Echo test failed in client.");
+ rethrow;
+ }
+ // 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 testInvalidReadWriteOperations() {
+ asyncStart();
+ RawServerSocket.bind(InternetAddress.LOOPBACK_IP_V4, 0).then((server) {
+ server.listen((socket) {});
+ List<int> data = createTestData();
+ var socket =
+ RawSynchronousSocket.connectSync(LOOPBACK_IP_V4_STRING, server.port);
+
+ // Invalid writeFromSync invocations
+ Expect.throws(() => socket.writeFromSync(data, data.length + 1),
+ (e) => e is RangeError);
+ Expect.throws(() => socket.writeFromSync(data, 0, data.length + 1),
+ (e) => e is RangeError);
+ Expect.throws(
+ () => socket.writeFromSync(data, 1, 0), (e) => e is RangeError);
+ Expect.throws(
+ () => socket.writeFromSync(data, null), (e) => e is ArgumentError);
+
+ // Invalid readIntoSync invocations
+ List<int> buffer = new List<int>(10);
+ Expect.throws(() => socket.readIntoSync(buffer, buffer.length + 1),
+ (e) => e is RangeError);
+ Expect.throws(() => socket.readIntoSync(buffer, 0, buffer.length + 1),
+ (e) => e is RangeError);
+ Expect.throws(
+ () => socket.readIntoSync(buffer, 1, 0), (e) => e is RangeError);
+ Expect.throws(
+ () => socket.readIntoSync(buffer, null), (e) => e is ArgumentError);
+
+ // Invalid readSync invocation
+ Expect.throws(() => socket.readSync(-1), (e) => e is ArgumentError);
+
+ server.close();
+ socket.closeSync();
+ 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(); Long timeout for bad lookups, so disable for bots.
+ await testShutdown();
+ testSimpleConnect();
+ testServerListenAfterConnect();
+ await testSimpleReadWrite();
+ await testPartialRead();
+ await testPartialWrite();
+ testInvalidReadWriteOperations();
+ testClosedError();
+ asyncEnd();
+}
« no previous file with comments | « sdk/lib/io/sync_socket.dart ('k') | tests/standalone/standalone.status » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698