blob: 2bca092d73cad83538d735ded72a16f539ddbbb2 [file] [log] [blame]
// Copyright (c) 2020, 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 'package:dds/dds.dart';
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc;
import 'package:pedantic/pedantic.dart';
import 'package:test/test.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'common/fakes.dart';
class StreamCancelDisconnectPeer extends FakePeer {
@override
Future<dynamic> sendRequest(String method, [args]) async {
final completer = Completer<dynamic>();
switch (method) {
case 'streamCancel':
completer.completeError(
StateError('The client closed with pending request "streamCancel".'),
);
// Notify listeners that this client is closed.
doneCompleter.complete();
break;
default:
completer.complete(await super.sendRequest(method, args));
}
return completer.future;
}
}
void main() {
webSocketBuilder = (Uri _) => null;
peerBuilder =
(WebSocketChannel _, dynamic __) async => StreamCancelDisconnectPeer();
test('StateError handled by _StreamManager.clientDisconnect', () async {
final dds = await DartDevelopmentService.startDartDevelopmentService(
Uri(scheme: 'http'));
final ws = await WebSocketChannel.connect(dds.uri.replace(scheme: 'ws'));
// Create a VM service client that connects to DDS.
final client = json_rpc.Client(ws.cast<String>());
unawaited(client.listen());
// Listen to a non-core DDS stream so that DDS will cancel it once the
// client disconnects.
await client.sendRequest('streamListen', {
'streamId': 'Service',
});
// Closing the client should result in DDS cleaning up stream subscriptions
// with no more clients subscribed to them. This will result in
// streamCancel being invoked, which StreamCancelDisconnectPeer overrides
// to act as if the VM service has shutdown with the request in flight
// which would result in a StateError being thrown by sendRequest. This
// test ensures that this exception is handled and doesn't escape outside
// of DDS.
await client.close();
await dds.done;
});
}