From ffba5c425cd2df99493716187cb9797e9ba3755e Mon Sep 17 00:00:00 2001 From: Dan Field Date: Wed, 19 Jan 2022 17:50:18 -0800 Subject: [PATCH] Print a more helpful error if DDS is enabled when running integration_test and enableTimeline is called (#92509) --- dev/bots/test.dart | 2 +- .../lib/integration_test.dart | 44 +++++++++++++++++-- .../test/socket_fail_test.dart | 32 ++++++++++++++ 3 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 packages/integration_test/test/socket_fail_test.dart diff --git a/dev/bots/test.dart b/dev/bots/test.dart index fdc51b4e7b..651e4437f3 100644 --- a/dev/bots/test.dart +++ b/dev/bots/test.dart @@ -883,7 +883,7 @@ Future _runFrameworkTests() async { await _runFlutterTest(path.join(flutterRoot, 'dev', 'tools', 'gen_keycodes')); await _runFlutterTest(path.join(flutterRoot, 'dev', 'benchmarks', 'test_apps', 'stocks')); await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_driver'), tests: [path.join('test', 'src', 'real_tests')], options: soundNullSafetyOptions); - await _runFlutterTest(path.join(flutterRoot, 'packages', 'integration_test')); + await _runFlutterTest(path.join(flutterRoot, 'packages', 'integration_test'), options: ['--enable-vmservice']); await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_goldens'), options: soundNullSafetyOptions); await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_localizations'), options: soundNullSafetyOptions); await _runFlutterTest(path.join(flutterRoot, 'packages', 'flutter_test'), options: soundNullSafetyOptions); diff --git a/packages/integration_test/lib/integration_test.dart b/packages/integration_test/lib/integration_test.dart index ea69c02843..55c828815c 100644 --- a/packages/integration_test/lib/integration_test.dart +++ b/packages/integration_test/lib/integration_test.dart @@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:developer' as developer; +import 'dart:io' show SocketException, WebSocket, HttpClient; import 'dart:ui'; import 'package:flutter/foundation.dart'; @@ -12,7 +13,6 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:vm_service/vm_service.dart' as vm; -import 'package:vm_service/vm_service_io.dart' as vm_io; import '_callback_io.dart' if (dart.library.html) '_callback_web.dart' as driver_actions; import '_extension_io.dart' if (dart.library.html) '_extension_web.dart'; @@ -235,6 +235,7 @@ https://flutter.dev/docs/testing/integration-tests#testing-on-firebase-test-lab Future enableTimeline({ List streams = const ['all'], @visibleForTesting vm.VmService? vmService, + @visibleForTesting HttpClient? httpClient, }) async { assert(streams != null); assert(streams.isNotEmpty); @@ -244,9 +245,18 @@ https://flutter.dev/docs/testing/integration-tests#testing-on-firebase-test-lab if (_vmService == null) { final developer.ServiceProtocolInfo info = await developer.Service.getInfo(); assert(info.serverUri != null); - _vmService = await vm_io.vmServiceConnectUri( - 'ws://localhost:${info.serverUri!.port}${info.serverUri!.path}ws', - ); + final String address = 'ws://localhost:${info.serverUri!.port}${info.serverUri!.path}ws'; + try { + _vmService = await _vmServiceConnectUri(address, httpClient: httpClient); + } on SocketException catch(e, s) { + throw StateError( + 'Failed to connect to VM Service at $address.\n' + 'This may happen if DDS is enabled. If this test was launched via ' + '`flutter drive`, try adding `--no-dds`.\n' + 'The original exception was:\n' + '$e\n$s', + ); + } } await _vmService!.setVMTimelineFlags(streams); } @@ -447,3 +457,29 @@ class _GarbageCollectionInfo { final int oldCount; final int newCount; } + +// Connect to the given uri and return a new [VmService] instance. +// +// Copied from vm_service_io so that we can pass a custom [HttpClient] for +// testing. Currently, the WebSocket API reuses an HttpClient that +// is created before the test can change the HttpOverrides. +Future _vmServiceConnectUri( + String wsUri, { + HttpClient? httpClient, +}) async { + final WebSocket socket = await WebSocket.connect(wsUri, customClient: httpClient); + final StreamController controller = StreamController(); + final Completer streamClosedCompleter = Completer(); + + socket.listen( + (dynamic data) => controller.add(data), + onDone: () => streamClosedCompleter.complete(), + ); + + return vm.VmService( + controller.stream, + (String message) => socket.add(message), + disposeHandler: () => socket.close(), + streamClosed: streamClosedCompleter.future, + ); +} diff --git a/packages/integration_test/test/socket_fail_test.dart b/packages/integration_test/test/socket_fail_test.dart new file mode 100644 index 0000000000..ad9716983e --- /dev/null +++ b/packages/integration_test/test/socket_fail_test.dart @@ -0,0 +1,32 @@ +// Copyright 2014 The Flutter Authors. 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:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; + +class SocketExceptionHttpClient extends Fake implements HttpClient { + @override + Future openUrl(String method, Uri url) { + throw const SocketException('always throw'); + } +} + +Future main() async { + final IntegrationTestWidgetsFlutterBinding binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized() as IntegrationTestWidgetsFlutterBinding; + + test('Prints an appropriate message on socket exception', () async { + bool gotStateError = false; + try { + await binding.enableTimeline(httpClient: SocketExceptionHttpClient()); + } on StateError catch (e) { + gotStateError = true; + expect(e.toString(), contains('This may happen if DDS is enabled')); + } on SocketException catch (_) { + fail('Did not expect a socket exception.'); + } + expect(gotStateError, true); + }); +}