test(web_socket...tests): close connections (#1908)
diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart
index cf39f95..cbaf827 100644
--- a/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart
+++ b/pkgs/web_socket_conformance_tests/lib/src/close_local_tests.dart
@@ -15,6 +15,7 @@
import 'continuously_writing_server_vm.dart'
if (dart.library.html) 'continuously_writing_server_web.dart'
as writing_server;
+import 'utils.dart';
/// Tests that the [WebSocket] can correctly close the connection to the peer.
void testCloseLocal(
@@ -36,6 +37,7 @@
test('peer writes after close are ignored', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await channel.close();
expect(await channel.events.isEmpty, true);
});
@@ -57,30 +59,35 @@
test('reserved close code: 1004', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await expectLater(
() => channel.close(1004), throwsA(isA<ArgumentError>()));
});
test('reserved close code: 2999', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await expectLater(
() => channel.close(2999), throwsA(isA<ArgumentError>()));
});
test('reserved close code: 5000', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await expectLater(
() => channel.close(5000), throwsA(isA<ArgumentError>()));
});
test('too long close reason', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await expectLater(() => channel.close(3000, 'a'.padLeft(124)),
throwsA(isA<ArgumentError>()));
});
test('close', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await channel.close();
final closeCode = await httpServerQueue.next as int?;
@@ -93,6 +100,7 @@
test('close with 1000', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await channel.close(1000);
final closeCode = await httpServerQueue.next as int?;
@@ -105,6 +113,7 @@
test('with code 3000', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await channel.close(3000);
final closeCode = await httpServerQueue.next as int?;
@@ -117,6 +126,7 @@
test('with code 4999', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await channel.close(4999);
final closeCode = await httpServerQueue.next as int?;
@@ -129,6 +139,7 @@
test('with code and reason', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await channel.close(3000, 'Client initiated closure');
final closeCode = await httpServerQueue.next as int?;
@@ -141,6 +152,7 @@
test('close after close', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await channel.close(3000, 'Client initiated closure');
@@ -151,6 +163,7 @@
test('sendBytes after close', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await channel.close(3000, 'Client initiated closure');
@@ -160,6 +173,7 @@
test('sendText after close', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
await channel.close(3000, 'Client initiated closure');
diff --git a/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart
index aca846a..0171e30 100644
--- a/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart
+++ b/pkgs/web_socket_conformance_tests/lib/src/close_remote_tests.dart
@@ -11,6 +11,7 @@
import 'close_remote_server_vm.dart'
if (dart.library.html) 'close_remote_server_web.dart';
+import 'utils.dart';
/// Tests that the [WebSocket] can correctly receive Close frames from the peer.
void testCloseRemote(
@@ -32,6 +33,7 @@
test('with code and reason', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
channel.sendText('Please close');
expect(await channel.events.toList(),
@@ -40,6 +42,7 @@
test('sendBytes after close received', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
channel.sendBytes(Uint8List(10));
expect(await channel.events.toList(),
@@ -50,6 +53,7 @@
test('sendText after close received', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
channel.sendText('Please close');
expect(await channel.events.toList(),
@@ -60,6 +64,7 @@
test('close after close received', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
channel.sendText('Please close');
expect(await channel.events.toList(),
@@ -71,6 +76,7 @@
test('with invalid reason', () async {
final channel =
await channelFactory(uri.replace(queryParameters: {'badutf8': ''}));
+ addTearDown(() => closeWebSocket(channel));
channel.sendText('Please close');
final events = await channel.events.toList();
diff --git a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart
index b5f52e9..52970aa 100644
--- a/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart
+++ b/pkgs/web_socket_conformance_tests/lib/src/disconnect_after_upgrade_tests.dart
@@ -9,6 +9,7 @@
import 'disconnect_after_upgrade_server_vm.dart'
if (dart.library.html) 'disconnect_after_upgrade_server_web.dart';
+import 'utils.dart';
/// Tests that the [WebSocket] generates a correct [CloseReceived] event if
/// the peer disconnects after WebSocket upgrade.
@@ -29,6 +30,7 @@
test('disconnect after upgrade', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
channel.sendText('test');
expect(
(await channel.events.single as CloseReceived).code,
diff --git a/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart
index 6b04b91..eaebeae 100644
--- a/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart
+++ b/pkgs/web_socket_conformance_tests/lib/src/payload_transfer_tests.dart
@@ -10,6 +10,7 @@
import 'package:web_socket/web_socket.dart';
import 'echo_server_vm.dart' if (dart.library.html) 'echo_server_web.dart';
+import 'utils.dart';
/// Tests that the [WebSocket] can correctly transmit and receive text
/// and binary payloads.
@@ -20,41 +21,48 @@
late Uri uri;
late StreamChannel<Object?> httpServerChannel;
late StreamQueue<Object?> httpServerQueue;
- late WebSocket webSocket;
setUp(() async {
httpServerChannel = await startServer();
httpServerQueue = StreamQueue(httpServerChannel.stream);
uri = Uri.parse('ws://localhost:${await httpServerQueue.next}');
- webSocket = await webSocketFactory(uri);
});
tearDown(() async {
httpServerChannel.sink.add(null);
- await webSocket.close();
});
test('empty string request and response', () async {
+ final webSocket = await webSocketFactory(uri);
+ addTearDown(() => closeWebSocket(webSocket));
webSocket.sendText('');
expect(await webSocket.events.first, TextDataReceived(''));
});
test('empty binary request and response', () async {
+ final webSocket = await webSocketFactory(uri);
+ addTearDown(() => closeWebSocket(webSocket));
webSocket.sendBytes(Uint8List(0));
expect(await webSocket.events.first, BinaryDataReceived(Uint8List(0)));
});
test('string request and response', () async {
+ final webSocket = await webSocketFactory(uri);
+ addTearDown(() => closeWebSocket(webSocket));
webSocket.sendText('Hello World!');
expect(await webSocket.events.first, TextDataReceived('Hello World!'));
});
test('binary request and response', () async {
+ final webSocket = await webSocketFactory(uri);
+ addTearDown(() => closeWebSocket(webSocket));
webSocket.sendBytes(Uint8List.fromList([1, 2, 3, 4, 5]));
expect(await webSocket.events.first,
BinaryDataReceived(Uint8List.fromList([1, 2, 3, 4, 5])));
});
test('large string request and response', () async {
+ final webSocket = await webSocketFactory(uri);
+ addTearDown(() => closeWebSocket(webSocket));
final data = 'Hello World!' * 10000;
webSocket.sendText(data);
@@ -62,6 +70,8 @@
});
test('large binary request and response', () async {
+ final webSocket = await webSocketFactory(uri);
+ addTearDown(() => closeWebSocket(webSocket));
final data = Uint8List(1000000);
data
..fillRange(0, data.length ~/ 10, 1)
@@ -80,11 +90,15 @@
});
test('non-ascii string request and response', () async {
+ final webSocket = await webSocketFactory(uri);
+ addTearDown(() => closeWebSocket(webSocket));
webSocket.sendText('🎨⛳🌈');
expect(await webSocket.events.first, TextDataReceived('🎨⛳🌈'));
});
test('alternative string and binary request and response', () async {
+ final webSocket = await webSocketFactory(uri);
+ addTearDown(() => closeWebSocket(webSocket));
webSocket
..sendBytes(Uint8List.fromList([1]))
..sendText('Hello!')
diff --git a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart
index ba44f51..52d2ec6 100644
--- a/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart
+++ b/pkgs/web_socket_conformance_tests/lib/src/peer_protocol_errors_tests.dart
@@ -9,6 +9,7 @@
import 'peer_protocol_errors_server_vm.dart'
if (dart.library.html) 'peer_protocol_errors_server_web.dart';
+import 'utils.dart';
/// Tests that the [WebSocket] can correctly handle incorrect WebSocket frames.
void testPeerProtocolErrors(
@@ -28,6 +29,7 @@
test('bad data after upgrade', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
expect(
(await channel.events.single as CloseReceived).code,
anyOf([
@@ -39,6 +41,7 @@
test('bad data after upgrade with write', () async {
final channel = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(channel));
channel.sendText('test');
expect(
(await channel.events.single as CloseReceived).code,
diff --git a/pkgs/web_socket_conformance_tests/lib/src/protocol_tests.dart b/pkgs/web_socket_conformance_tests/lib/src/protocol_tests.dart
index 2d35f1c..3289507 100644
--- a/pkgs/web_socket_conformance_tests/lib/src/protocol_tests.dart
+++ b/pkgs/web_socket_conformance_tests/lib/src/protocol_tests.dart
@@ -9,6 +9,7 @@
import 'protocol_server_vm.dart'
if (dart.library.html) 'protocol_server_web.dart';
+import 'utils.dart';
/// Tests that the [WebSocket] can correctly negotiate a subprotocol with the
/// peer.
@@ -28,10 +29,13 @@
httpServerQueue = StreamQueue(httpServerChannel.stream);
uri = Uri.parse('ws://localhost:${await httpServerQueue.next}');
});
- tearDown(() => httpServerChannel.sink.add(null));
+ tearDown(() async {
+ httpServerChannel.sink.add(null);
+ });
test('no protocol', () async {
final socket = await channelFactory(uri);
+ addTearDown(() => closeWebSocket(socket));
expect(await httpServerQueue.next, null);
expect(socket.protocol, '');
@@ -42,6 +46,7 @@
final socket = await channelFactory(
uri.replace(queryParameters: {'protocol': 'chat.example.com'}),
protocols: ['chat.example.com']);
+ addTearDown(() => closeWebSocket(socket));
expect(await httpServerQueue.next, ['chat.example.com']);
expect(socket.protocol, 'chat.example.com');
@@ -52,6 +57,7 @@
final socket = await channelFactory(
uri.replace(queryParameters: {'protocol': 'text.example.com'}),
protocols: ['chat.example.com', 'text.example.com']);
+ addTearDown(() => closeWebSocket(socket));
expect(
await httpServerQueue.next, ['chat.example.com, text.example.com']);
diff --git a/pkgs/web_socket_conformance_tests/lib/src/utils.dart b/pkgs/web_socket_conformance_tests/lib/src/utils.dart
new file mode 100644
index 0000000..248b078
--- /dev/null
+++ b/pkgs/web_socket_conformance_tests/lib/src/utils.dart
@@ -0,0 +1,15 @@
+// Copyright (c) 2026, 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 'package:web_socket/web_socket.dart';
+
+/// Closes the [socket] and ignores [WebSocketConnectionClosed] if it's already
+/// closed.
+Future<void> closeWebSocket(WebSocket socket) async {
+ try {
+ await socket.close();
+ } on WebSocketConnectionClosed {
+ // Already closed.
+ }
+}