From fe7bbf722689cbf3769eb82059294bcff50c3939 Mon Sep 17 00:00:00 2001 From: Ben Konyi Date: Fri, 10 Jul 2020 15:35:21 -0700 Subject: [PATCH] Add support for Dart Development Service (DDS) in Flutter Tools (#59114) --- .../test/image_painting_event_test.dart | 4 ++ packages/flutter_tools/lib/src/base/dds.dart | 49 +++++++++++++++++++ .../lib/src/commands/attach.dart | 3 +- .../flutter_tools/lib/src/commands/drive.dart | 4 ++ .../flutter_tools/lib/src/commands/run.dart | 2 + packages/flutter_tools/lib/src/device.dart | 10 ++++ .../lib/src/fuchsia/fuchsia_device.dart | 10 ++++ .../flutter_tools/lib/src/ios/devices.dart | 2 +- .../lib/src/ios/fallback_discovery.dart | 4 +- .../lib/src/resident_runner.dart | 21 +++++++- .../lib/src/runner/flutter_command.dart | 13 +++++ .../lib/src/test/flutter_platform.dart | 15 +++--- packages/flutter_tools/lib/src/tracing.dart | 6 --- packages/flutter_tools/pubspec.yaml | 6 ++- .../commands.shard/hermetic/attach_test.dart | 22 ++++++++- .../commands.shard/hermetic/drive_test.dart | 19 +++++-- .../test/general.shard/cold_test.dart | 2 + .../fuchsia/fuchsia_device_test.dart | 1 + .../test/general.shard/hot_test.dart | 2 + .../ios/fallback_discovery_test.dart | 10 ++-- .../general.shard/resident_runner_test.dart | 5 ++ .../flutter_attach_test.dart | 15 +++++- .../flutter_run_with_error_test.dart | 3 -- .../test/integration.shard/test_driver.dart | 7 +++ .../vmservice_integration_test.dart | 9 ++++ 25 files changed, 211 insertions(+), 33 deletions(-) create mode 100644 packages/flutter_tools/lib/src/base/dds.dart diff --git a/dev/tracing_tests/test/image_painting_event_test.dart b/dev/tracing_tests/test/image_painting_event_test.dart index ccc05fd181..a786cce741 100644 --- a/dev/tracing_tests/test/image_painting_event_test.dart +++ b/dev/tracing_tests/test/image_painting_event_test.dart @@ -38,6 +38,10 @@ void main() { await binding.endOfFrame; }); + tearDownAll(() { + vmService.dispose(); + }); + test('Image painting events - deduplicates across frames', () async { final Completer completer = Completer(); vmService.onExtensionEvent.first.then(completer.complete); diff --git a/packages/flutter_tools/lib/src/base/dds.dart b/packages/flutter_tools/lib/src/base/dds.dart new file mode 100644 index 0000000000..4eb1e66797 --- /dev/null +++ b/packages/flutter_tools/lib/src/base/dds.dart @@ -0,0 +1,49 @@ +// 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:async'; + +import 'package:dds/dds.dart' as dds; +import 'package:meta/meta.dart'; + +import 'io.dart' as io; +import 'logger.dart'; + +/// Helper class to launch a [dds.DartDevelopmentService]. Allows for us to +/// mock out this functionality for testing purposes. +class DartDevelopmentService { + DartDevelopmentService({@required this.logger}); + + final Logger logger; + dds.DartDevelopmentService _ddsInstance; + + Future startDartDevelopmentService( + Uri observatoryUri, + bool ipv6, + ) async { + final Uri ddsUri = Uri( + scheme: 'http', + host: (ipv6 ? + io.InternetAddress.loopbackIPv6 : + io.InternetAddress.loopbackIPv4 + ).host, + port: 0, + ); + logger.printTrace( + 'Launching a Dart Developer Service (DDS) instance at $ddsUri, ' + 'connecting to VM service at $observatoryUri.', + ); + try { + _ddsInstance = await dds.DartDevelopmentService.startDartDevelopmentService( + observatoryUri, + serviceUri: ddsUri, + ); + logger.printTrace('DDS is listening at ${_ddsInstance.uri}.'); + } on dds.DartDevelopmentServiceException catch (e) { + logger.printError('Warning: Failed to start DDS: ${e.message}'); + } + } + + Future shutdown() async => await _ddsInstance?.shutdown(); +} diff --git a/packages/flutter_tools/lib/src/commands/attach.dart b/packages/flutter_tools/lib/src/commands/attach.dart index d327867275..1dcc3ac4e3 100644 --- a/packages/flutter_tools/lib/src/commands/attach.dart +++ b/packages/flutter_tools/lib/src/commands/attach.dart @@ -100,6 +100,7 @@ class AttachCommand extends FlutterCommand { 'and progress in machine friendly format.', ); usesTrackWidgetCreation(verboseHelp: verboseHelp); + addDdsOptions(verboseHelp: verboseHelp); hotRunnerFactory ??= HotRunnerFactory(); } @@ -372,7 +373,7 @@ class AttachCommand extends FlutterCommand { ); flutterDevice.observatoryUris = observatoryUris; final List flutterDevices = [flutterDevice]; - final DebuggingOptions debuggingOptions = DebuggingOptions.enabled(getBuildInfo()); + final DebuggingOptions debuggingOptions = DebuggingOptions.enabled(getBuildInfo(), disableDds: boolArg('disable-dds')); return getBuildInfo().isDebug ? hotRunnerFactory.build( diff --git a/packages/flutter_tools/lib/src/commands/drive.dart b/packages/flutter_tools/lib/src/commands/drive.dart index abdf34d8ee..b644fa8113 100644 --- a/packages/flutter_tools/lib/src/commands/drive.dart +++ b/packages/flutter_tools/lib/src/commands/drive.dart @@ -244,6 +244,10 @@ class DriveCommand extends RunCommandBase { throwToolExit('Application failed to start. Will not run test. Quitting.', exitCode: 1); } observatoryUri = result.observatoryUri.toString(); + // TODO(bkonyi): add web support (https://github.com/flutter/flutter/issues/61259) + if (!isWebPlatform) { + await device.dds.startDartDevelopmentService(Uri.parse(observatoryUri), ipv6); + } } else { globals.printStatus('Will connect to already running application instance.'); observatoryUri = stringArg('use-existing-app'); diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 9dab37574e..a63ae6043f 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -218,6 +218,7 @@ class RunCommand extends RunCommandBase { 'Currently this is only supported on Android devices. This option ' 'cannot be paired with --use-application-binary.' ); + addDdsOptions(verboseHelp: verboseHelp); } @override @@ -383,6 +384,7 @@ class RunCommand extends RunCommandBase { buildInfo, startPaused: boolArg('start-paused'), disableServiceAuthCodes: boolArg('disable-service-auth-codes'), + disableDds: boolArg('disable-dds'), dartFlags: stringArg('dart-flags') ?? '', useTestFonts: boolArg('use-test-fonts'), enableSoftwareRendering: boolArg('enable-software-rendering'), diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 2e1d158af4..9104748015 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -16,6 +16,7 @@ import 'application_package.dart'; import 'artifacts.dart'; import 'base/config.dart'; import 'base/context.dart'; +import 'base/dds.dart'; import 'base/file_system.dart'; import 'base/io.dart'; import 'base/logger.dart'; @@ -547,6 +548,12 @@ abstract class Device { /// Get the port forwarder for this device. DevicePortForwarder get portForwarder; + /// Get the DDS instance for this device. + DartDevelopmentService get dds => _dds ??= DartDevelopmentService( + logger: globals.logger, + ); + DartDevelopmentService _dds; + /// Clear the device's logs. void clearLogs(); @@ -720,6 +727,7 @@ class DebuggingOptions { this.buildInfo, { this.startPaused = false, this.disableServiceAuthCodes = false, + this.disableDds = false, this.dartFlags = '', this.enableSoftwareRendering = false, this.skiaDeterministicRendering = false, @@ -760,6 +768,7 @@ class DebuggingOptions { startPaused = false, dartFlags = '', disableServiceAuthCodes = false, + disableDds = false, enableSoftwareRendering = false, skiaDeterministicRendering = false, traceSkia = false, @@ -779,6 +788,7 @@ class DebuggingOptions { final bool startPaused; final String dartFlags; final bool disableServiceAuthCodes; + final bool disableDds; final bool enableSoftwareRendering; final bool skiaDeterministicRendering; final bool traceSkia; diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart index 5fefc73f57..59789e6093 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart @@ -52,6 +52,13 @@ Future _kDefaultFuchsiaIsolateDiscoveryConnector(Uri uri) return connectToVmService(uri); } +Future _kDefaultDartDevelopmentServiceStarter( + Device device, + Uri observatoryUri, +) async { + await device.dds.startDartDevelopmentService(observatoryUri, true); +} + /// Read the log for a particular device. class _FuchsiaLogReader extends DeviceLogReader { _FuchsiaLogReader(this._device, this._systemClock, [this._app]); @@ -695,6 +702,7 @@ class FuchsiaIsolateDiscoveryProtocol { this._device, this._isolateName, [ this._vmServiceConnector = _kDefaultFuchsiaIsolateDiscoveryConnector, + this._ddsStarter = _kDefaultDartDevelopmentServiceStarter, this._pollOnce = false, ]); @@ -704,6 +712,7 @@ class FuchsiaIsolateDiscoveryProtocol { final String _isolateName; final Completer _foundUri = Completer(); final Future Function(Uri) _vmServiceConnector; + final Future Function(Device, Uri) _ddsStarter; // whether to only poll once. final bool _pollOnce; Timer _pollingTimer; @@ -746,6 +755,7 @@ class FuchsiaIsolateDiscoveryProtocol { final int localPort = await _device.portForwarder.forward(port); try { final Uri uri = Uri.parse('http://[$_ipv6Loopback]:$localPort'); + await _ddsStarter(_device, uri); service = await _vmServiceConnector(uri); _ports[port] = service; } on SocketException catch (err) { diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart index 51d650fdd8..6747178781 100644 --- a/packages/flutter_tools/lib/src/ios/devices.dart +++ b/packages/flutter_tools/lib/src/ios/devices.dart @@ -437,7 +437,7 @@ class IOSDevice extends Device { ); final Uri localUri = await fallbackDiscovery.discover( assumedDevicePort: assumedObservatoryPort, - deivce: this, + device: this, usesIpv6: ipv6, hostVmservicePort: debuggingOptions.hostVmServicePort, packageId: packageId, diff --git a/packages/flutter_tools/lib/src/ios/fallback_discovery.dart b/packages/flutter_tools/lib/src/ios/fallback_discovery.dart index 32634f5d13..9a9e7e7061 100644 --- a/packages/flutter_tools/lib/src/ios/fallback_discovery.dart +++ b/packages/flutter_tools/lib/src/ios/fallback_discovery.dart @@ -67,7 +67,7 @@ class FallbackDiscovery { Future discover({ @required int assumedDevicePort, @required String packageId, - @required Device deivce, + @required Device device, @required bool usesIpv6, @required int hostVmservicePort, @required String packageName, @@ -84,7 +84,7 @@ class FallbackDiscovery { try { final Uri result = await _mDnsObservatoryDiscovery.getObservatoryUri( packageId, - deivce, + device, usesIpv6: usesIpv6, hostVmservicePort: hostVmservicePort, ); diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 1efc91fc13..c387cb392b 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -211,6 +211,8 @@ class FlutterDevice { ReloadMethod reloadMethod, GetSkSLMethod getSkSLMethod, PrintStructuredErrorLogMethod printStructuredErrorLogMethod, + bool disableDds = false, + bool ipv6 = false, }) { final Completer completer = Completer(); StreamSubscription subscription; @@ -221,7 +223,12 @@ class FlutterDevice { globals.printTrace('Connecting to service protocol: $observatoryUri'); isWaitingForVm = true; vm_service.VmService service; - + if (!disableDds) { + await device.dds.startDartDevelopmentService( + observatoryUri, + ipv6, + ); + } try { service = await connectToVmService( observatoryUri, @@ -943,6 +950,7 @@ abstract class ResidentRunner { Future exit() async { _exited = true; await shutdownDevtools(); + await shutdownDartDevelopmentService(); await stopEchoingDeviceLog(); await preExit(); await exitApp(); @@ -950,6 +958,7 @@ abstract class ResidentRunner { Future detach() async { await shutdownDevtools(); + await shutdownDartDevelopmentService(); await stopEchoingDeviceLog(); await preExit(); appFinished(); @@ -1112,6 +1121,14 @@ abstract class ResidentRunner { ); } + Future shutdownDartDevelopmentService() async { + await Future.wait( + flutterDevices.map>( + (FlutterDevice device) => device.device?.dds?.shutdown() + ).where((Future element) => element != null) + ); + } + @protected void cacheInitialDillCompilation() { if (_dillOutputPath != null) { @@ -1165,9 +1182,11 @@ abstract class ResidentRunner { reloadSources: reloadSources, restart: restart, compileExpression: compileExpression, + disableDds: debuggingOptions.disableDds, reloadMethod: reloadMethod, getSkSLMethod: getSkSLMethod, printStructuredErrorLogMethod: printStructuredErrorLog, + ipv6: ipv6, ); // This will wait for at least one flutter view before returning. final Status status = globals.logger.startProgress( diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 7cd47bb14c..80db831694 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -289,6 +289,19 @@ abstract class FlutterCommand extends Command { _usesPortOption = true; } + void addDdsOptions({@required bool verboseHelp}) { + argParser.addFlag( + 'disable-dds', + hide: !verboseHelp, + help: 'Disable the Dart Developer Service (DDS). This flag should only be provided' + ' when attaching to an application with an existing DDS instance (e.g.,' + ' attaching to an application currently connected to by "flutter run") or' + ' when running certain tests.\n' + 'Note: passing this flag may degrade IDE functionality if a DDS instance is not' + ' already connected to the target application.' + ); + } + /// Gets the vmservice port provided to in the 'observatory-port' or /// 'host-vmservice-port option. /// diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart index 7b7c83c0b6..7365bd8f41 100644 --- a/packages/flutter_tools/lib/src/test/flutter_platform.dart +++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart @@ -4,6 +4,7 @@ import 'dart:async'; +import 'package:dds/dds.dart'; import 'package:meta/meta.dart'; import 'package:package_config/package_config.dart'; import 'package:stream_channel/stream_channel.dart'; @@ -514,7 +515,7 @@ class FlutterPlatform extends PlatformPlugin { Uri processObservatoryUri; _pipeStandardStreamsToConsole( process, - reportObservatoryUri: (Uri detectedUri) { + reportObservatoryUri: (Uri detectedUri) async { assert(processObservatoryUri == null); assert(explicitObservatoryPort == null || explicitObservatoryPort == detectedUri.port); @@ -527,13 +528,14 @@ class FlutterPlatform extends PlatformPlugin { globals.printTrace('test $ourTestCount: using observatory uri $detectedUri from pid ${process.pid}'); } processObservatoryUri = detectedUri; + await DartDevelopmentService.startDartDevelopmentService(processObservatoryUri); { globals.printTrace('Connecting to service protocol: $processObservatoryUri'); final Future localVmService = connectToVmService(processObservatoryUri, compileExpression: _compileExpressionService); - localVmService.then((vm_service.VmService vmservice) { + unawaited(localVmService.then((vm_service.VmService vmservice) { globals.printTrace('Successfully connected to service protocol: $processObservatoryUri'); - }); + })); } gotProcessObservatoryUri.complete(); watcher?.handleStartedProcess( @@ -870,8 +872,9 @@ class FlutterPlatform extends PlatformPlugin { void _pipeStandardStreamsToConsole( Process process, { void startTimeoutTimer(), - void reportObservatoryUri(Uri uri), + Future reportObservatoryUri(Uri uri), }) { + const String observatoryString = 'Observatory listening on '; for (final Stream> stream in >>[ process.stderr, @@ -881,7 +884,7 @@ class FlutterPlatform extends PlatformPlugin { .transform(utf8.decoder) .transform(const LineSplitter()) .listen( - (String line) { + (String line) async { if (line == _kStartTimeoutTimerMessage) { if (startTimeoutTimer != null) { startTimeoutTimer(); @@ -894,7 +897,7 @@ class FlutterPlatform extends PlatformPlugin { try { final Uri uri = Uri.parse(line.substring(observatoryString.length)); if (reportObservatoryUri != null) { - reportObservatoryUri(uri); + await reportObservatoryUri(uri); } } on Exception catch (error) { globals.printError('Could not parse shell observatory port message: $error'); diff --git a/packages/flutter_tools/lib/src/tracing.dart b/packages/flutter_tools/lib/src/tracing.dart index 7458eb0ea0..4e1c2c2242 100644 --- a/packages/flutter_tools/lib/src/tracing.dart +++ b/packages/flutter_tools/lib/src/tracing.dart @@ -22,12 +22,6 @@ class Tracing { Tracing(this.vmService); static const String firstUsefulFrameEventName = kFirstFrameRasterizedEventName; - - static Future connect(Uri uri) async { - final vm_service.VmService observatory = await connectToVmService(uri); - return Tracing(observatory); - } - final vm_service.VmService vmService; Future startTracing() async { diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 933cdff206..34ef0416b2 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -11,10 +11,11 @@ dependencies: # To update these, use "flutter update-packages --force-upgrade". archive: 2.0.13 args: 1.6.0 - dwds: 5.0.0 completion: 0.2.2 coverage: 0.14.0 crypto: 2.1.5 + dds: 1.2.1 + dwds: 5.0.0 file: 5.2.1 flutter_template_images: 1.0.1 http: 0.12.1 @@ -63,6 +64,7 @@ dependencies: http_parser: 3.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 0.3.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.6.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + json_rpc_2: 2.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 0.11.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.8 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" mime: 0.9.6+3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -107,4 +109,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: f9c4 +# PUBSPEC CHECKSUM: 7f8b diff --git a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart index ee034fa51b..21f988a767 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart @@ -7,6 +7,7 @@ import 'dart:io'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/common.dart'; +import 'package:flutter_tools/src/base/dds.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/logger.dart'; @@ -71,6 +72,7 @@ void main() { FakeDeviceLogReader mockLogReader; MockPortForwarder portForwarder; + MockDartDevelopmentService mockDds; MockAndroidDevice device; MockHttpClient httpClient; @@ -78,6 +80,7 @@ void main() { mockLogReader = FakeDeviceLogReader(); portForwarder = MockPortForwarder(); device = MockAndroidDevice(); + mockDds = MockDartDevelopmentService(); when(device.portForwarder) .thenReturn(portForwarder); when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort'))) @@ -86,6 +89,8 @@ void main() { .thenReturn([ForwardedPort(hostPort, devicePort)]); when(portForwarder.unforward(any)) .thenAnswer((_) async => null); + when(device.dds).thenReturn(mockDds); + when(mockDds.startDartDevelopmentService(any, false)).thenReturn(null); final HttpClientRequest httpClientRequest = MockHttpClientRequest(); httpClient = MockHttpClient(); @@ -286,11 +291,14 @@ void main() { const int hostPort = 42; final FakeDeviceLogReader mockLogReader = FakeDeviceLogReader(); final MockPortForwarder portForwarder = MockPortForwarder(); + final MockDartDevelopmentService mockDds = MockDartDevelopmentService(); final MockAndroidDevice device = MockAndroidDevice(); final MockHotRunner mockHotRunner = MockHotRunner(); final MockHotRunnerFactory mockHotRunnerFactory = MockHotRunnerFactory(); when(device.portForwarder) .thenReturn(portForwarder); + when(device.dds) + .thenReturn(mockDds); when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort'))) .thenAnswer((_) async => hostPort); when(portForwarder.forwardedPorts) @@ -309,6 +317,7 @@ void main() { )).thenReturn(mockHotRunner); when(mockHotRunner.exited).thenReturn(false); when(mockHotRunner.isWaitingForObservatory).thenReturn(false); + when(mockDds.startDartDevelopmentService(any, false)).thenReturn(null); testDeviceManager.addDevice(device); when(device.getLogReader(includePastLogs: anyNamed('includePastLogs'))) @@ -359,10 +368,14 @@ void main() { const int hostPort = 42; final FakeDeviceLogReader mockLogReader = FakeDeviceLogReader(); final MockPortForwarder portForwarder = MockPortForwarder(); + final MockDartDevelopmentService mockDds = MockDartDevelopmentService(); final MockIOSDevice device = MockIOSDevice(); final MockHotRunner mockHotRunner = MockHotRunner(); final MockHotRunnerFactory mockHotRunnerFactory = MockHotRunnerFactory(); - when(device.portForwarder).thenReturn(portForwarder); + when(device.portForwarder) + .thenReturn(portForwarder); + when(device.dds) + .thenReturn(mockDds); when(device.getLogReader(includePastLogs: anyNamed('includePastLogs'))) .thenAnswer((_) => mockLogReader); when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort'))) @@ -383,6 +396,7 @@ void main() { )).thenReturn(mockHotRunner); when(mockHotRunner.exited).thenReturn(false); when(mockHotRunner.isWaitingForObservatory).thenReturn(false); + when(mockDds.startDartDevelopmentService(any, false)).thenReturn(null); testDeviceManager.addDevice(device); @@ -415,6 +429,7 @@ void main() { setUp(() { portForwarder = MockPortForwarder(); + final MockDartDevelopmentService mockDds = MockDartDevelopmentService(); device = MockAndroidDevice(); when(device.portForwarder) @@ -425,6 +440,10 @@ void main() { .thenReturn([ForwardedPort(hostPort, devicePort)]); when(portForwarder.unforward(any)) .thenAnswer((_) async => null); + when(device.dds) + .thenReturn(mockDds); + when(mockDds.startDartDevelopmentService(any, any)) + .thenReturn(null); }); testUsingContext('succeeds in ipv4 mode', () async { @@ -795,6 +814,7 @@ class TestHotRunnerFactory extends HotRunnerFactory { } } +class MockDartDevelopmentService extends Mock implements DartDevelopmentService {} class MockProcessManager extends Mock implements ProcessManager {} class MockProcess extends Mock implements Process {} class MockHttpClientRequest extends Mock implements HttpClientRequest {} diff --git a/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart index 0ce92dad6e..231860567c 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart @@ -8,6 +8,7 @@ import 'package:file/memory.dart'; import 'package:file_testing/file_testing.dart'; import 'package:flutter_tools/src/android/android_device.dart'; import 'package:flutter_tools/src/base/common.dart'; +import 'package:flutter_tools/src/base/dds.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/platform.dart'; @@ -67,6 +68,12 @@ void main() { tryToDelete(tempDir); }); + void applyDdsMocks(Device device) { + final MockDartDevelopmentService mockDds = MockDartDevelopmentService(); + when(device.dds).thenReturn(mockDds); + when(mockDds.startDartDevelopmentService(any, any)).thenReturn(null); + } + testUsingContext('returns 1 when test file is not found', () async { testDeviceManager.addDevice(MockDevice()); @@ -188,7 +195,9 @@ void main() { }); testUsingContext('returns 0 when test ends successfully', () async { - testDeviceManager.addDevice(MockAndroidDevice()); + final MockAndroidDevice mockDevice = MockAndroidDevice(); + applyDdsMocks(mockDevice); + testDeviceManager.addDevice(mockDevice); final String testApp = globals.fs.path.join(tempDir.path, 'test', 'e2e.dart'); final String testFile = globals.fs.path.join(tempDir.path, 'test_driver', 'e2e_test.dart'); @@ -227,7 +236,9 @@ void main() { }); testUsingContext('returns exitCode set by test runner', () async { - testDeviceManager.addDevice(MockDevice()); + final MockDevice mockDevice = MockDevice(); + applyDdsMocks(mockDevice); + testDeviceManager.addDevice(mockDevice); final String testApp = globals.fs.path.join(tempDir.path, 'test', 'e2e.dart'); final String testFile = globals.fs.path.join(tempDir.path, 'test_driver', 'e2e_test.dart'); @@ -374,6 +385,7 @@ void main() { Future appStarterSetup() async { final Device mockDevice = MockDevice(); + applyDdsMocks(mockDevice); testDeviceManager.addDevice(mockDevice); final FakeDeviceLogReader mockDeviceLogReader = FakeDeviceLogReader(); @@ -509,6 +521,7 @@ void main() { Future appStarterSetup() async { final Device mockDevice = MockDevice(); + applyDdsMocks(mockDevice); testDeviceManager.addDevice(mockDevice); final FakeDeviceLogReader mockDeviceLogReader = FakeDeviceLogReader(); @@ -792,5 +805,5 @@ class MockDevice extends Mock implements Device { } class MockAndroidDevice extends Mock implements AndroidDevice { } - +class MockDartDevelopmentService extends Mock implements DartDevelopmentService { } class MockLaunchResult extends Mock implements LaunchResult { } diff --git a/packages/flutter_tools/test/general.shard/cold_test.dart b/packages/flutter_tools/test/general.shard/cold_test.dart index 3800969862..3141df0705 100644 --- a/packages/flutter_tools/test/general.shard/cold_test.dart +++ b/packages/flutter_tools/test/general.shard/cold_test.dart @@ -139,6 +139,8 @@ class TestFlutterDevice extends FlutterDevice { ReloadMethod reloadMethod, GetSkSLMethod getSkSLMethod, PrintStructuredErrorLogMethod printStructuredErrorLogMethod, + bool disableDds = false, + bool ipv6 = false, }) async { throw exception; } diff --git a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart index 89772864dd..e2f7826cd5 100644 --- a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart +++ b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart @@ -728,6 +728,7 @@ void main() { fuchsiaDevice, expectedIsolateName, (Uri uri) async => fakeVmServiceHost.vmService, + (Device device, Uri uri) => null, true, // only poll once. ); diff --git a/packages/flutter_tools/test/general.shard/hot_test.dart b/packages/flutter_tools/test/general.shard/hot_test.dart index 6d9c011b95..3d11937fb7 100644 --- a/packages/flutter_tools/test/general.shard/hot_test.dart +++ b/packages/flutter_tools/test/general.shard/hot_test.dart @@ -578,6 +578,8 @@ class TestFlutterDevice extends FlutterDevice { ReloadMethod reloadMethod, GetSkSLMethod getSkSLMethod, PrintStructuredErrorLogMethod printStructuredErrorLogMethod, + bool disableDds = false, + bool ipv6 = false, }) async { throw exception; } diff --git a/packages/flutter_tools/test/general.shard/ios/fallback_discovery_test.dart b/packages/flutter_tools/test/general.shard/ios/fallback_discovery_test.dart index 06f901eaa0..3b33719493 100644 --- a/packages/flutter_tools/test/general.shard/ios/fallback_discovery_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/fallback_discovery_test.dart @@ -61,7 +61,7 @@ void main() { expect(await fallbackDiscovery.discover( assumedDevicePort: 23, - deivce: null, + device: null, hostVmservicePort: 1, packageId: null, usesIpv6: false, @@ -86,7 +86,7 @@ void main() { }); expect(await fallbackDiscovery.discover( assumedDevicePort: 23, - deivce: null, + device: null, hostVmservicePort: 1, packageId: null, usesIpv6: false, @@ -117,7 +117,7 @@ void main() { expect(await fallbackDiscovery.discover( assumedDevicePort: 23, - deivce: null, + device: null, hostVmservicePort: 1, packageId: 'hello', usesIpv6: false, @@ -139,7 +139,7 @@ void main() { expect(await fallbackDiscovery.discover( assumedDevicePort: 23, - deivce: null, + device: null, hostVmservicePort: 1, packageId: 'hello', usesIpv6: false, @@ -161,7 +161,7 @@ void main() { expect(await fallbackDiscovery.discover( assumedDevicePort: 23, - deivce: null, + device: null, hostVmservicePort: 1, packageId: 'hello', usesIpv6: false, diff --git a/packages/flutter_tools/test/general.shard/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_runner_test.dart index 17918bd43a..84660240df 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart @@ -4,6 +4,7 @@ import 'dart:async'; +import 'package:flutter_tools/src/base/dds.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:vm_service/vm_service.dart' as vm_service; import 'package:file/memory.dart'; @@ -1645,8 +1646,11 @@ void main() { testUsingContext('connect sets up log reader', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: []); final MockDevice mockDevice = MockDevice(); + final MockDartDevelopmentService mockDds = MockDartDevelopmentService(); final MockDeviceLogReader mockLogReader = MockDeviceLogReader(); when(mockDevice.getLogReader(app: anyNamed('app'))).thenReturn(mockLogReader); + when(mockDevice.dds).thenReturn(mockDds); + when(mockDds.startDartDevelopmentService(any, any)).thenReturn(null); final TestFlutterDevice flutterDevice = TestFlutterDevice( mockDevice, @@ -1678,6 +1682,7 @@ void main() { } class MockFlutterDevice extends Mock implements FlutterDevice {} +class MockDartDevelopmentService extends Mock implements DartDevelopmentService {} class MockVMService extends Mock implements vm_service.VmService {} class MockDevFS extends Mock implements DevFS {} class MockDevice extends Mock implements Device {} diff --git a/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart b/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart index 8878c3517e..9e2ebdece3 100644 --- a/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart +++ b/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart @@ -21,7 +21,14 @@ void main() { tempDir = createResolvedTempDirectorySync('attach_test.'); await _project.setUpIn(tempDir); _flutterRun = FlutterRunTestDriver(tempDir, logPrefix: ' RUN '); - _flutterAttach = FlutterRunTestDriver(tempDir, logPrefix: 'ATTACH '); + _flutterAttach = FlutterRunTestDriver( + tempDir, + logPrefix: 'ATTACH ', + // Only one DDS instance can be connected to the VM service at a time. + // DDS can also only initialize if the VM service doesn't have any existing + // clients, so we'll just let _flutterRun be responsible for spawning DDS. + spawnDdsInstance: false, + ); }); tearDown(() async { @@ -58,7 +65,11 @@ void main() { await _flutterRun.run(withDebugger: true); await _flutterAttach.attach(_flutterRun.vmServicePort); await _flutterAttach.quit(); - _flutterAttach = FlutterRunTestDriver(tempDir, logPrefix: 'ATTACH-2'); + _flutterAttach = FlutterRunTestDriver( + tempDir, + logPrefix: 'ATTACH-2', + spawnDdsInstance: false, + ); await _flutterAttach.attach(_flutterRun.vmServicePort); await _flutterAttach.hotReload(); }); diff --git a/packages/flutter_tools/test/integration.shard/flutter_run_with_error_test.dart b/packages/flutter_tools/test/integration.shard/flutter_run_with_error_test.dart index 5ffc17c0a4..075141eb4b 100644 --- a/packages/flutter_tools/test/integration.shard/flutter_run_with_error_test.dart +++ b/packages/flutter_tools/test/integration.shard/flutter_run_with_error_test.dart @@ -25,7 +25,6 @@ void main() { }); tearDown(() async { - await _flutter.stop(); tryToDelete(tempDir); }); @@ -51,7 +50,6 @@ void main() { // Complete anyway in case we don't see the 'Another exception' line. completer.complete(); }); - await _flutter.stop(); expect(stdout.toString(), contains(_exceptionStart)); @@ -62,7 +60,6 @@ void main() { await _flutter.run(startPaused: true, withDebugger: true, structuredErrors: true, chrome: true); await _flutter.resume(); - final Completer completer = Completer(); bool lineFound = false; diff --git a/packages/flutter_tools/test/integration.shard/test_driver.dart b/packages/flutter_tools/test/integration.shard/test_driver.dart index a6a9ed9bd5..f1230fbd9f 100644 --- a/packages/flutter_tools/test/integration.shard/test_driver.dart +++ b/packages/flutter_tools/test/integration.shard/test_driver.dart @@ -428,6 +428,7 @@ class FlutterRunTestDriver extends FlutterTestDriver { FlutterRunTestDriver( Directory projectFolder, { String logPrefix, + this.spawnDdsInstance = true, }) : super(projectFolder, logPrefix: logPrefix); String _currentRunningAppId; @@ -447,6 +448,8 @@ class FlutterRunTestDriver extends FlutterTestDriver { 'run', if (!chrome) '--disable-service-auth-codes', + if (!spawnDdsInstance) + '--disable-dds', '--machine', '-d', if (chrome) @@ -479,6 +482,8 @@ class FlutterRunTestDriver extends FlutterTestDriver { [ 'attach', '--machine', + if (!spawnDdsInstance) + '--disable-dds', '-d', 'flutter-tester', '--debug-port', @@ -680,6 +685,8 @@ class FlutterRunTestDriver extends FlutterTestDriver { void _throwErrorResponse(String message) { throw '$message\n\n$_lastResponse\n\n${_errorBuffer.toString()}'.trim(); } + + final bool spawnDdsInstance; } class FlutterTestTestDriver extends FlutterTestDriver { diff --git a/packages/flutter_tools/test/integration.shard/vmservice_integration_test.dart b/packages/flutter_tools/test/integration.shard/vmservice_integration_test.dart index ae2c6f395b..4a16f27103 100644 --- a/packages/flutter_tools/test/integration.shard/vmservice_integration_test.dart +++ b/packages/flutter_tools/test/integration.shard/vmservice_integration_test.dart @@ -39,6 +39,15 @@ void main() { tryToDelete(tempDir); }); + test('getSupportedProtocols includes DDS', () async { + final ProtocolList protocolList = + await vmService.getSupportedProtocols(); + expect(protocolList.protocols, hasLength(2)); + for (final Protocol protocol in protocolList.protocols) { + expect(protocol.protocolName, anyOf('VM Service', 'DDS')); + } + }); + test('flutterVersion can be called', () async { final Response response = await vmService.callServiceExtension('s0.flutterVersion');