6fe105671c
Not removing from any file with a language marker. Also not touching anything in `tests/language/primary_constructors` or `.../private_named_parameters`, which are both assumed to be primary constructor feature aware. Two files rewritten from multi-test to error-test. Change-Id: I43d444a35a41c7734f266794e9f167655692473f Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/480640 Reviewed-by: Nate Bosch <nbosch@google.com> Commit-Queue: Lasse Nielsen <lrn@google.com>
522 lines
17 KiB
Dart
522 lines
17 KiB
Dart
// 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:expect/async_helper.dart";
|
|
import "package:expect/expect.dart";
|
|
|
|
const String loopbackIPv4String = "127.0.0.1";
|
|
|
|
void testArguments() {
|
|
Expect.throws(() => RawSynchronousSocket.connectSync(null, 0));
|
|
Expect.throws(
|
|
() => RawSynchronousSocket.connectSync(loopbackIPv4String, 65536),
|
|
);
|
|
Expect.throws(() => RawSynchronousSocket.connectSync(loopbackIPv4String, -1));
|
|
}
|
|
|
|
// The connection attempt happens on the main Dart thread and the OS timeout can
|
|
// be arbitrarily long, causing timeout issues on the build bots. This isn't an
|
|
// issue with the async sockets since the lookup for a connect call happens on
|
|
// the IO service thread.
|
|
/*
|
|
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 unavailable 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.loopbackIPv4, 0).then((server) {
|
|
var socket = RawSynchronousSocket.connectSync(
|
|
loopbackIPv4String,
|
|
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.loopbackIPv4, 0).then((server) {
|
|
Expect.isTrue(server.port > 0);
|
|
var client = RawSynchronousSocket.connectSync(
|
|
loopbackIPv4String,
|
|
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(sendPort) async {
|
|
RawServerSocket.bind(InternetAddress.loopbackIPv4, 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>.filled(length, 0);
|
|
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.readClosed:
|
|
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);
|
|
Isolate.current.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(
|
|
loopbackIPv4String,
|
|
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(
|
|
loopbackIPv4String,
|
|
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>.filled(data.length, 0);
|
|
|
|
// 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(
|
|
loopbackIPv4String,
|
|
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>.filled(endOffset - startOffset, 0);
|
|
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(
|
|
loopbackIPv4String,
|
|
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 then try and perform a read. This should
|
|
// cause a SocketException.
|
|
socket = RawSynchronousSocket.connectSync(
|
|
loopbackIPv4String,
|
|
serverInternetPort,
|
|
);
|
|
socket.shutdown(SocketDirection.receive);
|
|
// Throws exception when the socket is closed for RECEIVE.
|
|
Expect.throws(
|
|
() => socket.readSync(data.length),
|
|
(e) => e is SocketException,
|
|
);
|
|
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(
|
|
loopbackIPv4String,
|
|
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();
|
|
}
|
|
|
|
void testInvalidReadWriteOperations() {
|
|
asyncStart();
|
|
RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) {
|
|
server.listen((socket) {});
|
|
List<int> data = createTestData();
|
|
var socket = RawSynchronousSocket.connectSync(
|
|
loopbackIPv4String,
|
|
server.port,
|
|
);
|
|
|
|
// Invalid writeFromSync invocations
|
|
Expect.throwsRangeError(() => socket.writeFromSync(data, data.length + 1));
|
|
Expect.throwsRangeError(
|
|
() => socket.writeFromSync(data, 0, data.length + 1),
|
|
);
|
|
Expect.throwsRangeError(() => socket.writeFromSync(data, 1, 0));
|
|
|
|
// Invalid readIntoSync invocations
|
|
List<int> buffer = new List<int>.filled(10, 0);
|
|
Expect.throwsRangeError(
|
|
() => socket.readIntoSync(buffer, buffer.length + 1),
|
|
);
|
|
Expect.throwsRangeError(
|
|
() => socket.readIntoSync(buffer, 0, buffer.length + 1),
|
|
);
|
|
Expect.throwsRangeError(() => socket.readIntoSync(buffer, 1, 0));
|
|
|
|
// Invalid readSync invocation
|
|
Expect.throwsArgumentError(() => socket.readSync(-1));
|
|
|
|
server.close();
|
|
socket.closeSync();
|
|
asyncEnd();
|
|
});
|
|
}
|
|
|
|
void testClosedError() {
|
|
asyncStart();
|
|
RawServerSocket.bind(InternetAddress.loopbackIPv4, 0).then((server) {
|
|
server.listen((socket) {
|
|
socket.close();
|
|
});
|
|
var socket = RawSynchronousSocket.connectSync(
|
|
loopbackIPv4String,
|
|
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();
|
|
}
|