From 209bdcb6695b6bffa7b21ce6fc8fcf2310f5ecd0 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Tue, 5 May 2020 10:47:01 -0700 Subject: [PATCH] [flutter_tools] remove flutter view cache (#56223) Remove caching of FlutterView. Perhaps the FlutterView RPC might return an empty list if the VM is not quite up yet? We had some old logic to poll the flutter views RPC for up to 200ms. That doesn't seem like a great approach, so instead we could forgo it entirely and trust that either the views come up before the developer tries to interact, or we crash. --- .../src/build_runner/resident_web_runner.dart | 5 + .../lib/src/resident_runner.dart | 120 ++++++-------- packages/flutter_tools/lib/src/run_cold.dart | 5 +- packages/flutter_tools/lib/src/run_hot.dart | 51 +++--- packages/flutter_tools/lib/src/vmservice.dart | 32 +++- .../flutter_tools/lib/src/web/web_device.dart | 5 +- .../commands.shard/hermetic/attach_test.dart | 4 - .../permeable/devices_test.dart | 2 +- .../coverage_collector_test.dart | 2 - .../test/general.shard/devfs_test.dart | 2 - .../fuchsia/fuchsia_device_test.dart | 1 - .../test/general.shard/hot_test.dart | 147 +++++++++++++---- .../general.shard/resident_runner_test.dart | 155 +++++++++--------- .../resident_web_runner_test.dart | 25 --- .../general.shard/terminal_handler_test.dart | 4 +- .../test/general.shard/vmservice_test.dart | 63 ++++++- .../test/general.shard/web/devices_test.dart | 6 +- packages/flutter_tools/test/src/common.dart | 7 +- 18 files changed, 355 insertions(+), 281 deletions(-) diff --git a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart index 282e3de6cf1..a2370bd0b9d 100644 --- a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart @@ -190,6 +190,11 @@ abstract class ResidentWebRunner extends ResidentRunner { globals.printStatus('For a more detailed help message, press "h". $quitMessage'); } + @override + Future> listFlutterViews() async { + return []; + } + @override Future debugDumpApp() async { try { diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index 4e89ffab230..43ea0f480a0 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -226,38 +226,13 @@ class FlutterDevice { return completer.future; } - Future refreshViews() async { - if (vmService == null) { - return; - } - final List updatedViews = await vmService.getFlutterViews(); - _views - ..clear() - ..addAll(updatedViews); - } - final List _views = []; - - List get views { - if (vmService == null) { - return []; - } - if (viewFilter != null) { - return [ - for (final FlutterView flutterView in _views) - if (flutterView.uiIsolate.name.contains(viewFilter)) - flutterView - ]; - } - return _views; - } - Future exitApps({ @visibleForTesting Duration timeoutDelay = const Duration(seconds: 10), }) async { if (!device.supportsFlutterExit) { return device.stopApp(package); } - await refreshViews(); + final List views = await vmService.getFlutterViews(); if (views == null || views.isEmpty) { return device.stopApp(package); } @@ -333,6 +308,7 @@ class FlutterDevice { final Uri deviceAssetsDirectoryUri = devFS.baseUri.resolveUri( globals.fs.path.toUri(getAssetBuildDirectory())); assert(deviceAssetsDirectoryUri != null); + final List views = await vmService.getFlutterViews(); await Future.wait(views.map>( (FlutterView view) => vmService.setAssetDirectory( assetsDirectory: deviceAssetsDirectoryUri, @@ -343,6 +319,7 @@ class FlutterDevice { } Future debugDumpApp() async { + final List views = await vmService.getFlutterViews(); for (final FlutterView view in views) { await vmService.flutterDebugDumpApp( isolateId: view.uiIsolate.id, @@ -351,6 +328,7 @@ class FlutterDevice { } Future debugDumpRenderTree() async { + final List views = await vmService.getFlutterViews(); for (final FlutterView view in views) { await vmService.flutterDebugDumpRenderTree( isolateId: view.uiIsolate.id, @@ -359,6 +337,7 @@ class FlutterDevice { } Future debugDumpLayerTree() async { + final List views = await vmService.getFlutterViews(); for (final FlutterView view in views) { await vmService.flutterDebugDumpLayerTree( isolateId: view.uiIsolate.id, @@ -367,6 +346,7 @@ class FlutterDevice { } Future debugDumpSemanticsTreeInTraversalOrder() async { + final List views = await vmService.getFlutterViews(); for (final FlutterView view in views) { await vmService.flutterDebugDumpSemanticsTreeInTraversalOrder( isolateId: view.uiIsolate.id, @@ -375,6 +355,7 @@ class FlutterDevice { } Future debugDumpSemanticsTreeInInverseHitTestOrder() async { + final List views = await vmService.getFlutterViews(); for (final FlutterView view in views) { await vmService.flutterDebugDumpSemanticsTreeInInverseHitTestOrder( isolateId: view.uiIsolate.id, @@ -383,6 +364,7 @@ class FlutterDevice { } Future toggleDebugPaintSizeEnabled() async { + final List views = await vmService.getFlutterViews(); for (final FlutterView view in views) { await vmService.flutterToggleDebugPaintSizeEnabled( isolateId: view.uiIsolate.id, @@ -391,6 +373,7 @@ class FlutterDevice { } Future toggleDebugCheckElevationsEnabled() async { + final List views = await vmService.getFlutterViews(); for (final FlutterView view in views) { await vmService.flutterToggleDebugCheckElevationsEnabled( isolateId: view.uiIsolate.id, @@ -399,6 +382,7 @@ class FlutterDevice { } Future debugTogglePerformanceOverlayOverride() async { + final List views = await vmService.getFlutterViews(); for (final FlutterView view in views) { await vmService.flutterTogglePerformanceOverlayOverride( isolateId: view.uiIsolate.id, @@ -407,6 +391,7 @@ class FlutterDevice { } Future toggleWidgetInspector() async { + final List views = await vmService.getFlutterViews(); for (final FlutterView view in views) { await vmService.flutterToggleWidgetInspector( isolateId: view.uiIsolate.id, @@ -415,6 +400,7 @@ class FlutterDevice { } Future toggleProfileWidgetBuilds() async { + final List views = await vmService.getFlutterViews(); for (final FlutterView view in views) { await vmService.flutterToggleProfileWidgetBuilds( isolateId: view.uiIsolate.id, @@ -423,6 +409,7 @@ class FlutterDevice { } Future togglePlatform({ String from }) async { + final List views = await vmService.getFlutterViews(); final String to = nextPlatform(from, featureFlags); for (final FlutterView view in views) { await vmService.flutterPlatformOverride( @@ -764,15 +751,17 @@ abstract class ResidentRunner { Future> invokeFlutterExtensionRpcRawOnFirstIsolate( String method, { Map params, - }) { + }) async { + final List views = await flutterDevices + .first + .vmService.getFlutterViews(); return flutterDevices .first .vmService .invokeFlutterExtensionRpcRaw( method, args: params, - isolateId: flutterDevices - .first.views + isolateId: views .first.uiIsolate.id ); } @@ -811,13 +800,23 @@ abstract class ResidentRunner { throw Exception('Canvaskit not supported by this runner.'); } + /// List the attached flutter views. + Future> listFlutterViews() async { + return (await Future.wait( + flutterDevices.map((FlutterDevice d) => d.vmService.getFlutterViews())) + ).expand((List views) => views).toList(); + } + /// Write the SkSL shaders to a zip file in build directory. Future writeSkSL() async { if (!supportsWriteSkSL) { throw Exception('writeSkSL is not supported by this runner.'); } + final List views = await flutterDevices + .first + .vmService.getFlutterViews(); final Map data = await flutterDevices.first.vmService.getSkSLs( - viewId: flutterDevices.first.views.first.id, + viewId: views.first.id, ); if (data.isEmpty) { globals.logger.printStatus( @@ -902,78 +901,61 @@ abstract class ResidentRunner { appFinished(); } - Future refreshViews() async { - final List> futures = >[ - for (final FlutterDevice device in flutterDevices) device.refreshViews(), - ]; - await Future.wait(futures); - } - Future debugDumpApp() async { - await refreshViews(); for (final FlutterDevice device in flutterDevices) { await device.debugDumpApp(); } } Future debugDumpRenderTree() async { - await refreshViews(); for (final FlutterDevice device in flutterDevices) { await device.debugDumpRenderTree(); } } Future debugDumpLayerTree() async { - await refreshViews(); for (final FlutterDevice device in flutterDevices) { await device.debugDumpLayerTree(); } } Future debugDumpSemanticsTreeInTraversalOrder() async { - await refreshViews(); for (final FlutterDevice device in flutterDevices) { await device.debugDumpSemanticsTreeInTraversalOrder(); } } Future debugDumpSemanticsTreeInInverseHitTestOrder() async { - await refreshViews(); for (final FlutterDevice device in flutterDevices) { await device.debugDumpSemanticsTreeInInverseHitTestOrder(); } } Future debugToggleDebugPaintSizeEnabled() async { - await refreshViews(); for (final FlutterDevice device in flutterDevices) { await device.toggleDebugPaintSizeEnabled(); } } Future debugToggleDebugCheckElevationsEnabled() async { - await refreshViews(); for (final FlutterDevice device in flutterDevices) { await device.toggleDebugCheckElevationsEnabled(); } } Future debugTogglePerformanceOverlayOverride() async { - await refreshViews(); for (final FlutterDevice device in flutterDevices) { await device.debugTogglePerformanceOverlayOverride(); } } Future debugToggleWidgetInspector() async { - await refreshViews(); for (final FlutterDevice device in flutterDevices) { await device.toggleWidgetInspector(); } } Future debugToggleProfileWidgetBuilds() async { - await refreshViews(); for (final FlutterDevice device in flutterDevices) { await device.toggleProfileWidgetBuilds(); } @@ -997,11 +979,12 @@ abstract class ResidentRunner { 'flutter', 'png', ); + final List views = await device + .vmService.getFlutterViews(); try { if (supportsServiceProtocol && isRunningDebug) { - await device.refreshViews(); try { - for (final FlutterView view in device.views) { + for (final FlutterView view in views) { await device.vmService.flutterDebugAllowBanner( false, isolateId: view.uiIsolate.id, @@ -1018,7 +1001,7 @@ abstract class ResidentRunner { } finally { if (supportsServiceProtocol && isRunningDebug) { try { - for (final FlutterView view in device.views) { + for (final FlutterView view in views) { await device.vmService.flutterDebugAllowBanner( true, isolateId: view.uiIsolate.id, @@ -1043,9 +1026,10 @@ abstract class ResidentRunner { } Future debugTogglePlatform() async { - await refreshViews(); - final String isolateId = flutterDevices - .first.views.first.uiIsolate.id; + final List views = await flutterDevices + .first + .vmService.getFlutterViews(); + final String isolateId = views.first.uiIsolate.id; final String from = await flutterDevices .first.vmService.flutterPlatformOverride( isolateId: isolateId, @@ -1078,10 +1062,8 @@ abstract class ResidentRunner { if (!debuggingOptions.debuggingEnabled) { throw 'The service protocol is not enabled.'; } - _finished = Completer(); - - bool viewFound = false; + // Listen for service protocol connection to close. for (final FlutterDevice device in flutterDevices) { await device.connect( reloadSources: reloadSources, @@ -1089,21 +1071,16 @@ abstract class ResidentRunner { compileExpression: compileExpression, reloadMethod: reloadMethod, ); - await device.refreshViews(); - if (device.views.isNotEmpty) { - viewFound = true; + // This will wait for at least one flutter view before returning. + final Status status = globals.logger.startProgress( + 'Waiting for ${device.device.name} to report its views...', + timeout: const Duration(milliseconds: 200), + ); + try { + await device.vmService.getFlutterViews(); + } finally { + status.stop(); } - } - if (!viewFound) { - if (flutterDevices.length == 1) { - throw 'No Flutter view is available on ${flutterDevices.first.device.name}.'; - } - throw 'No Flutter view is available on any device ' - '(${flutterDevices.map((FlutterDevice device) => device.device.name).join(', ')}).'; - } - - // Listen for service protocol connection to close. - for (final FlutterDevice device in flutterDevices) { // This hooks up callbacks for when the connection stops in the future. // We don't want to wait for them. We don't handle errors in those callbacks' // futures either because they just print to logger and is not critical. @@ -1372,8 +1349,7 @@ class TerminalHandler { } return false; case 'l': - final List views = residentRunner.flutterDevices - .expand((FlutterDevice d) => d.views).toList(); + final List views = await residentRunner.listFlutterViews(); globals.printStatus('Connected ${pluralize('view', views.length)}:'); for (final FlutterView v in views) { globals.printStatus('${v.uiIsolate.name} (${v.uiIsolate.id})', indent: 2); diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart index 177942604f0..8bfd230b467 100644 --- a/packages/flutter_tools/lib/src/run_cold.dart +++ b/packages/flutter_tools/lib/src/run_cold.dart @@ -95,7 +95,6 @@ class ColdRunner extends ResidentRunner { continue; } await device.initLogReader(); - await device.refreshViews(); globals.printTrace('Connected to ${device.device.name}'); } @@ -145,9 +144,9 @@ class ColdRunner extends ResidentRunner { for (final FlutterDevice device in flutterDevices) { await device.initLogReader(); } - await refreshViews(); for (final FlutterDevice device in flutterDevices) { - for (final FlutterView view in device.views) { + final List views = await device.vmService.getFlutterViews(); + for (final FlutterView view in views) { globals.printTrace('Connected to $view.'); } } diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index 38b36fb0cbc..3d8b3123eba 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart @@ -198,7 +198,8 @@ class HotRunner extends ResidentRunner { } for (final FlutterDevice device in flutterDevices) { - for (final FlutterView view in device.views) { + final List views = await device.vmService.getFlutterViews(); + for (final FlutterView view in views) { await device.vmService.flutterFastReassemble( classId, isolateId: view.uiIsolate.id, @@ -277,14 +278,14 @@ class HotRunner extends ResidentRunner { return 3; } - await refreshViews(); for (final FlutterDevice device in flutterDevices) { // VM must have accepted the kernel binary, there will be no reload // report, so we let incremental compiler know that source code was accepted. if (device.generator != null) { device.generator.accept(); } - for (final FlutterView view in device.views) { + final List views = await device.vmService.getFlutterViews(); + for (final FlutterView view in views) { globals.printTrace('Connected to $view.'); } } @@ -474,15 +475,15 @@ class HotRunner extends ResidentRunner { Uri main, Uri assetsDirectory, ) async { + final List views = await device.vmService.getFlutterViews(); await Future.wait(>[ - for (final FlutterView view in device.views) + for (final FlutterView view in views) device.vmService.runInView( viewId: view.id, main: main, assetsDirectory: assetsDirectory, ), ]); - await device.refreshViews(); } Future _launchFromDevFS(String mainScript) async { @@ -501,7 +502,8 @@ class HotRunner extends ResidentRunner { if (benchmarkMode) { futures.clear(); for (final FlutterDevice device in flutterDevices) { - for (final FlutterView view in device.views) { + final List views = await device.vmService.getFlutterViews(); + for (final FlutterView view in views) { futures.add(device.vmService .flushUIThreadTasks(uiIsolateId: view.uiIsolate.id)); } @@ -514,9 +516,6 @@ class HotRunner extends ResidentRunner { String reason, bool benchmarkMode = false, }) async { - globals.printTrace('Refreshing active FlutterViews before restarting.'); - await refreshViews(); - final Stopwatch restartTimer = Stopwatch()..start(); // TODO(aam): Add generator reset logic once we switch to using incremental // compiler for full application recompilation on restart. @@ -541,7 +540,8 @@ class HotRunner extends ResidentRunner { final List> operations = >[]; for (final FlutterDevice device in flutterDevices) { final Set uiIsolatesIds = {}; - for (final FlutterView view in device.views) { + final List views = await device.vmService.getFlutterViews(); + for (final FlutterView view in views) { if (view.uiIsolate == null) { continue; } @@ -810,7 +810,8 @@ class HotRunner extends ResidentRunner { void Function(String message) onSlow, }) async { for (final FlutterDevice device in flutterDevices) { - for (final FlutterView view in device.views) { + final List views = await device.vmService.getFlutterViews(); + for (final FlutterView view in views) { if (view.uiIsolate == null) { return OperationResult(2, 'Application isolate not found', fatal: true); } @@ -818,10 +819,6 @@ class HotRunner extends ResidentRunner { } final Stopwatch reloadTimer = Stopwatch()..start(); - - globals.printTrace('Refreshing active FlutterViews before reloading.'); - await refreshViews(); - final Stopwatch devFSTimer = Stopwatch()..start(); final UpdateFSReport updatedDevFS = await _updateDevFS(); // Record time it took to synchronize to DevFS. @@ -912,14 +909,6 @@ class HotRunner extends ResidentRunner { // Record time it took for the VM to reload the sources. _addBenchmarkData('hotReloadVMReloadMilliseconds', vmReloadTimer.elapsed.inMilliseconds); final Stopwatch reassembleTimer = Stopwatch()..start(); - - // Reload the isolate data. - await Future.wait(>[ - for (final FlutterDevice device in flutterDevices) - device.refreshViews() - ]); - - globals.printTrace('Evicting dirty assets'); await _evictDirtyAssets(); // Check if any isolates are paused and reassemble those @@ -930,7 +919,8 @@ class HotRunner extends ResidentRunner { int pausedIsolatesFound = 0; bool failedReassemble = false; for (final FlutterDevice device in flutterDevices) { - for (final FlutterView view in device.views) { + final List views = await device.vmService.getFlutterViews(); + for (final FlutterView view in views) { // Check if the isolate is paused, and if so, don't reassemble. Ignore the // PostPauseEvent event - the client requesting the pause will resume the app. final vm_service.Isolate isolate = await device.vmService @@ -1055,11 +1045,7 @@ class HotRunner extends ResidentRunner { final StringBuffer message = StringBuffer(); bool plural; if (pausedIsolatesFound == 1) { - if (flutterDevices.length == 1 && flutterDevices.single.views.length == 1) { - message.write('The application is '); - } else { - message.write('An isolate is '); - } + message.write('The application is '); plural = false; } else { message.write('$pausedIsolatesFound isolates are '); @@ -1121,13 +1107,14 @@ class HotRunner extends ResidentRunner { } } - Future _evictDirtyAssets() { + Future _evictDirtyAssets() async { final List>> futures = >>[]; for (final FlutterDevice device in flutterDevices) { if (device.devFS.assetPathsToEvict.isEmpty) { continue; } - if (device.views.first.uiIsolate == null) { + final List views = await device.vmService.getFlutterViews(); + if (views.first.uiIsolate == null) { globals.printError('Application isolate not found for $device'); continue; } @@ -1136,7 +1123,7 @@ class HotRunner extends ResidentRunner { device.vmService .flutterEvictAsset( assetPath, - isolateId: device.views.first.uiIsolate.id, + isolateId: views.first.uiIsolate.id, ) ); } diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart index 9b34f082729..14bc858cff4 100644 --- a/packages/flutter_tools/lib/src/vmservice.dart +++ b/packages/flutter_tools/lib/src/vmservice.dart @@ -684,15 +684,29 @@ extension FlutterVmService on vm_service.VmService { } /// List all [FlutterView]s attached to the current VM. - Future> getFlutterViews() async { - final vm_service.Response response = await callMethod( - kListViewsMethod, - ); - final List rawViews = response.json['views'] as List; - return [ - for (final Object rawView in rawViews) - FlutterView.parse(rawView as Map) - ]; + /// + /// If this returns an empty list, it will poll forever unless [returnEarly] + /// is set to true. + /// + /// By default, the poll duration is 50 milliseconds. + Future> getFlutterViews({ + bool returnEarly = false, + Duration delay = const Duration(milliseconds: 50), + }) async { + while (true) { + final vm_service.Response response = await callMethod( + kListViewsMethod, + ); + final List rawViews = response.json['views'] as List; + final List views = [ + for (final Object rawView in rawViews) + FlutterView.parse(rawView as Map) + ]; + if (views.isNotEmpty || returnEarly) { + return views; + } + await Future.delayed(delay); + } } /// Attempt to retrieve the isolate with id [isolateId], or `null` if it has diff --git a/packages/flutter_tools/lib/src/web/web_device.dart b/packages/flutter_tools/lib/src/web/web_device.dart index fbb39c74d39..f984858060b 100644 --- a/packages/flutter_tools/lib/src/web/web_device.dart +++ b/packages/flutter_tools/lib/src/web/web_device.dart @@ -66,7 +66,7 @@ abstract class ChromiumDevice extends Device { bool get supportsStartPaused => true; @override - bool get supportsFlutterExit => true; + bool get supportsFlutterExit => false; @override bool get supportsScreenshot => false; @@ -343,6 +343,9 @@ class WebServerDevice extends Device { @override Future isLatestBuildInstalled(ApplicationPackage app) async => true; + @override + bool get supportsFlutterExit => false; + @override Future get isLocalEmulator async => false; 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 b79f653a903..bdf8dff081b 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart @@ -685,7 +685,6 @@ VMServiceConnector getFakeVmServiceFactory({ final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( requests: [ FakeVmServiceRequest( - id: '1', method: kListViewsMethod, args: null, jsonResponse: { @@ -698,14 +697,12 @@ VMServiceConnector getFakeVmServiceFactory({ }, ), FakeVmServiceRequest( - id: '2', method: 'getVM', args: null, jsonResponse: vm_service.VM.parse({}) .toJson(), ), FakeVmServiceRequest( - id: '3', method: '_createDevFS', args: { 'fsName': globals.fs.currentDirectory.absolute.path, @@ -715,7 +712,6 @@ VMServiceConnector getFakeVmServiceFactory({ }, ), FakeVmServiceRequest( - id: '4', method: kListViewsMethod, args: null, jsonResponse: { diff --git a/packages/flutter_tools/test/commands.shard/permeable/devices_test.dart b/packages/flutter_tools/test/commands.shard/permeable/devices_test.dart index 4be5edab051..9c0bccdf50f 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/devices_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/devices_test.dart @@ -65,7 +65,7 @@ void main() { 'hotRestart': true, 'screenshot': false, 'fastStart': false, - 'flutterExit': true, + 'flutterExit': false, 'hardwareRendering': false, 'startPaused': true } diff --git a/packages/flutter_tools/test/general.shard/coverage_collector_test.dart b/packages/flutter_tools/test/general.shard/coverage_collector_test.dart index fe7f4ca6f7e..5648feb2997 100644 --- a/packages/flutter_tools/test/general.shard/coverage_collector_test.dart +++ b/packages/flutter_tools/test/general.shard/coverage_collector_test.dart @@ -12,7 +12,6 @@ void main() { final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( requests: [ FakeVmServiceRequest( - id: '1', method: 'getVM', jsonResponse: (vm_service.VM.parse({}) ..isolates = [ @@ -23,7 +22,6 @@ void main() { ).toJson(), ), const FakeVmServiceRequest( - id: '2', method: 'getScripts', args: { 'isolateId': '1', diff --git a/packages/flutter_tools/test/general.shard/devfs_test.dart b/packages/flutter_tools/test/general.shard/devfs_test.dart index 80e99633353..2a631ee5791 100644 --- a/packages/flutter_tools/test/general.shard/devfs_test.dart +++ b/packages/flutter_tools/test/general.shard/devfs_test.dart @@ -129,7 +129,6 @@ void main() { final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( requests: [ FakeVmServiceRequest( - id: '1', method: '_createDevFS', args: { 'fsName': 'test', @@ -202,7 +201,6 @@ void main() { final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( requests: [ FakeVmServiceRequest( - id: '1', method: '_createDevFS', args: { 'fsName': 'test', 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 4f0bd414e33..dd91b10c69b 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 @@ -637,7 +637,6 @@ void main() { final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( requests: [ FakeVmServiceRequest( - id: '1', method: kListViewsMethod, jsonResponse: { 'views': [ diff --git a/packages/flutter_tools/test/general.shard/hot_test.dart b/packages/flutter_tools/test/general.shard/hot_test.dart index dae17c35767..9c18f5693ce 100644 --- a/packages/flutter_tools/test/general.shard/hot_test.dart +++ b/packages/flutter_tools/test/general.shard/hot_test.dart @@ -21,6 +21,36 @@ import '../src/common.dart'; import '../src/context.dart'; import '../src/mocks.dart'; +final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate( + id: '1', + pauseEvent: vm_service.Event( + kind: vm_service.EventKind.kResume, + timestamp: 0 + ), + breakpoints: [], + exceptionPauseMode: null, + libraries: [], + livePorts: 0, + name: 'test', + number: '1', + pauseOnExit: false, + runnable: true, + startTime: 0, +); + +final FlutterView fakeFlutterView = FlutterView( + id: 'a', + uiIsolate: fakeUnpausedIsolate, +); + +final FakeVmServiceRequest listViews = FakeVmServiceRequest( + method: kListViewsMethod, + jsonResponse: { + 'views': [ + fakeFlutterView.toJson(), + ], + }, +); void main() { group('validateReloadReport', () { testUsingContext('invalid', () async { @@ -172,42 +202,72 @@ void main() { testUsingContext('Does hot restarts when all devices support it', () async { final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: [ - const FakeVmServiceRequest( - id: '1', - method: kListViewsMethod, - jsonResponse: { - 'views': [], - } - ), - const FakeVmServiceRequest( - id: '2', - method: kListViewsMethod, - jsonResponse: { - 'views': [], - } + listViews, + FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': fakeUnpausedIsolate.id, + }, + jsonResponse: fakeUnpausedIsolate.toJson(), ), FakeVmServiceRequest( - id: '3', method: 'getVM', jsonResponse: vm_service.VM.parse({}).toJson() ), + listViews, + FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': fakeUnpausedIsolate.id, + }, + jsonResponse: fakeUnpausedIsolate.toJson(), + ), FakeVmServiceRequest( - id: '4', method: 'getVM', jsonResponse: vm_service.VM.parse({}).toJson() ), + listViews, + listViews, const FakeVmServiceRequest( - id: '5', - method: kListViewsMethod, - jsonResponse: { - 'views': [], + method: 'streamListen', + args: { + 'streamId': 'Isolate', } ), const FakeVmServiceRequest( - id: '6', - method: kListViewsMethod, - jsonResponse: { - 'views': [], + method: 'streamListen', + args: { + 'streamId': 'Isolate', + } + ), + FakeVmServiceStreamResponse( + streamId: 'Isolate', + event: vm_service.Event( + timestamp: 0, + kind: vm_service.EventKind.kIsolateRunnable, + ) + ), + FakeVmServiceStreamResponse( + streamId: 'Isolate', + event: vm_service.Event( + timestamp: 0, + kind: vm_service.EventKind.kIsolateRunnable, + ) + ), + FakeVmServiceRequest( + method: kRunInViewMethod, + args: { + 'viewId': fakeFlutterView.id, + 'mainScript': 'lib/main.dart.dill', + 'assetDirectory': 'build/flutter_assets', + } + ), + FakeVmServiceRequest( + method: kRunInViewMethod, + args: { + 'viewId': fakeFlutterView.id, + 'mainScript': 'lib/main.dart.dill', + 'assetDirectory': 'build/flutter_assets', } ), ]); @@ -257,25 +317,40 @@ void main() { testUsingContext('hot restart supported', () async { // Setup mocks final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': fakeUnpausedIsolate.id, + }, + jsonResponse: fakeUnpausedIsolate.toJson(), + ), + FakeVmServiceRequest( + method: 'getVM', + jsonResponse: vm_service.VM.parse({}).toJson(), + ), + listViews, const FakeVmServiceRequest( - id: '1', - method: kListViewsMethod, - jsonResponse: { - 'views': [], + method: 'streamListen', + args: { + 'streamId': 'Isolate', } ), FakeVmServiceRequest( - id: '2', - method: 'getVM', - jsonResponse: vm_service.VM.parse({}).toJson() - ), - const FakeVmServiceRequest( - id: '3', - method: kListViewsMethod, - jsonResponse: { - 'views': [], + method: kRunInViewMethod, + args: { + 'viewId': fakeFlutterView.id, + 'mainScript': 'lib/main.dart.dill', + 'assetDirectory': 'build/flutter_assets', } ), + FakeVmServiceStreamResponse( + streamId: 'Isolate', + event: vm_service.Event( + timestamp: 0, + kind: vm_service.EventKind.kIsolateRunnable, + ) + ), ]); final MockDevice mockDevice = MockDevice(); when(mockDevice.supportsHotReload).thenReturn(true); 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 1ce0659f6c2..59be8f7b828 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart @@ -70,6 +70,15 @@ final FlutterView fakeFlutterView = FlutterView( uiIsolate: fakeUnpausedIsolate, ); +final FakeVmServiceRequest listViews = FakeVmServiceRequest( + method: kListViewsMethod, + jsonResponse: { + 'views': [ + fakeFlutterView.toJson(), + ], + }, +); + void main() { final Uri testUri = Uri.parse('foo://bar'); Testbed testbed; @@ -127,10 +136,6 @@ void main() { ); }); when(mockFlutterDevice.devFS).thenReturn(mockDevFS); - when(mockFlutterDevice.views).thenReturn([ - // NB: still using mock FlutterDevice. - fakeFlutterView, - ]); when(mockFlutterDevice.device).thenReturn(mockDevice); when(mockFlutterDevice.stopEchoingDeviceLog()).thenAnswer((Invocation invocation) async { }); when(mockFlutterDevice.observatoryUris).thenAnswer((_) => Stream.value(testUri)); @@ -146,7 +151,6 @@ void main() { when(mockFlutterDevice.vmService).thenAnswer((Invocation invocation) { return fakeVmServiceHost.vmService; }); - when(mockFlutterDevice.refreshViews()).thenAnswer((Invocation invocation) async { }); when(mockFlutterDevice.reloadSources(any, pause: anyNamed('pause'))).thenAnswer((Invocation invocation) async { return >[ Future.value(vm_service.ReloadReport.parse({ @@ -166,15 +170,7 @@ void main() { test('FlutterDevice can list views with a filter', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ - FakeVmServiceRequest( - id: '1', - method: kListViewsMethod, - jsonResponse: { - 'views': [ - fakeFlutterView.toJson(), - ], - }, - ), + listViews, ]); final MockDevice mockDevice = MockDevice(); final FlutterDevice flutterDevice = FlutterDevice( @@ -184,14 +180,13 @@ void main() { ); flutterDevice.vmService = fakeVmServiceHost.vmService; - - await flutterDevice.refreshViews(); - - expect(flutterDevice.views, isEmpty); })); test('ResidentRunner can attach to device successfully', () => testbed.run(() async { - fakeVmServiceHost = FakeVmServiceHost(requests: []); + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + listViews, + ]); final Completer onConnectionInfo = Completer.sync(); final Completer onAppStart = Completer.sync(); final Future result = residentRunner.attach( @@ -207,12 +202,15 @@ void main() { expect(onConnectionInfo.isCompleted, true); expect((await connectionInfo).baseUri, 'foo://bar'); expect(onAppStart.isCompleted, true); + expect(fakeVmServiceHost.hasRemainingExpectations, false); })); test('ResidentRunner can attach to device successfully with --fast-start', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + listViews, + listViews, FakeVmServiceRequest( - id: '1', method: 'getIsolate', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -220,19 +218,17 @@ void main() { jsonResponse: fakeUnpausedIsolate.toJson(), ), FakeVmServiceRequest( - id: '2', method: 'getVM', jsonResponse: vm_service.VM.parse({}).toJson(), ), + listViews, const FakeVmServiceRequest( - id: '3', method: 'streamListen', args: { 'streamId': 'Isolate', } ), FakeVmServiceRequest( - id: '4', method: kRunInViewMethod, args: { 'viewId': fakeFlutterView.id, @@ -284,10 +280,15 @@ void main() { expect(onConnectionInfo.isCompleted, true); expect((await connectionInfo).baseUri, 'foo://bar'); expect(onAppStart.isCompleted, true); + expect(fakeVmServiceHost.hasRemainingExpectations, false); })); test('ResidentRunner can handle an RPC exception from hot reload', () => testbed.run(() async { - fakeVmServiceHost = FakeVmServiceHost(requests: []); + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + listViews, + listViews, + ]); when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async { return 'Example'; }); @@ -329,15 +330,18 @@ void main() { cdKey(CustomDimensions.hotEventEmulator): 'false', cdKey(CustomDimensions.hotEventFullRestart): 'false', })).called(1); + expect(fakeVmServiceHost.hasRemainingExpectations, false); }, overrides: { Usage: () => MockUsage(), })); test('ResidentRunner can send target platform to analytics from hot reload', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ - // Not all requests are present due to existing mocks + listViews, + listViews, + listViews, + listViews, FakeVmServiceRequest( - id: '1', method: 'getIsolate', args: { 'isolateId': '1', @@ -345,7 +349,6 @@ void main() { jsonResponse: fakeUnpausedIsolate.toJson(), ), FakeVmServiceRequest( - id: '2', method: 'ext.flutter.reassemble', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -381,10 +384,11 @@ void main() { })); test('ResidentRunner can send target platform to analytics from full restart', () => testbed.run(() async { - // Not all requests are present due to existing mocks fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + listViews, + listViews, FakeVmServiceRequest( - id: '1', method: 'getIsolate', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -392,19 +396,17 @@ void main() { jsonResponse: fakeUnpausedIsolate.toJson(), ), FakeVmServiceRequest( - id: '2', method: 'getVM', jsonResponse: vm_service.VM.parse({}).toJson(), ), + listViews, const FakeVmServiceRequest( - id: '3', method: 'streamListen', args: { 'streamId': 'Isolate', }, ), FakeVmServiceRequest( - id: '4', method: kRunInViewMethod, args: { 'viewId': fakeFlutterView.id, @@ -445,12 +447,16 @@ void main() { containsPair(cdKey(CustomDimensions.hotEventTargetPlatform), getNameForTargetPlatform(TargetPlatform.android_arm)), ); + expect(fakeVmServiceHost.hasRemainingExpectations, false); }, overrides: { Usage: () => MockUsage(), })); test('ResidentRunner Can handle an RPC exception from hot restart', () => testbed.run(() async { - fakeVmServiceHost = FakeVmServiceHost(requests: []); + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + listViews, + ]); when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async { return 'Example'; }); @@ -493,6 +499,7 @@ void main() { cdKey(CustomDimensions.hotEventEmulator): 'false', cdKey(CustomDimensions.hotEventFullRestart): 'true', })).called(1); + expect(fakeVmServiceHost.hasRemainingExpectations, false); }, overrides: { Usage: () => MockUsage(), })); @@ -578,14 +585,15 @@ void main() { test('ResidentRunner does support CanvasKit', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: []); + expect(() => residentRunner.toggleCanvaskit(), throwsA(isA())); })); test('ResidentRunner handles writeSkSL returning no data', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ - FakeVmServiceRequest( - id: '1', + listViews, + FakeVmServiceRequest( method: kGetSkSLsMethod, args: { 'viewId': fakeFlutterView.id, @@ -593,17 +601,18 @@ void main() { jsonResponse: { 'SkSLs': {} } - ) + ), ]); await residentRunner.writeSkSL(); expect(testLogger.statusText, contains('No data was receieved')); + expect(fakeVmServiceHost.hasRemainingExpectations, false); })); test('ResidentRunner can write SkSL data to a unique file with engine revision, platform, and device name', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, FakeVmServiceRequest( - id: '1', method: kGetSkSLsMethod, args: { 'viewId': fakeFlutterView.id, @@ -634,8 +643,8 @@ void main() { test('ResidentRunner can take screenshot on debug device', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, FakeVmServiceRequest( - id: '1', method: 'ext.flutter.debugAllowBanner', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -643,7 +652,6 @@ void main() { }, ), FakeVmServiceRequest( - id: '2', method: 'ext.flutter.debugAllowBanner', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -676,8 +684,8 @@ void main() { test('ResidentRunner bails taking screenshot on debug device if debugAllowBanner throws RpcError', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, FakeVmServiceRequest( - id: '1', method: 'ext.flutter.debugAllowBanner', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -696,8 +704,8 @@ void main() { test('ResidentRunner bails taking screenshot on debug device if debugAllowBanner during second request', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, FakeVmServiceRequest( - id: '1', method: 'ext.flutter.debugAllowBanner', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -705,7 +713,6 @@ void main() { }, ), FakeVmServiceRequest( - id: '2', method: 'ext.flutter.debugAllowBanner', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -719,12 +726,13 @@ void main() { await residentRunner.screenshot(mockFlutterDevice); expect(testLogger.errorText, contains('Error')); + expect(fakeVmServiceHost.hasRemainingExpectations, false); })); test('ResidentRunner bails taking screenshot on debug device if takeScreenshot throws', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, FakeVmServiceRequest( - id: '1', method: 'ext.flutter.debugAllowBanner', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -732,7 +740,6 @@ void main() { }, ), FakeVmServiceRequest( - id: '2', method: 'ext.flutter.debugAllowBanner', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -754,10 +761,13 @@ void main() { expect(() => residentRunner.screenshot(mockFlutterDevice), throwsAssertionError); + expect(fakeVmServiceHost.hasRemainingExpectations, false); })); test('ResidentRunner does not toggle banner in non-debug mode', () => testbed.run(() async { - fakeVmServiceHost = FakeVmServiceHost(requests: []); + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + ]); residentRunner = HotRunner( [ mockFlutterDevice, @@ -781,7 +791,6 @@ void main() { test('FlutterDevice will not exit a paused isolate', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ FakeVmServiceRequest( - id: '1', method: '_flutter.listViews', jsonResponse: { 'views': [ @@ -790,7 +799,6 @@ void main() { }, ), FakeVmServiceRequest( - id: '2', method: 'getIsolate', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -800,7 +808,6 @@ void main() { ]); final TestFlutterDevice flutterDevice = TestFlutterDevice( mockDevice, - [ fakeFlutterView ], ); flutterDevice.vmService = fakeVmServiceHost.vmService; when(mockDevice.supportsFlutterExit).thenReturn(true); @@ -814,7 +821,6 @@ void main() { test('FlutterDevice will call stopApp if the exit request times out', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ FakeVmServiceRequest( - id: '1', method: '_flutter.listViews', jsonResponse: { 'views': [ @@ -823,7 +829,6 @@ void main() { }, ), FakeVmServiceRequest( - id: '2', method: 'getIsolate', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -831,7 +836,6 @@ void main() { jsonResponse: fakeUnpausedIsolate.toJson(), ), FakeVmServiceRequest( - id: '3', method: 'ext.flutter.exit', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -842,7 +846,6 @@ void main() { ]); final TestFlutterDevice flutterDevice = TestFlutterDevice( mockDevice, - [ fakeFlutterView ], ); flutterDevice.vmService = fakeVmServiceHost.vmService; when(mockDevice.supportsFlutterExit).thenReturn(true); @@ -858,7 +861,6 @@ void main() { test('FlutterDevice will exit an un-paused isolate', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ FakeVmServiceRequest( - id: '1', method: kListViewsMethod, jsonResponse: { 'views': [ @@ -867,7 +869,6 @@ void main() { }, ), FakeVmServiceRequest( - id: '2', method: 'getIsolate', args: { 'isolateId': fakeUnpausedIsolate.id @@ -875,7 +876,6 @@ void main() { jsonResponse: fakeUnpausedIsolate.toJson(), ), FakeVmServiceRequest( - id: '3', method: 'ext.flutter.exit', args: { 'isolateId': fakeUnpausedIsolate.id, @@ -885,7 +885,6 @@ void main() { ]); final TestFlutterDevice flutterDevice = TestFlutterDevice( mockDevice, - [fakeFlutterView ], ); flutterDevice.vmService = fakeVmServiceHost.vmService; @@ -897,18 +896,10 @@ void main() { expect(fakeVmServiceHost.hasRemainingExpectations, false); })); - test('ResidentRunner refreshViews calls flutter device', () => testbed.run(() async { - fakeVmServiceHost = FakeVmServiceHost(requests: []); - await residentRunner.refreshViews(); - - verify(mockFlutterDevice.refreshViews()).called(1); - })); - test('ResidentRunner debugDumpApp calls flutter device', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: []); await residentRunner.debugDumpApp(); - verify(mockFlutterDevice.refreshViews()).called(1); verify(mockFlutterDevice.debugDumpApp()).called(1); })); @@ -916,7 +907,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: []); await residentRunner.debugDumpRenderTree(); - verify(mockFlutterDevice.refreshViews()).called(1); verify(mockFlutterDevice.debugDumpRenderTree()).called(1); })); @@ -924,7 +914,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: []); await residentRunner.debugDumpLayerTree(); - verify(mockFlutterDevice.refreshViews()).called(1); verify(mockFlutterDevice.debugDumpLayerTree()).called(1); })); @@ -932,7 +921,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: []); await residentRunner.debugDumpSemanticsTreeInTraversalOrder(); - verify(mockFlutterDevice.refreshViews()).called(1); verify(mockFlutterDevice.debugDumpSemanticsTreeInTraversalOrder()).called(1); })); @@ -940,7 +928,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: []); await residentRunner.debugDumpSemanticsTreeInInverseHitTestOrder(); - verify(mockFlutterDevice.refreshViews()).called(1); verify(mockFlutterDevice.debugDumpSemanticsTreeInInverseHitTestOrder()).called(1); })); @@ -948,7 +935,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: []); await residentRunner.debugToggleDebugPaintSizeEnabled(); - verify(mockFlutterDevice.refreshViews()).called(1); verify(mockFlutterDevice.toggleDebugPaintSizeEnabled()).called(1); })); @@ -956,7 +942,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: []); await residentRunner.debugToggleDebugCheckElevationsEnabled(); - verify(mockFlutterDevice.refreshViews()).called(1); verify(mockFlutterDevice.toggleDebugCheckElevationsEnabled()).called(1); })); @@ -964,7 +949,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: []); await residentRunner.debugTogglePerformanceOverlayOverride(); - verify(mockFlutterDevice.refreshViews()).called(1); verify(mockFlutterDevice.debugTogglePerformanceOverlayOverride()).called(1); })); @@ -972,7 +956,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: []); await residentRunner.debugToggleWidgetInspector(); - verify(mockFlutterDevice.refreshViews()).called(1); verify(mockFlutterDevice.toggleWidgetInspector()).called(1); })); @@ -980,12 +963,14 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: []); await residentRunner.debugToggleProfileWidgetBuilds(); - verify(mockFlutterDevice.refreshViews()).called(1); verify(mockFlutterDevice.toggleProfileWidgetBuilds()).called(1); })); test('HotRunner writes vm service file when providing debugging option', () => testbed.run(() async { - fakeVmServiceHost = FakeVmServiceHost(requests: []); + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + listViews, + ]); setWsAddress(testUri, fakeVmServiceHost.vmService); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); residentRunner = HotRunner( @@ -1007,7 +992,10 @@ void main() { })); test('HotRunner unforwards device ports', () => testbed.run(() async { - fakeVmServiceHost = FakeVmServiceHost(requests: []); + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + listViews, + ]); final MockDevicePortForwarder mockPortForwarder = MockDevicePortForwarder(); when(mockDevice.portForwarder).thenReturn(mockPortForwarder); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); @@ -1035,7 +1023,10 @@ void main() { })); test('HotRunner handles failure to write vmservice file', () => testbed.run(() async { - fakeVmServiceHost = FakeVmServiceHost(requests: []); + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + listViews, + ]); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); residentRunner = HotRunner( [ @@ -1053,13 +1044,16 @@ void main() { await residentRunner.run(); expect(testLogger.errorText, contains('Failed to write vmservice-out-file at foo')); + expect(fakeVmServiceHost.hasRemainingExpectations, false); }, overrides: { FileSystem: () => ThrowingForwardingFileSystem(MemoryFileSystem()), })); test('ColdRunner writes vm service file when providing debugging option', () => testbed.run(() async { - fakeVmServiceHost = FakeVmServiceHost(requests: []); + fakeVmServiceHost = FakeVmServiceHost(requests: [ + listViews, + ]); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); setWsAddress(testUri, fakeVmServiceHost.vmService); residentRunner = ColdRunner( @@ -1078,6 +1072,7 @@ void main() { await residentRunner.run(); expect(await globals.fs.file('foo').readAsString(), testUri.toString()); + expect(fakeVmServiceHost.hasRemainingExpectations, false); })); test('FlutterDevice uses dartdevc configuration when targeting web', () => testbed.run(() async { @@ -1117,7 +1112,6 @@ void main() { final TestFlutterDevice flutterDevice = TestFlutterDevice( mockDevice, - [], observatoryUris: Stream.value(testUri), ); @@ -1153,14 +1147,11 @@ class MockUsage extends Mock implements Usage {} class MockProcessManager extends Mock implements ProcessManager {} class TestFlutterDevice extends FlutterDevice { - TestFlutterDevice(Device device, this.views, { Stream observatoryUris }) + TestFlutterDevice(Device device, { Stream observatoryUris }) : super(device, buildInfo: BuildInfo.debug) { _observatoryUris = observatoryUris; } - @override - final List views; - @override Stream get observatoryUris => _observatoryUris; Stream _observatoryUris; diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart index 7392b914d1d..eaaa55aaf2b 100644 --- a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart @@ -36,14 +36,12 @@ import '../src/testbed.dart'; const List kAttachLogExpectations = [ FakeVmServiceRequest( - id: '1', method: 'streamListen', args: { 'streamId': 'Stdout', }, ), FakeVmServiceRequest( - id: '2', method: 'streamListen', args: { 'streamId': 'Stderr', @@ -53,14 +51,12 @@ const List kAttachLogExpectations = const List kAttachIsolateExpectations = [ FakeVmServiceRequest( - id: '3', method: 'streamListen', args: { 'streamId': 'Isolate' } ), FakeVmServiceRequest( - id: '4', method: 'registerService', args: { 'service': 'reloadSources', @@ -362,7 +358,6 @@ void main() { ...kAttachExpectations, const FakeVmServiceRequest( method: 'hotRestart', - id: '5', jsonResponse: { 'type': 'Success', } @@ -439,7 +434,6 @@ void main() { ...kAttachExpectations, const FakeVmServiceRequest( method: 'hotRestart', - id: '5', jsonResponse: { 'type': 'Success', } @@ -670,7 +664,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'hotRestart', jsonResponse: { 'type': 'Failed', @@ -693,7 +686,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'hotRestart', // Failed response, errorCode: RPCErrorCodes.kInternalError, @@ -725,7 +717,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'ext.flutter.debugDumpApp', args: { 'isolateId': null, @@ -747,7 +738,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'ext.flutter.debugDumpLayerTree', args: { 'isolateId': null, @@ -769,7 +759,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'ext.flutter.debugDumpRenderTree', args: { 'isolateId': null, @@ -791,7 +780,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder', args: { 'isolateId': null, @@ -813,7 +801,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder', args: { 'isolateId': null, @@ -836,7 +823,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'ext.flutter.debugPaint', args: { 'isolateId': null, @@ -846,7 +832,6 @@ void main() { }, ), const FakeVmServiceRequest( - id: '6', method: 'ext.flutter.debugPaint', args: { 'isolateId': null, @@ -874,7 +859,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'ext.flutter.showPerformanceOverlay', args: { 'isolateId': null, @@ -884,7 +868,6 @@ void main() { }, ), const FakeVmServiceRequest( - id: '6', method: 'ext.flutter.showPerformanceOverlay', args: { 'isolateId': null, @@ -911,7 +894,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'ext.flutter.inspector.show', args: { 'isolateId': null, @@ -921,7 +903,6 @@ void main() { }, ), const FakeVmServiceRequest( - id: '6', method: 'ext.flutter.inspector.show', args: { 'isolateId': null, @@ -948,7 +929,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'ext.flutter.profileWidgetBuilds', args: { 'isolateId': null, @@ -958,7 +938,6 @@ void main() { }, ), const FakeVmServiceRequest( - id: '6', method: 'ext.flutter.profileWidgetBuilds', args: { 'isolateId': null, @@ -985,7 +964,6 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachExpectations, const FakeVmServiceRequest( - id: '5', method: 'ext.flutter.platformOverride', args: { 'isolateId': null, @@ -995,7 +973,6 @@ void main() { }, ), const FakeVmServiceRequest( - id: '6', method: 'ext.flutter.platformOverride', args: { 'isolateId': null, @@ -1089,14 +1066,12 @@ void main() { fakeVmServiceHost = FakeVmServiceHost(requests: [ ...kAttachLogExpectations, const FakeVmServiceRequest( - id: '3', method: 'streamListen', args: { 'streamId': 'Isolate' } ), const FakeVmServiceRequest( - id: '4', method: 'registerService', args: { 'service': 'reloadSources', diff --git a/packages/flutter_tools/test/general.shard/terminal_handler_test.dart b/packages/flutter_tools/test/general.shard/terminal_handler_test.dart index 4c289b902a6..3ed50fda880 100644 --- a/packages/flutter_tools/test/general.shard/terminal_handler_test.dart +++ b/packages/flutter_tools/test/general.shard/terminal_handler_test.dart @@ -139,7 +139,9 @@ void main() { final MockFlutterDevice mockFlutterDevice = MockFlutterDevice(); when(mockResidentRunner.isRunningDebug).thenReturn(true); when(mockResidentRunner.flutterDevices).thenReturn([mockFlutterDevice]); - when(mockFlutterDevice.views).thenReturn([]); + when(mockResidentRunner.listFlutterViews()).thenAnswer((Invocation invocation) async { + return []; + }); await terminalHandler.processTerminalInput('l'); diff --git a/packages/flutter_tools/test/general.shard/vmservice_test.dart b/packages/flutter_tools/test/general.shard/vmservice_test.dart index 61007faa608..3ae406206f5 100644 --- a/packages/flutter_tools/test/general.shard/vmservice_test.dart +++ b/packages/flutter_tools/test/general.shard/vmservice_test.dart @@ -231,10 +231,10 @@ void main() { testWithoutContext('runInView forwards arguments correctly', () async { final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( requests: [ - const FakeVmServiceRequest(method: 'streamListen', id: '1', args: { + const FakeVmServiceRequest(method: 'streamListen', args: { 'streamId': 'Isolate' }), - const FakeVmServiceRequest(method: kRunInViewMethod, id: '2', args: { + const FakeVmServiceRequest(method: kRunInViewMethod, args: { 'viewId': '1234', 'mainScript': 'main.dart', 'assetDirectory': 'flutter_assets/', @@ -256,6 +256,65 @@ void main() { ); expect(fakeVmServiceHost.hasRemainingExpectations, false); }); + + testWithoutContext('getFlutterViews polls until a view is returned', () async { + final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( + requests: [ + const FakeVmServiceRequest( + method: kListViewsMethod, + jsonResponse: { + 'views': [], + }, + ), + const FakeVmServiceRequest( + method: kListViewsMethod, + jsonResponse: { + 'views': [], + }, + ), + const FakeVmServiceRequest( + method: kListViewsMethod, + jsonResponse: { + 'views': [ + { + 'id': 'a', + 'isolate': {}, + }, + ], + }, + ), + ] + ); + + expect( + await fakeVmServiceHost.vmService.getFlutterViews( + delay: Duration.zero, + ), + isNotEmpty, + ); + expect(fakeVmServiceHost.hasRemainingExpectations, false); + }); + + testWithoutContext('getFlutterViews does not poll if returnEarly is true', () async { + final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( + requests: [ + const FakeVmServiceRequest( + method: kListViewsMethod, + jsonResponse: { + 'views': [], + }, + ), + ] + ); + + expect( + await fakeVmServiceHost.vmService.getFlutterViews( + returnEarly: true, + ), + isEmpty, + ); + expect(fakeVmServiceHost.hasRemainingExpectations, false); + }); } class MockDevice extends Mock implements Device {} diff --git a/packages/flutter_tools/test/general.shard/web/devices_test.dart b/packages/flutter_tools/test/general.shard/web/devices_test.dart index c068fd47317..e5dd3307b39 100644 --- a/packages/flutter_tools/test/general.shard/web/devices_test.dart +++ b/packages/flutter_tools/test/general.shard/web/devices_test.dart @@ -76,7 +76,7 @@ void main() { expect(chromeDevice.supportsHotReload, true); expect(chromeDevice.supportsHotRestart, true); expect(chromeDevice.supportsStartPaused, true); - expect(chromeDevice.supportsFlutterExit, true); + expect(chromeDevice.supportsFlutterExit, false); expect(chromeDevice.supportsScreenshot, false); expect(await chromeDevice.isLocalEmulator, false); expect(chromeDevice.getLogReader(), isA()); @@ -96,7 +96,7 @@ void main() { expect(chromeDevice.supportsHotReload, true); expect(chromeDevice.supportsHotRestart, true); expect(chromeDevice.supportsStartPaused, true); - expect(chromeDevice.supportsFlutterExit, true); + expect(chromeDevice.supportsFlutterExit, false); expect(chromeDevice.supportsScreenshot, false); expect(await chromeDevice.isLocalEmulator, false); expect(chromeDevice.getLogReader(), isA()); @@ -114,7 +114,7 @@ void main() { expect(device.supportsHotReload, true); expect(device.supportsHotRestart, true); expect(device.supportsStartPaused, true); - expect(device.supportsFlutterExit, true); + expect(device.supportsFlutterExit, false); expect(device.supportsScreenshot, false); expect(await device.isLocalEmulator, false); expect(device.getLogReader(), isA()); diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart index c22c3a601c7..b29ea3ece45 100644 --- a/packages/flutter_tools/test/src/common.dart +++ b/packages/flutter_tools/test/src/common.dart @@ -239,7 +239,6 @@ class FakeVmServiceHost { final FakeVmServiceRequest fakeRequest = _requests.removeAt(0) as FakeVmServiceRequest; expect(request, isA>() .having((Map request) => request['method'], 'method', fakeRequest.method) - .having((Map request) => request['id'], 'id', fakeRequest.id) .having((Map request) => request['params'], 'args', fakeRequest.args) ); if (fakeRequest.close) { @@ -250,13 +249,13 @@ class FakeVmServiceHost { if (fakeRequest.errorCode == null) { _input.add(json.encode({ 'jsonrpc': '2.0', - 'id': fakeRequest.id, + 'id': request['id'], 'result': fakeRequest.jsonResponse ?? {'type': 'Success'}, })); } else { _input.add(json.encode({ 'jsonrpc': '2.0', - 'id': fakeRequest.id, + 'id': request['id'], 'error': { 'code': fakeRequest.errorCode, } @@ -299,7 +298,6 @@ abstract class VmServiceExpectation { class FakeVmServiceRequest implements VmServiceExpectation { const FakeVmServiceRequest({ @required this.method, - @required this.id, this.args = const {}, this.jsonResponse, this.errorCode, @@ -307,7 +305,6 @@ class FakeVmServiceRequest implements VmServiceExpectation { }); final String method; - final String id; /// When true, the vm service is automatically closed. final bool close;