diff --git a/packages/flutter/lib/src/foundation/binding.dart b/packages/flutter/lib/src/foundation/binding.dart index 7701da326c..f6287b2465 100644 --- a/packages/flutter/lib/src/foundation/binding.dart +++ b/packages/flutter/lib/src/foundation/binding.dart @@ -246,6 +246,14 @@ abstract class BindingBase { }, ); + registerStringServiceExtension( + name: 'connectedVmServiceUri', + getter: () async => connectedVmServiceUri ?? '', + setter: (String uri) async { + connectedVmServiceUri = uri; + }, + ); + registerStringServiceExtension( name: 'activeDevToolsServerAddress', getter: () async => activeDevToolsServerAddress ?? '', diff --git a/packages/flutter/lib/src/foundation/debug.dart b/packages/flutter/lib/src/foundation/debug.dart index 31d70d1f5f..5fc406ac83 100644 --- a/packages/flutter/lib/src/foundation/debug.dart +++ b/packages/flutter/lib/src/foundation/debug.dart @@ -113,3 +113,6 @@ ui.Brightness? debugBrightnessOverride; /// The address for the active DevTools server used for debugging this /// application. String? activeDevToolsServerAddress; + +/// The uri for the connected vm service protocol. +String? connectedVmServiceUri; diff --git a/packages/flutter/lib/src/widgets/widget_inspector.dart b/packages/flutter/lib/src/widgets/widget_inspector.dart index ce49de7199..2cd4c09489 100644 --- a/packages/flutter/lib/src/widgets/widget_inspector.dart +++ b/packages/flutter/lib/src/widgets/widget_inspector.dart @@ -713,12 +713,6 @@ mixin WidgetInspectorService { _instance = instance; } - /// Information about the VM service protocol for the running application. - /// - /// This information is necessary to provide Flutter DevTools deep links in - /// error messages. - developer.ServiceProtocolInfo? _serviceInfo; - static bool _debugServiceExtensionsRegistered = false; /// Ground truth tracking what object(s) are currently selected used by both @@ -982,12 +976,6 @@ mixin WidgetInspectorService { /// * [BindingBase.initServiceExtensions], which explains when service /// extensions can be used. void initServiceExtensions(_RegisterServiceExtensionCallback registerServiceExtensionCallback) { - if (!kIsWeb) { - developer.Service.getInfo().then((developer.ServiceProtocolInfo info) { - _serviceInfo = info; - }); - } - _structuredExceptionHandler = _reportError; if (isStructuredErrorsEnabled()) { FlutterError.onError = _structuredExceptionHandler; @@ -1399,13 +1387,10 @@ mixin WidgetInspectorService { /// Returns a DevTools uri linking to a specific element on the inspector page. String? _devToolsInspectorUriForElement(Element element) { - if (activeDevToolsServerAddress != null && _serviceInfo != null) { - final Uri? vmServiceUri = _serviceInfo!.serverUri; - if (vmServiceUri != null) { - final String? inspectorRef = toId(element, _consoleObjectGroup); - if (inspectorRef != null) { - return devToolsInspectorUri(vmServiceUri, inspectorRef); - } + if (activeDevToolsServerAddress != null && connectedVmServiceUri != null) { + final String? inspectorRef = toId(element, _consoleObjectGroup); + if (inspectorRef != null) { + return devToolsInspectorUri(inspectorRef); } } return null; @@ -1414,10 +1399,13 @@ mixin WidgetInspectorService { /// Returns the DevTools inspector uri for the given vm service connection and /// inspector reference. @visibleForTesting - String devToolsInspectorUri(Uri vmServiceUri, String inspectorRef) { + String devToolsInspectorUri(String inspectorRef) { + assert(activeDevToolsServerAddress != null); + assert(connectedVmServiceUri != null); + final Uri uri = Uri.parse(activeDevToolsServerAddress!).replace( queryParameters: { - 'uri': '$vmServiceUri', + 'uri': connectedVmServiceUri!, 'inspectorRef': inspectorRef, }, ); @@ -2959,7 +2947,6 @@ Iterable _describeRelevantUserCode(Element element) { return nodes; } - /// Debugging message for DevTools deep links. /// /// The [value] for this property is a string representation of the Flutter diff --git a/packages/flutter/test/foundation/service_extensions_test.dart b/packages/flutter/test/foundation/service_extensions_test.dart index d74a90a842..40ad215331 100644 --- a/packages/flutter/test/foundation/service_extensions_test.dart +++ b/packages/flutter/test/foundation/service_extensions_test.dart @@ -167,7 +167,7 @@ void main() { const int disabledExtensions = kIsWeb ? 2 : 0; // If you add a service extension... TEST IT! :-) // ...then increment this number. - expect(binding.extensions.length, 30 + widgetInspectorExtensionCount - disabledExtensions); + expect(binding.extensions.length, 31 + widgetInspectorExtensionCount - disabledExtensions); expect(console, isEmpty); debugPrint = debugPrintThrottled; @@ -771,4 +771,17 @@ void main() { serverAddress = result['value'] as String; expect(serverAddress, 'http://127.0.0.1:9102'); }); + + test('Service extensions - connectedVmServiceUri', () async { + Map result; + result = await binding.testExtension('connectedVmServiceUri', {}); + String serverAddress = result['value'] as String; + expect(serverAddress, ''); + result = await binding.testExtension('connectedVmServiceUri', {'value': 'http://127.0.0.1:54669/kMUMseKAnog=/'}); + serverAddress = result['value'] as String; + expect(serverAddress, 'http://127.0.0.1:54669/kMUMseKAnog=/'); + result = await binding.testExtension('connectedVmServiceUri', {'value': 'http://127.0.0.1:54000/kMUMseKAnog=/'}); + serverAddress = result['value'] as String; + expect(serverAddress, 'http://127.0.0.1:54000/kMUMseKAnog=/'); + }); } diff --git a/packages/flutter/test/widgets/widget_inspector_test.dart b/packages/flutter/test/widgets/widget_inspector_test.dart index f9cf0e676c..50de011444 100644 --- a/packages/flutter/test/widgets/widget_inspector_test.dart +++ b/packages/flutter/test/widgets/widget_inspector_test.dart @@ -2827,11 +2827,9 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { test('devToolsInspectorUri test', () { activeDevToolsServerAddress = 'http://127.0.0.1:9100'; + connectedVmServiceUri = 'http://127.0.0.1:55269/798ay5al_FM=/'; expect( - WidgetInspectorService.instance.devToolsInspectorUri( - Uri.parse('http://127.0.0.1:55269/798ay5al_FM=/'), - 'inspector-0', - ), + WidgetInspectorService.instance.devToolsInspectorUri('inspector-0'), equals('http://127.0.0.1:9100/#/inspector?uri=http%3A%2F%2F127.0.0.1%3A55269%2F798ay5al_FM%3D%2F&inspectorRef=inspector-0'), ); }); @@ -2840,11 +2838,11 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { final DevToolsDeepLinkProperty node = DevToolsDeepLinkProperty( 'description of the deep link', - 'the-deep-link', + 'http://the-deeplink/', ); expect(node.toString(), equals('description of the deep link')); expect(node.name, isEmpty); - expect(node.value, equals('the-deep-link')); + expect(node.value, equals('http://the-deeplink/')); expect( node.toJsonMap(const DiagnosticsSerializationDelegate()), equals({ @@ -2856,7 +2854,7 @@ class _TestWidgetInspectorService extends TestWidgetInspectorService { 'missingIfNull': false, 'propertyType': 'String', 'defaultLevel': 'info', - 'value': 'the-deep-link', + 'value': 'http://the-deeplink/', }), ); }); diff --git a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart index f4547c48e5..2e1d911c3d 100644 --- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart @@ -131,6 +131,7 @@ abstract class ResidentWebRunner extends ResidentRunner { @override Future> invokeFlutterExtensionRpcRawOnFirstIsolate( String method, { + FlutterDevice device, Map params, }) async { final vmservice.Response response = diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart index a7160a24f1..6dc1f62b5a 100644 --- a/packages/flutter_tools/lib/src/resident_runner.dart +++ b/packages/flutter_tools/lib/src/resident_runner.dart @@ -846,13 +846,12 @@ abstract class ResidentRunner { // runner to support a single flutter device. Future> invokeFlutterExtensionRpcRawOnFirstIsolate( String method, { + FlutterDevice device, Map params, }) async { - final List views = await flutterDevices - .first - .vmService.getFlutterViews(); - return flutterDevices - .first + device ??= flutterDevices.first; + final List views = await device.vmService.getFlutterViews(); + return device .vmService .invokeFlutterExtensionRpcRaw( method, @@ -1286,21 +1285,60 @@ abstract class ResidentRunner { Future maybeCallDevToolsUriServiceExtension() async { _devToolsLauncher ??= DevtoolsLauncher.instance; - if (_devToolsLauncher.activeDevToolsServer != null) { - - await Future.wait(>[ + if (_devToolsLauncher?.activeDevToolsServer != null) { + await Future.wait(>[ for (final FlutterDevice device in flutterDevices) - waitForExtension(device.vmService, 'ext.flutter.activeDevToolsServerAddress'), + _callDevToolsUriExtension(device), ]); + } + } + + Future _callDevToolsUriExtension(FlutterDevice device) async { + if (_devToolsLauncher == null) { + return; + } + await waitForExtension(device.vmService, 'ext.flutter.activeDevToolsServerAddress'); + try { + unawaited(invokeFlutterExtensionRpcRawOnFirstIsolate( + 'ext.flutter.activeDevToolsServerAddress', + device: device, + params: { + 'value': _devToolsLauncher.activeDevToolsServer.uri.toString(), + }, + )); + } on Exception catch (e) { + globals.printError( + 'Failed to set DevTools server address: ${e.toString()}. Deep links to' + ' DevTools will not show in Flutter errors.', + ); + } + } + + Future callConnectedVmServiceUriExtension() async { + await Future.wait(>[ + for (final FlutterDevice device in flutterDevices) + _callConnectedVmServiceExtension(device), + ]); + } + + Future _callConnectedVmServiceExtension(FlutterDevice device) async { + if (device.vmService.httpAddress != null || device.vmService.wsAddress != null) { + final Uri uri = device.vmService.httpAddress ?? device.vmService.wsAddress; + await waitForExtension(device.vmService, 'ext.flutter.connectedVmServiceUri'); try { unawaited(invokeFlutterExtensionRpcRawOnFirstIsolate( - 'ext.flutter.activeDevToolsServerAddress', + 'ext.flutter.connectedVmServiceUri', + device: device, params: { - 'value': _devToolsLauncher.activeDevToolsServer.uri.toString(), + 'value': uri.toString(), }, )); } on Exception catch (e) { globals.printError(e.toString()); + globals.printError( + 'Failed to set vm service URI: ${e.toString()}. Deep links to DevTools' + ' will not show in Flutter errors.', + ); } } } diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart index abb2370065..05ea69ad3e 100644 --- a/packages/flutter_tools/lib/src/run_cold.dart +++ b/packages/flutter_tools/lib/src/run_cold.dart @@ -123,6 +123,7 @@ class ColdRunner extends ResidentRunner { if (debuggingEnabled) { unawaited(maybeCallDevToolsUriServiceExtension()); + unawaited(callConnectedVmServiceUriExtension()); } appStartedCompleter?.complete(); @@ -168,6 +169,7 @@ class ColdRunner extends ResidentRunner { } unawaited(maybeCallDevToolsUriServiceExtension()); + unawaited(callConnectedVmServiceUriExtension()); appStartedCompleter?.complete(); if (stayResident) { diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart index a103ef8a2a..5bc1af09b6 100644 --- a/packages/flutter_tools/lib/src/run_hot.dart +++ b/packages/flutter_tools/lib/src/run_hot.dart @@ -217,6 +217,7 @@ class HotRunner extends ResidentRunner { } unawaited(maybeCallDevToolsUriServiceExtension()); + unawaited(callConnectedVmServiceUriExtension()); final Stopwatch initialUpdateDevFSsTimer = Stopwatch()..start(); final UpdateFSReport devfsResult = await _updateDevFS(fullRestart: true); @@ -635,6 +636,7 @@ class HotRunner extends ResidentRunner { globals.printStatus('Restarted application in ${getElapsedAsMilliseconds(timer.elapsed)}.'); } unawaited(maybeCallDevToolsUriServiceExtension()); + unawaited(callConnectedVmServiceUriExtension()); return result; } final OperationResult result = await _hotReloadHelper( 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 7289073004..2bc623e466 100644 --- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart +++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart @@ -44,6 +44,7 @@ final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate( ), breakpoints: [], exceptionPauseMode: null, + extensionRPCs: [], libraries: [ vm_service.LibraryRef( id: '1', @@ -128,6 +129,11 @@ const FakeVmServiceRequest setAssetBundlePath = FakeVmServiceRequest( } ); +const FakeVmServiceRequest listenToExtensionStream = FakeVmServiceRequest( + method: 'streamListen', + args: {'streamId': 'Extension'}, +); + void main() { final Uri testUri = Uri.parse('foo://bar'); Testbed testbed; @@ -2384,7 +2390,19 @@ void main() { testUsingContext('HotRunner writes vm service file when providing debugging option', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, + listenToExtensionStream, + FakeVmServiceRequest( + method: 'getVM', + jsonResponse: fakeVM.toJson(), + ), listViews, + FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': '1', + }, + jsonResponse: fakeUnpausedIsolate.toJson(), + ), setAssetBundlePath, ]); setWsAddress(testUri, fakeVmServiceHost.vmService); @@ -2413,7 +2431,19 @@ void main() { testUsingContext('HotRunner copies compiled app.dill to cache during startup', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, + listenToExtensionStream, + FakeVmServiceRequest( + method: 'getVM', + jsonResponse: fakeVM.toJson(), + ), listViews, + FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': '1', + }, + jsonResponse: fakeUnpausedIsolate.toJson(), + ), setAssetBundlePath, ]); setWsAddress(testUri, fakeVmServiceHost.vmService); @@ -2443,7 +2473,19 @@ void main() { testUsingContext('HotRunner copies compiled app.dill to cache during startup with dart defines', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, + listenToExtensionStream, + FakeVmServiceRequest( + method: 'getVM', + jsonResponse: fakeVM.toJson(), + ), listViews, + FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': '1', + }, + jsonResponse: fakeUnpausedIsolate.toJson(), + ), setAssetBundlePath, ]); setWsAddress(testUri, fakeVmServiceHost.vmService); @@ -2481,7 +2523,19 @@ void main() { testUsingContext('HotRunner copies compiled app.dill to cache during startup with null safety', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, + listenToExtensionStream, + FakeVmServiceRequest( + method: 'getVM', + jsonResponse: fakeVM.toJson(), + ), listViews, + FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': '1', + }, + jsonResponse: fakeUnpausedIsolate.toJson(), + ), setAssetBundlePath, ]); setWsAddress(testUri, fakeVmServiceHost.vmService); @@ -2519,7 +2573,19 @@ void main() { testUsingContext('HotRunner does not copy app.dill if a dillOutputPath is given', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, + listenToExtensionStream, + FakeVmServiceRequest( + method: 'getVM', + jsonResponse: fakeVM.toJson(), + ), listViews, + FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': '1', + }, + jsonResponse: fakeUnpausedIsolate.toJson(), + ), setAssetBundlePath, ]); setWsAddress(testUri, fakeVmServiceHost.vmService); @@ -2550,7 +2616,19 @@ void main() { testUsingContext('HotRunner copies compiled app.dill to cache during startup with --track-widget-creation', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, + listenToExtensionStream, + FakeVmServiceRequest( + method: 'getVM', + jsonResponse: fakeVM.toJson(), + ), listViews, + FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': '1', + }, + jsonResponse: fakeUnpausedIsolate.toJson(), + ), setAssetBundlePath, ]); setWsAddress(testUri, fakeVmServiceHost.vmService); @@ -2652,6 +2730,18 @@ void main() { testUsingContext('ColdRunner writes vm service file when providing debugging option', () => testbed.run(() async { fakeVmServiceHost = FakeVmServiceHost(requests: [ listViews, + listenToExtensionStream, + FakeVmServiceRequest( + method: 'getVM', + jsonResponse: fakeVM.toJson(), + ), + FakeVmServiceRequest( + method: 'getIsolate', + args: { + 'isolateId': '1', + }, + jsonResponse: fakeUnpausedIsolate.toJson(), + ), ]); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); setWsAddress(testUri, fakeVmServiceHost.vmService); @@ -2670,6 +2760,9 @@ void main() { return 0; }); await residentRunner.run(); + // Await a short delay so that we don't try to exit before all the expected + // VM service requests have been fired. + await Future.delayed(const Duration(milliseconds: 200)); expect(await globals.fs.file('foo').readAsString(), testUri.toString()); expect(fakeVmServiceHost.hasRemainingExpectations, false); 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 d57a73f498..31d849d6a7 100644 --- a/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart +++ b/packages/flutter_tools/test/integration.shard/flutter_attach_test.dart @@ -5,6 +5,7 @@ import 'dart:io'; import 'package:file/file.dart'; +import 'package:vm_service/vm_service.dart'; import '../src/common.dart'; import 'test_data/basic_project.dart'; @@ -77,25 +78,41 @@ void main() { await _flutterRun.run( startPaused: true, withDebugger: true, + additionalCommandArgs: ['--devtools-server-address', 'http://127.0.0.1:9105'], ); await _flutterRun.resume(); - await pollForServiceExtensionValue( + await pollForServiceExtensionValue( testDriver: _flutterRun, extension: 'ext.flutter.activeDevToolsServerAddress', continuePollingValue: '', - expectedValue: 'http://127.0.0.1:9100', + matches: equals('http://127.0.0.1:9105'), ); + await pollForServiceExtensionValue( + testDriver: _flutterRun, + extension: 'ext.flutter.connectedVmServiceUri', + continuePollingValue: '', + matches: isNotEmpty, + ); + + final Response response = await _flutterRun.callServiceExtension('ext.flutter.connectedVmServiceUri'); + final String vmServiceUri = response.json['value'] as String; // Attach with a different DevTools server address. await _flutterAttach.attach( _flutterRun.vmServicePort, additionalCommandArgs: ['--devtools-server-address', 'http://127.0.0.1:9110'], ); - await pollForServiceExtensionValue( + await pollForServiceExtensionValue( testDriver: _flutterAttach, extension: 'ext.flutter.activeDevToolsServerAddress', continuePollingValue: '', - expectedValue: 'http://127.0.0.1:9110', + matches: equals('http://127.0.0.1:9110'), ); - }); + await pollForServiceExtensionValue( + testDriver: _flutterRun, + extension: 'ext.flutter.connectedVmServiceUri', + continuePollingValue: '', + matches: equals(vmServiceUri), + ); + }, timeout: const Timeout.factor(4)); } diff --git a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart index 4049962405..4baa3edb78 100644 --- a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart +++ b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart @@ -7,6 +7,7 @@ import 'package:flutter_tools/src/base/io.dart'; import 'package:process/process.dart'; import '../src/common.dart'; +import '../src/context.dart'; import 'test_data/basic_project.dart'; import 'test_driver.dart'; import 'test_utils.dart'; @@ -61,11 +62,17 @@ void main() { additionalCommandArgs: ['--devtools-server-address', 'http://127.0.0.1:9110'], ); await _flutter.resume(); - await pollForServiceExtensionValue( + await pollForServiceExtensionValue( testDriver: _flutter, extension: 'ext.flutter.activeDevToolsServerAddress', continuePollingValue: '', - expectedValue: 'http://127.0.0.1:9110', + matches: equals('http://127.0.0.1:9110'), ); - }); + await pollForServiceExtensionValue( + testDriver: _flutter, + extension: 'ext.flutter.connectedVmServiceUri', + continuePollingValue: '', + matches: isNotEmpty, + ); + }, timeout: const Timeout.factor(4)); } diff --git a/packages/flutter_tools/test/integration.shard/test_driver.dart b/packages/flutter_tools/test/integration.shard/test_driver.dart index ceae0b4583..e61d4db636 100644 --- a/packages/flutter_tools/test/integration.shard/test_driver.dart +++ b/packages/flutter_tools/test/integration.shard/test_driver.dart @@ -53,6 +53,7 @@ abstract class FlutterTestDriver { final StringBuffer _errorBuffer = StringBuffer(); String _lastResponse; Uri _vmServiceWsUri; + int _attachPort; bool _hasExited = false; VmService _vmService; @@ -173,8 +174,9 @@ abstract class FlutterTestDriver { String extension, { Map args = const {}, }) async { - final VmService vmService = await vmServiceConnectUri('ws://localhost:$vmServicePort/ws'); - final Isolate isolate = await waitForExtension(vmService, 'ext.flutter.activeDevToolsServerAddress'); + final int port = _vmServiceWsUri != null ? vmServicePort : _attachPort; + final VmService vmService = await vmServiceConnectUri('ws://localhost:$port/ws'); + final Isolate isolate = await waitForExtension(vmService, extension); return await vmService.callServiceExtension( extension, isolateId: isolate.id, @@ -508,6 +510,7 @@ class FlutterRunTestDriver extends FlutterTestDriver { bool singleWidgetReloads = false, List additionalCommandArgs, }) async { + _attachPort = port; await _setupProcess( [ 'attach', @@ -526,6 +529,7 @@ class FlutterRunTestDriver extends FlutterTestDriver { pauseOnExceptions: pauseOnExceptions, pidFile: pidFile, singleWidgetReloads: singleWidgetReloads, + attachPort: port, ); } @@ -538,6 +542,7 @@ class FlutterRunTestDriver extends FlutterTestDriver { bool pauseOnExceptions = false, bool singleWidgetReloads = false, File pidFile, + int attachPort, }) async { assert(!startPaused || withDebugger); await super._setupProcess( @@ -582,6 +587,13 @@ class FlutterRunTestDriver extends FlutterTestDriver { } } + // In order to call service extensions from test runners started with + // attach, we need to store the port that the test runner was attached + // to. + if (_vmServiceWsUri == null && attachPort != null) { + _attachPort = attachPort; + } + // Now await the started event; if it had already happened the future will // have already completed. _currentRunningAppId = (await started)['params']['appId'] as String; diff --git a/packages/flutter_tools/test/integration.shard/test_utils.dart b/packages/flutter_tools/test/integration.shard/test_utils.dart index be8e9d75dc..3a3db0469e 100644 --- a/packages/flutter_tools/test/integration.shard/test_utils.dart +++ b/packages/flutter_tools/test/integration.shard/test_utils.dart @@ -81,16 +81,20 @@ Future pollForServiceExtensionValue({ @required FlutterTestDriver testDriver, @required String extension, @required T continuePollingValue, - @required T expectedValue, + @required Matcher matches, String valueKey = 'value', }) async { - for (int i = 10; i < 10; i++) { + for (int i = 0; i < 10; i++) { final Response response = await testDriver.callServiceExtension(extension); - if (response.json[valueKey] == continuePollingValue) { + if (response.json[valueKey] as T == continuePollingValue) { await Future.delayed(const Duration(seconds: 1)); } else { - expect(response.json[valueKey], equals(expectedValue)); - break; + expect(response.json[valueKey] as T, matches); + return; } } + fail( + 'Did not find expected value for service extension \'$extension\'. All call' + ' attempts responded with \'$continuePollingValue\'.', + ); }