Unverified Commit 9fdda012 authored by Jia Hao's avatar Jia Hao Committed by GitHub

[flutter_tools] Generalize waitForExtension (#77220)

parent 385edc33
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import 'base/logger.dart'; import 'base/logger.dart';
import 'resident_runner.dart'; import 'resident_runner.dart';
...@@ -66,12 +65,12 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler { ...@@ -66,12 +65,12 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
// report their URLs yet. Do so now. // report their URLs yet. Do so now.
_residentRunner.printDebuggerList(includeObservatory: false); _residentRunner.printDebuggerList(includeObservatory: false);
} }
await _waitForExtensions(flutterDevices); final List<FlutterDevice> devicesWithExtension = await _devicesWithExtensions(flutterDevices);
await _maybeCallDevToolsUriServiceExtension( await _maybeCallDevToolsUriServiceExtension(
flutterDevices, devicesWithExtension,
); );
await _callConnectedVmServiceUriExtension( await _callConnectedVmServiceUriExtension(
flutterDevices, devicesWithExtension,
); );
} }
...@@ -107,12 +106,28 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler { ...@@ -107,12 +106,28 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
} }
} }
Future<void> _waitForExtensions(List<FlutterDevice> flutterDevices) async { Future<List<FlutterDevice>> _devicesWithExtensions(List<FlutterDevice> flutterDevices) async {
await Future.wait(<Future<void>>[ final List<FlutterDevice> devices = await Future.wait(<Future<FlutterDevice>>[
for (final FlutterDevice device in flutterDevices) for (final FlutterDevice device in flutterDevices)
if (device.vmService != null) _waitForExtensionsForDevice(device)
waitForExtension(device.vmService.service, 'ext.flutter.connectedVmServiceUri'),
]); ]);
return devices.where((FlutterDevice device) => device != null).toList();
}
/// Returns null if the service extension cannot be found on the device.
Future<FlutterDevice> _waitForExtensionsForDevice(FlutterDevice flutterDevice) async {
const String extension = 'ext.flutter.connectedVmServiceUri';
try {
await flutterDevice.vmService?.findExtensionIsolate(extension);
return flutterDevice;
} on VmServiceDisappearedException {
_logger.printTrace(
'The VM Service for ${flutterDevice.device} disappeared while trying to'
' find the $extension service extension. Skipping subsequent DevTools '
'setup for this device.',
);
return null;
}
} }
Future<void> _callConnectedVmServiceUriExtension(List<FlutterDevice> flutterDevices) async { Future<void> _callConnectedVmServiceUriExtension(List<FlutterDevice> flutterDevices) async {
...@@ -164,10 +179,10 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler { ...@@ -164,10 +179,10 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
@override @override
Future<void> hotRestart(List<FlutterDevice> flutterDevices) async { Future<void> hotRestart(List<FlutterDevice> flutterDevices) async {
await _waitForExtensions(flutterDevices); final List<FlutterDevice> devicesWithExtension = await _devicesWithExtensions(flutterDevices);
await Future.wait(<Future<void>>[ await Future.wait(<Future<void>>[
_maybeCallDevToolsUriServiceExtension(flutterDevices), _maybeCallDevToolsUriServiceExtension(devicesWithExtension),
_callConnectedVmServiceUriExtension(flutterDevices), _callConnectedVmServiceUriExtension(devicesWithExtension),
]); ]);
} }
...@@ -181,37 +196,6 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler { ...@@ -181,37 +196,6 @@ class FlutterResidentDevtoolsHandler implements ResidentDevtoolsHandler {
} }
} }
@visibleForTesting
Future<void> waitForExtension(vm_service.VmService vmService, String extension) async {
final Completer<void> completer = Completer<void>();
try {
await vmService.streamListen(vm_service.EventStreams.kExtension);
} on Exception {
// do nothing
}
StreamSubscription<vm_service.Event> extensionStream;
extensionStream = vmService.onExtensionEvent.listen((vm_service.Event event) {
if (event.json['extensionKind'] == 'Flutter.FrameworkInitialization') {
// The 'Flutter.FrameworkInitialization' event is sent on hot restart
// as well, so make sure we don't try to complete this twice.
if (!completer.isCompleted) {
completer.complete();
extensionStream.cancel();
}
}
});
final vm_service.VM vm = await vmService.getVM();
if (vm.isolates.isNotEmpty) {
final vm_service.IsolateRef isolateRef = vm.isolates.first;
final vm_service.Isolate isolate = await vmService.getIsolate(isolateRef.id);
if (isolate.extensionRPCs.contains(extension)) {
return;
}
}
await completer.future;
}
@visibleForTesting @visibleForTesting
NoOpDevtoolsHandler createNoOpHandler(DevtoolsLauncher launcher, ResidentRunner runner, Logger logger) { NoOpDevtoolsHandler createNoOpHandler(DevtoolsLauncher launcher, ResidentRunner runner, Logger logger) {
return NoOpDevtoolsHandler(); return NoOpDevtoolsHandler();
......
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
// @dart = 2.8 // @dart = 2.8
import 'dart:async';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:meta/meta.dart' show required; import 'package:meta/meta.dart' show required;
import 'package:vm_service/vm_service.dart' as vm_service; import 'package:vm_service/vm_service.dart' as vm_service;
...@@ -479,7 +481,7 @@ class FlutterVmService { ...@@ -479,7 +481,7 @@ class FlutterVmService {
@required Uri assetsDirectory, @required Uri assetsDirectory,
}) async { }) async {
try { try {
await service.streamListen('Isolate'); await service.streamListen(vm_service.EventStreams.kIsolate);
} on vm_service.RPCError { } on vm_service.RPCError {
// Do nothing, since the tool is already subscribed. // Do nothing, since the tool is already subscribed.
} }
...@@ -784,6 +786,58 @@ class FlutterVmService { ...@@ -784,6 +786,58 @@ class FlutterVmService {
} }
} }
/// Waits for a signal from the VM service that [extensionName] is registered.
///
/// Looks at the list of loaded extensions for first Flutter view, as well as
/// the stream of added extensions to avoid races.
///
/// Throws a [VmServiceDisappearedException] should the VM Service disappear
/// while making calls to it.
Future<vm_service.IsolateRef> findExtensionIsolate(String extensionName) async {
try {
await service.streamListen(vm_service.EventStreams.kIsolate);
} on vm_service.RPCError {
// Do nothing, since the tool is already subscribed.
}
final Completer<vm_service.IsolateRef> extensionAdded = Completer<vm_service.IsolateRef>();
StreamSubscription<vm_service.Event> isolateEvents;
isolateEvents = service.onIsolateEvent.listen((vm_service.Event event) {
if (event.kind == vm_service.EventKind.kServiceExtensionAdded
&& event.extensionRPC == extensionName) {
isolateEvents.cancel();
extensionAdded.complete(event.isolate);
}
});
try {
final List<FlutterView> flutterViews = await getFlutterViews();
if (flutterViews.isEmpty) {
throw VmServiceDisappearedException();
}
for (final FlutterView flutterView in flutterViews) {
final vm_service.IsolateRef isolateRef = flutterView.uiIsolate;
if (isolateRef == null) {
continue;
}
final vm_service.Isolate isolate = await service.getIsolate(isolateRef.id);
if (isolate.extensionRPCs.contains(extensionName)) {
return isolateRef;
}
}
return await extensionAdded.future;
} finally {
await isolateEvents.cancel();
try {
await service.streamCancel(vm_service.EventStreams.kIsolate);
} on vm_service.RPCError {
// It's ok for cleanup to fail, such as when the service disappears.
}
}
}
/// Attempt to retrieve the isolate with id [isolateId], or `null` if it has /// Attempt to retrieve the isolate with id [isolateId], or `null` if it has
/// been collected. /// been collected.
Future<vm_service.Isolate> getIsolateOrNull(String isolateId) { Future<vm_service.Isolate> getIsolateOrNull(String isolateId) {
...@@ -845,6 +899,9 @@ class FlutterVmService { ...@@ -845,6 +899,9 @@ class FlutterVmService {
} }
} }
/// Thrown when the VM Service disappears while calls are being made to it.
class VmServiceDisappearedException implements Exception {}
/// Whether the event attached to an [Isolate.pauseEvent] should be considered /// Whether the event attached to an [Isolate.pauseEvent] should be considered
/// a "pause" event. /// a "pause" event.
bool isPauseEvent(String kind) { bool isPauseEvent(String kind) {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
// @dart = 2.8 // @dart = 2.8
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/devtools_launcher.dart'; import 'package:flutter_tools/src/devtools_launcher.dart';
import 'package:flutter_tools/src/vmservice.dart'; import 'package:flutter_tools/src/vmservice.dart';
import 'package:vm_service/vm_service.dart' as vm_service; import 'package:vm_service/vm_service.dart' as vm_service;
...@@ -17,7 +18,7 @@ import 'package:test/fake.dart'; ...@@ -17,7 +18,7 @@ import 'package:test/fake.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
final vm_service.Isolate isolate = vm_service.Isolate( final vm_service.Isolate isolate = vm_service.Isolate(
id: '1', id: '1',
pauseEvent: vm_service.Event( pauseEvent: vm_service.Event(
kind: vm_service.EventKind.kResume, kind: vm_service.EventKind.kResume,
...@@ -40,60 +41,17 @@ import '../src/context.dart'; ...@@ -40,60 +41,17 @@ import '../src/context.dart';
startTime: 0, startTime: 0,
isSystemIsolate: false, isSystemIsolate: false,
isolateFlags: <vm_service.IsolateFlag>[], isolateFlags: <vm_service.IsolateFlag>[],
extensionRPCs: <String>['foo'] extensionRPCs: <String>['ext.flutter.connectedVmServiceUri'],
);
final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate(
id: '1',
pauseEvent: vm_service.Event(
kind: vm_service.EventKind.kResume,
timestamp: 0
),
breakpoints: <vm_service.Breakpoint>[],
exceptionPauseMode: null,
extensionRPCs: <String>[],
libraries: <vm_service.LibraryRef>[
vm_service.LibraryRef(
id: '1',
uri: 'file:///hello_world/main.dart',
name: '',
),
],
livePorts: 0,
name: 'test',
number: '1',
pauseOnExit: false,
runnable: true,
startTime: 0,
isSystemIsolate: false,
isolateFlags: <vm_service.IsolateFlag>[],
);
final vm_service.VM fakeVM = vm_service.VM(
isolates: <vm_service.IsolateRef>[fakeUnpausedIsolate],
pid: 1,
hostCPU: '',
isolateGroups: <vm_service.IsolateGroupRef>[],
targetCPU: '',
startTime: 0,
name: 'dart',
architectureBits: 64,
operatingSystem: '',
version: '',
systemIsolateGroups: <vm_service.IsolateGroupRef>[],
systemIsolates: <vm_service.IsolateRef>[],
);
final FlutterView fakeFlutterView = FlutterView(
id: 'a',
uiIsolate: fakeUnpausedIsolate,
); );
final FakeVmServiceRequest listViews = FakeVmServiceRequest( final FakeVmServiceRequest listViews = FakeVmServiceRequest(
method: kListViewsMethod, method: kListViewsMethod,
jsonResponse: <String, Object>{ jsonResponse: <String, Object>{
'views': <Object>[ 'views': <Object>[
fakeFlutterView.toJson(), FlutterView(
id: 'a',
uiIsolate: isolate,
).toJson()
], ],
}, },
); );
...@@ -173,10 +131,10 @@ void main() { ...@@ -173,10 +131,10 @@ void main() {
const FakeVmServiceRequest( const FakeVmServiceRequest(
method: 'streamListen', method: 'streamListen',
args: <String, Object>{ args: <String, Object>{
'streamId': 'Extension', 'streamId': 'Isolate',
} }
), ),
FakeVmServiceRequest(method: 'getVM', jsonResponse: fakeVM.toJson()), listViews,
FakeVmServiceRequest( FakeVmServiceRequest(
method: 'getIsolate', method: 'getIsolate',
jsonResponse: isolate.toJson(), jsonResponse: isolate.toJson(),
...@@ -184,13 +142,11 @@ void main() { ...@@ -184,13 +142,11 @@ void main() {
'isolateId': '1', 'isolateId': '1',
}, },
), ),
FakeVmServiceStreamResponse( const FakeVmServiceRequest(
streamId: 'Extension', method: 'streamCancel',
event: vm_service.Event( args: <String, Object>{
timestamp: 0, 'streamId': 'Isolate',
extensionKind: 'Flutter.FrameworkInitialization', },
kind: 'test',
),
), ),
listViews, listViews,
const FakeVmServiceRequest( const FakeVmServiceRequest(
...@@ -218,15 +174,55 @@ void main() { ...@@ -218,15 +174,55 @@ void main() {
); );
}); });
testWithoutContext('wait for extension handles an immediate extension', () { testWithoutContext('serveAndAnnounceDevTools with skips calling service extensions when VM service disappears', () async {
final ResidentDevtoolsHandler handler = FlutterResidentDevtoolsHandler(
FakeDevtoolsLauncher()..activeDevToolsServer = DevToolsServerAddress('localhost', 8080),
FakeResidentRunner(),
BufferLogger.test(),
);
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[ final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
const FakeVmServiceRequest( const FakeVmServiceRequest(
method: 'streamListen', method: 'streamListen',
args: <String, Object>{ args: <String, Object>{
'streamId': 'Extension', 'streamId': 'Isolate',
} },
), ),
FakeVmServiceRequest(method: 'getVM', jsonResponse: fakeVM.toJson()), const FakeVmServiceRequest(
method: kListViewsMethod,
errorCode: RPCErrorCodes.kServiceDisappeared,
),
const FakeVmServiceRequest(
method: 'streamCancel',
args: <String, Object>{
'streamId': 'Isolate',
},
errorCode: RPCErrorCodes.kServiceDisappeared,
),
], httpAddress: Uri.parse('http://localhost:1234'));
final FakeFlutterDevice device = FakeFlutterDevice()
..vmService = fakeVmServiceHost.vmService;
await handler.serveAndAnnounceDevTools(
flutterDevices: <FlutterDevice>[device],
);
});
testWithoutContext('serveAndAnnounceDevTools with multiple devices and VM service disappears on one', () async {
final ResidentDevtoolsHandler handler = FlutterResidentDevtoolsHandler(
FakeDevtoolsLauncher()..activeDevToolsServer = DevToolsServerAddress('localhost', 8080),
FakeResidentRunner(),
BufferLogger.test(),
);
final FakeVmServiceHost vmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
const FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{
'streamId': 'Isolate',
},
),
listViews,
FakeVmServiceRequest( FakeVmServiceRequest(
method: 'getIsolate', method: 'getIsolate',
jsonResponse: isolate.toJson(), jsonResponse: isolate.toJson(),
...@@ -234,46 +230,61 @@ void main() { ...@@ -234,46 +230,61 @@ void main() {
'isolateId': '1', 'isolateId': '1',
}, },
), ),
]); const FakeVmServiceRequest(
waitForExtension(fakeVmServiceHost.vmService.service, 'foo'); method: 'streamCancel',
}); args: <String, Object>{
'streamId': 'Isolate',
},
),
listViews,
const FakeVmServiceRequest(
method: 'ext.flutter.activeDevToolsServerAddress',
args: <String, Object>{
'isolateId': '1',
'value': 'http://localhost:8080',
},
),
listViews,
const FakeVmServiceRequest(
method: 'ext.flutter.connectedVmServiceUri',
args: <String, Object>{
'isolateId': '1',
'value': 'http://localhost:1234',
},
),
], httpAddress: Uri.parse('http://localhost:1234'));
testWithoutContext('wait for extension handles no isolates', () { final FakeVmServiceHost vmServiceHostThatDisappears = FakeVmServiceHost(requests: <VmServiceExpectation>[
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
const FakeVmServiceRequest( const FakeVmServiceRequest(
method: 'streamListen', method: 'streamListen',
args: <String, Object>{ args: <String, Object>{
'streamId': 'Extension', 'streamId': 'Isolate',
} },
), ),
FakeVmServiceRequest(method: 'getVM', jsonResponse: vm_service.VM( const FakeVmServiceRequest(
isolates: <vm_service.IsolateRef>[], method: kListViewsMethod,
pid: 1, errorCode: RPCErrorCodes.kServiceDisappeared,
hostCPU: '',
isolateGroups: <vm_service.IsolateGroupRef>[],
targetCPU: '',
startTime: 0,
name: 'dart',
architectureBits: 64,
operatingSystem: '',
version: '',
systemIsolateGroups: <vm_service.IsolateGroupRef>[],
systemIsolates: <vm_service.IsolateRef>[],
).toJson()),
FakeVmServiceStreamResponse(
streamId: 'Extension',
event: vm_service.Event(
timestamp: 0,
extensionKind: 'Flutter.FrameworkInitialization',
kind: 'test',
),
), ),
]); const FakeVmServiceRequest(
waitForExtension(fakeVmServiceHost.vmService.service, 'foo'); method: 'streamCancel',
args: <String, Object>{
'streamId': 'Isolate',
},
errorCode: RPCErrorCodes.kServiceDisappeared,
),
], httpAddress: Uri.parse('http://localhost:5678'));
await handler.serveAndAnnounceDevTools(
flutterDevices: <FlutterDevice>[
FakeFlutterDevice()
..vmService = vmServiceHostThatDisappears.vmService,
FakeFlutterDevice()
..vmService = vmServiceHost.vmService,
],
);
}); });
} }
class FakeDevtoolsLauncher extends Fake implements DevtoolsLauncher { class FakeDevtoolsLauncher extends Fake implements DevtoolsLauncher {
@override @override
DevToolsServerAddress activeDevToolsServer; DevToolsServerAddress activeDevToolsServer;
...@@ -296,6 +307,11 @@ class FakeResidentRunner extends Fake implements ResidentRunner { ...@@ -296,6 +307,11 @@ class FakeResidentRunner extends Fake implements ResidentRunner {
} }
class FakeFlutterDevice extends Fake implements FlutterDevice { class FakeFlutterDevice extends Fake implements FlutterDevice {
@override
final Device device = FakeDevice();
@override @override
FlutterVmService vmService; FlutterVmService vmService;
} }
class FakeDevice extends Fake implements Device {}
...@@ -45,52 +45,47 @@ final Map<String, Object> vm = <String, dynamic>{ ...@@ -45,52 +45,47 @@ final Map<String, Object> vm = <String, dynamic>{
], ],
}; };
final vm_service.Isolate isolate = vm_service.Isolate.parse( const String kExtensionName = 'ext.flutter.test.interestingExtension';
<String, dynamic>{
'type': 'Isolate', final vm_service.Isolate isolate = vm_service.Isolate(
'fixedId': true, id: '1',
'id': 'isolates/242098474', pauseEvent: vm_service.Event(
'name': 'main.dart:main()', kind: vm_service.EventKind.kResume,
'number': 242098474, timestamp: 0
'_originNumber': 242098474, ),
'startTime': 1540488745340, breakpoints: <vm_service.Breakpoint>[],
'_heaps': <String, dynamic>{ exceptionPauseMode: null,
'new': <String, dynamic>{ libraries: <vm_service.LibraryRef>[
'used': 0, vm_service.LibraryRef(
'capacity': 0, id: '1',
'external': 0, uri: 'file:///hello_world/main.dart',
'collections': 0, name: '',
'time': 0.0, ),
'avgCollectionPeriodMillis': 0.0, ],
}, livePorts: 0,
'old': <String, dynamic>{ name: 'test',
'used': 0, number: '1',
'capacity': 0, pauseOnExit: false,
'external': 0, runnable: true,
'collections': 0, startTime: 0,
'time': 0.0, isSystemIsolate: false,
'avgCollectionPeriodMillis': 0.0, isolateFlags: <vm_service.IsolateFlag>[],
}, extensionRPCs: <String>[kExtensionName],
},
}
); );
final Map<String, Object> listViews = <String, dynamic>{ final FlutterView fakeFlutterView = FlutterView(
'type': 'FlutterViewList', id: 'a',
'views': <dynamic>[ uiIsolate: isolate,
<String, dynamic>{ );
'type': 'FlutterView',
'id': '_flutterView/0x4a4c1f8', final FakeVmServiceRequest listViewsRequest = FakeVmServiceRequest(
'isolate': <String, dynamic>{ method: kListViewsMethod,
'type': '@Isolate', jsonResponse: <String, Object>{
'fixedId': true, 'views': <Object>[
'id': 'isolates/242098474', fakeFlutterView.toJson(),
'name': 'main.dart:main()', ],
'number': 242098474, },
}, );
},
]
};
typedef ServiceCallback = Future<Map<String, dynamic>> Function(Map<String, Object>); typedef ServiceCallback = Future<Map<String, dynamic>> Function(Map<String, Object>);
...@@ -408,17 +403,7 @@ void main() { ...@@ -408,17 +403,7 @@ void main() {
'views': <Object>[], 'views': <Object>[],
}, },
), ),
const FakeVmServiceRequest( listViewsRequest,
method: kListViewsMethod,
jsonResponse: <String, Object>{
'views': <Object>[
<String, Object>{
'id': 'a',
'isolate': <String, Object>{},
},
],
},
),
] ]
); );
...@@ -452,6 +437,187 @@ void main() { ...@@ -452,6 +437,187 @@ void main() {
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
}); });
group('findExtensionIsolate', () {
testWithoutContext('returns an isolate with the registered extensionRPC', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
const FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{
'streamId': 'Isolate',
},
),
listViewsRequest,
FakeVmServiceRequest(
method: 'getIsolate',
jsonResponse: isolate.toJson(),
args: <String, Object>{
'isolateId': '1',
},
),
const FakeVmServiceRequest(
method: 'streamCancel',
args: <String, Object>{
'streamId': 'Isolate',
},
),
]);
final vm_service.IsolateRef isolateRef = await fakeVmServiceHost.vmService.findExtensionIsolate(kExtensionName);
expect(isolateRef.id, '1');
});
testWithoutContext('returns the isolate with the registered extensionRPC when there are multiple FlutterViews', () async {
const String otherExtensionName = 'ext.flutter.test.otherExtension';
// Copy the other isolate and change a few fields.
final vm_service.Isolate isolate2 = vm_service.Isolate.parse(
isolate.toJson()
..['id'] = '2'
..['extensionRPCs'] = <String>[otherExtensionName],
);
final FlutterView fakeFlutterView2 = FlutterView(
id: '2',
uiIsolate: isolate2,
);
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
const FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{
'streamId': 'Isolate',
},
),
FakeVmServiceRequest(
method: kListViewsMethod,
jsonResponse: <String, Object>{
'views': <Object>[
fakeFlutterView.toJson(),
fakeFlutterView2.toJson(),
],
},
),
FakeVmServiceRequest(
method: 'getIsolate',
jsonResponse: isolate.toJson(),
args: <String, Object>{
'isolateId': '1',
},
),
FakeVmServiceRequest(
method: 'getIsolate',
jsonResponse: isolate2.toJson(),
args: <String, Object>{
'isolateId': '2',
},
),
const FakeVmServiceRequest(
method: 'streamCancel',
args: <String, Object>{
'streamId': 'Isolate',
},
),
]);
final vm_service.IsolateRef isolateRef = await fakeVmServiceHost.vmService.findExtensionIsolate(otherExtensionName);
expect(isolateRef.id, '2');
});
testWithoutContext('when the isolate stream is already subscribed, returns an isolate with the registered extensionRPC', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
const FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{
'streamId': 'Isolate',
},
// Stream already subscribed - https://github.com/dart-lang/sdk/blob/master/runtime/vm/service/service.md#streamlisten
errorCode: 103,
),
listViewsRequest,
FakeVmServiceRequest(
method: 'getIsolate',
jsonResponse: isolate.toJson()..['extensionRPCs'] = <String>[kExtensionName],
args: <String, Object>{
'isolateId': '1',
},
),
const FakeVmServiceRequest(
method: 'streamCancel',
args: <String, Object>{
'streamId': 'Isolate',
},
),
]);
final vm_service.IsolateRef isolateRef = await fakeVmServiceHost.vmService.findExtensionIsolate(kExtensionName);
expect(isolateRef.id, '1');
});
testWithoutContext('returns an isolate with a extensionRPC that is registered later', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
const FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{
'streamId': 'Isolate',
},
),
listViewsRequest,
FakeVmServiceRequest(
method: 'getIsolate',
jsonResponse: isolate.toJson(),
args: <String, Object>{
'isolateId': '1',
},
),
FakeVmServiceStreamResponse(
streamId: 'Isolate',
event: vm_service.Event(
kind: vm_service.EventKind.kServiceExtensionAdded,
extensionRPC: kExtensionName,
timestamp: 1,
),
),
const FakeVmServiceRequest(
method: 'streamCancel',
args: <String, Object>{
'streamId': 'Isolate',
},
),
]);
final vm_service.IsolateRef isolateRef = await fakeVmServiceHost.vmService.findExtensionIsolate(kExtensionName);
expect(isolateRef.id, '1');
});
testWithoutContext('throws when the service disappears', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
const FakeVmServiceRequest(
method: 'streamListen',
args: <String, Object>{
'streamId': 'Isolate',
},
),
const FakeVmServiceRequest(
method: kListViewsMethod,
errorCode: RPCErrorCodes.kServiceDisappeared,
),
const FakeVmServiceRequest(
method: 'streamCancel',
args: <String, Object>{
'streamId': 'Isolate',
},
errorCode: RPCErrorCodes.kServiceDisappeared,
),
]);
expect(
() => fakeVmServiceHost.vmService.findExtensionIsolate(kExtensionName),
throwsA(isA<VmServiceDisappearedException>()),
);
});
});
testWithoutContext('Can process log events from the vm service', () { testWithoutContext('Can process log events from the vm service', () {
final vm_service.Event event = vm_service.Event( final vm_service.Event event = vm_service.Event(
bytes: base64.encode(utf8.encode('Hello There\n')), bytes: base64.encode(utf8.encode('Hello There\n')),
......
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