Unverified Commit c89d6498 authored by Kenzie Schmoll's avatar Kenzie Schmoll Committed by GitHub

Add connectedVmServiceUri service extension and set from flutter_tools (#74534)

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