blob: f9ad2d0d7d4e9ef09f6157e8577411e59792c5e8 [file] [log] [blame]
// Copyright (c) 2013, 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.
// @dart = 2.9
// VMOptions=--enable-isolate-groups
// VMOptions=--no-enable-isolate-groups
//
// VMOptions=
// VMOptions=--short_socket_read
// VMOptions=--short_socket_write
// VMOptions=--short_socket_read --short_socket_write
//
// Test socket close events.
import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import "package:async_helper/async_helper.dart";
import "package:expect/expect.dart";
const SERVERSHUTDOWN = -1;
const ITERATIONS = 10;
Future sendReceive(SendPort port, message) {
ReceivePort receivePort = new ReceivePort();
port.send([message, receivePort.sendPort]);
return receivePort.first;
}
class SocketClose {
SocketClose.start(this._mode, this._done)
: _sendPort = null,
_readBytes = 0,
_dataEvents = 0,
_closeEvents = 0,
_errorEvents = 0,
_iterations = 0 {
initialize();
}
void proceed() {
if (_iterations < ITERATIONS) {
Timer.run(sendData);
} else {
shutdown();
}
}
void sendData() {
void dataHandler(bytes) {
switch (_mode) {
case 0:
case 1:
case 2:
Expect.fail("No data expected");
break;
case 3:
case 4:
case 5:
case 6:
_readBytes += bytes.length;
if ((_readBytes % 5) == 0) {
_dataEvents++;
}
break;
default:
Expect.fail("Unknown test mode");
}
}
void closeHandler(socket) {
_closeEvents++;
switch (_mode) {
case 0:
case 1:
socket.close();
break;
case 2:
case 3:
socket.close();
proceed();
break;
case 4:
proceed();
break;
case 5:
socket.close();
proceed();
break;
case 6:
proceed();
break;
default:
Expect.fail("Unknown test mode");
}
}
void errorHandler(Socket socket) {
_errorEvents++;
socket.close();
}
void connectHandler(socket) {
socket.listen(dataHandler,
onDone: () => closeHandler(socket),
onError: (error) => errorHandler(socket));
void writeHello() {
socket.write("Hello");
}
_iterations++;
switch (_mode) {
case 0:
socket.destroy();
proceed();
break;
case 1:
writeHello();
socket.destroy();
proceed();
break;
case 2:
case 3:
writeHello();
break;
case 4:
writeHello();
socket.close(); // Half close.
break;
case 5:
writeHello();
break;
case 6:
writeHello();
socket.close(); // Half close.
break;
default:
Expect.fail("Unknown test mode");
}
}
Socket.connect(SocketCloseServer.HOST, _port).then(connectHandler);
}
void initialize() {
ReceivePort receivePort = new ReceivePort();
var remote = Isolate.spawn(startSocketCloseServer, receivePort.sendPort);
receivePort.first.then((message) {
this._sendPort = message;
sendReceive(_sendPort, _mode).then((port) {
this._port = port;
proceed();
});
});
}
void shutdown() {
sendReceive(_sendPort, SERVERSHUTDOWN).then((_) {
_done();
});
switch (_mode) {
case 0:
case 1:
Expect.equals(0, _dataEvents);
Expect.equals(ITERATIONS, _closeEvents);
break;
case 2:
Expect.equals(0, _dataEvents);
Expect.equals(ITERATIONS, _closeEvents);
break;
case 3:
case 4:
Expect.isTrue(_dataEvents <= ITERATIONS);
Expect.isTrue(_dataEvents >= 0);
Expect.equals(ITERATIONS, _closeEvents);
break;
case 5:
case 6:
Expect.equals(ITERATIONS, _dataEvents);
Expect.equals(ITERATIONS, _closeEvents);
break;
default:
Expect.fail("Unknown test mode");
}
Expect.equals(0, _errorEvents);
}
int _port;
SendPort _sendPort;
List<int> _buffer;
int _readBytes;
int _dataEvents;
int _closeEvents;
int _errorEvents;
int _iterations;
int _mode;
Function _done;
}
class ConnectionData {
ConnectionData(Socket this.connection) : readBytes = 0;
Socket connection;
int readBytes;
}
void startSocketCloseServer(Object replyToObj) {
SendPort replyTo = replyToObj;
var server = new SocketCloseServer();
replyTo.send(server.dispatchSendPort);
}
class SocketCloseServer {
static const HOST = "127.0.0.1";
SocketCloseServer() : _dispatchPort = new ReceivePort() {
_dispatchPort.listen(dispatch);
}
void connectionHandler(ConnectionData data) {
var connection = data.connection;
void readBytes(bytes, whenFiveBytes) {
data.readBytes += bytes.length;
Expect.isTrue(data.readBytes <= 5);
if (data.readBytes == 5) {
whenFiveBytes();
}
}
void writeHello() {
connection.write("Hello");
}
void dataHandler(bytes) {
switch (_mode) {
case 0:
Expect.fail("No data expected");
break;
case 1:
readBytes(bytes, () {
_dataEvents++;
});
break;
case 2:
readBytes(bytes, () {
_dataEvents++;
connection.destroy();
});
break;
case 3:
readBytes(bytes, () {
_dataEvents++;
writeHello();
connection.destroy();
});
break;
case 4:
readBytes(bytes, () {
_dataEvents++;
writeHello();
});
break;
case 5:
case 6:
readBytes(bytes, () {
_dataEvents++;
writeHello();
connection.close(); // Half close.
});
break;
default:
Expect.fail("Unknown test mode");
}
}
void closeHandler() {
_closeEvents++;
connection.close();
}
void errorHandler(e) {
Expect.fail("Socket error $e");
}
_iterations++;
connection.listen(dataHandler, onDone: closeHandler, onError: errorHandler);
}
void errorHandlerServer(e) {
Expect.fail("Server socket error");
}
waitForResult() {
// Make sure all iterations have been run. In multiple of these
// scenarios it is possible to get the SERVERSHUTDOWN message
// before we have received the last close event on the
// server. In these cases we wait for the correct number of
// close events.
if (_iterations == ITERATIONS &&
(_closeEvents == ITERATIONS || (_mode == 2 || _mode == 3))) {
switch (_mode) {
case 0:
Expect.equals(0, _dataEvents);
Expect.equals(ITERATIONS, _closeEvents);
break;
case 1:
Expect.isTrue(_dataEvents <= ITERATIONS);
Expect.isTrue(_dataEvents >= 0);
Expect.equals(ITERATIONS, _closeEvents);
break;
case 2:
case 3:
Expect.equals(ITERATIONS, _dataEvents);
Expect.equals(ITERATIONS, _closeEvents);
break;
case 4:
case 5:
case 6:
Expect.equals(ITERATIONS, _dataEvents);
Expect.equals(ITERATIONS, _closeEvents);
break;
default:
Expect.fail("Unknown test mode");
}
Expect.equals(0, _errorEvents);
_server.close();
_dispatchPort.close();
_donePort.send(null);
} else {
new Timer(new Duration(milliseconds: 100), waitForResult);
}
}
SendPort get dispatchSendPort => _dispatchPort.sendPort;
void dispatch(message) {
var command = message[0];
SendPort replyTo = message[1];
_donePort = replyTo;
if (command != SERVERSHUTDOWN) {
_readBytes = 0;
_errorEvents = 0;
_dataEvents = 0;
_closeEvents = 0;
_iterations = 0;
_mode = command;
ServerSocket.bind("127.0.0.1", 0).then((server) {
_server = server;
_server.listen((socket) {
var data = new ConnectionData(socket);
connectionHandler(data);
}, onError: errorHandlerServer);
replyTo.send(_server.port);
});
} else {
Timer.run(waitForResult);
}
}
ServerSocket _server;
final ReceivePort _dispatchPort;
SendPort _donePort;
int _readBytes;
int _errorEvents;
int _dataEvents;
int _closeEvents;
int _iterations;
int _mode;
}
main() {
// Run the close test in these different "modes".
// 0: Client closes without sending at all.
// 1: Client sends and destroys.
// 2: Client sends. Server destroys.
// 3: Client sends. Server responds and destroys.
// 4: Client sends and half-closes. Server responds and destroys.
// 5: Client sends. Server responds and half closes.
// 6: Client sends and half-closes. Server responds and half closes.
var tests = 7;
for (var i = 0; i < tests; i++) {
asyncStart();
new SocketClose.start(i, asyncEnd);
}
}