mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
This reverts commit 2e50fd75eb
.
This commit is contained in:
parent
2e50fd75eb
commit
07c451fea9
|
@ -620,12 +620,13 @@ class FuchsiaDevice extends Device {
|
|||
// loopback (::1).
|
||||
final Uri uri = Uri.parse('http://[$_ipv6Loopback]:$port');
|
||||
final VMService vmService = await VMService.connect(uri);
|
||||
final List<FlutterView> flutterViews = await vmService.getFlutterViews();
|
||||
for (final FlutterView flutterView in flutterViews) {
|
||||
await vmService.getVMOld();
|
||||
await vmService.refreshViews();
|
||||
for (final FlutterView flutterView in vmService.vm.views) {
|
||||
if (flutterView.uiIsolate == null) {
|
||||
continue;
|
||||
}
|
||||
final Uri address = vmService.httpAddress;
|
||||
final Uri address = flutterView.owner.vmService.httpAddress;
|
||||
if (flutterView.uiIsolate.name.contains(isolateName)) {
|
||||
return address.port;
|
||||
}
|
||||
|
@ -716,12 +717,13 @@ class FuchsiaIsolateDiscoveryProtocol {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
final List<FlutterView> flutterViews = await service.getFlutterViews();
|
||||
for (final FlutterView flutterView in flutterViews) {
|
||||
await service.getVMOld();
|
||||
await service.refreshViews();
|
||||
for (final FlutterView flutterView in service.vm.views) {
|
||||
if (flutterView.uiIsolate == null) {
|
||||
continue;
|
||||
}
|
||||
final Uri address = service.httpAddress;
|
||||
final Uri address = flutterView.owner.vmService.httpAddress;
|
||||
if (flutterView.uiIsolate.name.contains(_isolateName)) {
|
||||
_foundUri.complete(_device.ipv6
|
||||
? Uri.parse('http://[$_ipv6Loopback]:${address.port}/')
|
||||
|
|
|
@ -233,25 +233,18 @@ class FlutterDevice {
|
|||
if (vmService == null) {
|
||||
return;
|
||||
}
|
||||
final List<FlutterView> updatedViews = await vmService.getFlutterViews();
|
||||
_views
|
||||
..clear()
|
||||
..addAll(updatedViews);
|
||||
await flutterDeprecatedVmService.vm.refreshViews(waitForViews: true);
|
||||
}
|
||||
final List<FlutterView> _views = <FlutterView>[];
|
||||
|
||||
List<FlutterView> get views {
|
||||
if (vmService == null) {
|
||||
if (vmService == null || flutterDeprecatedVmService.isClosed) {
|
||||
return <FlutterView>[];
|
||||
}
|
||||
if (viewFilter != null) {
|
||||
return <FlutterView>[
|
||||
for (final FlutterView flutterView in views)
|
||||
if (flutterView.uiIsolate.name.contains(viewFilter))
|
||||
flutterView
|
||||
];
|
||||
}
|
||||
return _views;
|
||||
|
||||
|
||||
return (viewFilter != null
|
||||
? flutterDeprecatedVmService.vm.allViewsWithName(viewFilter)
|
||||
: flutterDeprecatedVmService.vm.views).toList();
|
||||
}
|
||||
|
||||
Future<void> getVMs() => flutterDeprecatedVmService.getVMOld();
|
||||
|
@ -261,32 +254,35 @@ class FlutterDevice {
|
|||
await device.stopApp(package);
|
||||
return;
|
||||
}
|
||||
await refreshViews();
|
||||
if (views == null || views.isEmpty) {
|
||||
final List<FlutterView> flutterViews = views;
|
||||
if (flutterViews == null || flutterViews.isEmpty) {
|
||||
return;
|
||||
}
|
||||
// If any of the flutter views are paused, we might not be able to
|
||||
// cleanly exit since the service extension may not have been registered.
|
||||
for (final FlutterView flutterView in views) {
|
||||
final vm_service.Isolate isolate = await vmService
|
||||
.getIsolateOrNull(flutterView.uiIsolate.id);
|
||||
if (isolate == null) {
|
||||
continue;
|
||||
}
|
||||
if (isPauseEvent(isolate.pauseEvent.kind)) {
|
||||
await device.stopApp(package);
|
||||
return;
|
||||
if (flutterViews.any((FlutterView view) {
|
||||
return view != null &&
|
||||
view.uiIsolate != null &&
|
||||
view.uiIsolate.pauseEvent != null &&
|
||||
view.uiIsolate.pauseEvent.isPauseEvent;
|
||||
}
|
||||
)) {
|
||||
await device.stopApp(package);
|
||||
return;
|
||||
}
|
||||
for (final FlutterView view in views) {
|
||||
final List<Future<void>> futures = <Future<void>>[];
|
||||
for (final FlutterView view in flutterViews) {
|
||||
if (view != null && view.uiIsolate != null) {
|
||||
// If successful, there will be no response from flutterExit.
|
||||
unawaited(vmService.flutterExit(
|
||||
assert(!view.uiIsolate.pauseEvent.isPauseEvent);
|
||||
futures.add(vmService.flutterExit(
|
||||
isolateId: view.uiIsolate.id,
|
||||
));
|
||||
}
|
||||
}
|
||||
return vmService.onDone;
|
||||
// The flutterExit message only returns if it fails, so just wait a few
|
||||
// seconds then assume it worked.
|
||||
// TODO(ianh): We should make this return once the VM service disconnects.
|
||||
await Future.wait(futures).timeout(const Duration(seconds: 2), onTimeout: () => <void>[]);
|
||||
}
|
||||
|
||||
Future<Uri> setupDevFS(
|
||||
|
|
|
@ -514,8 +514,10 @@ class HotRunner extends ResidentRunner {
|
|||
String reason,
|
||||
bool benchmarkMode = false,
|
||||
}) async {
|
||||
globals.printTrace('Refreshing active FlutterViews before restarting.');
|
||||
await refreshViews();
|
||||
if (!_isPaused()) {
|
||||
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
|
||||
|
@ -540,36 +542,31 @@ class HotRunner extends ResidentRunner {
|
|||
// Check if the isolate is paused and resume it.
|
||||
final List<Future<void>> operations = <Future<void>>[];
|
||||
for (final FlutterDevice device in flutterDevices) {
|
||||
final Set<String> uiIsolatesIds = <String>{};
|
||||
final Set<Isolate> uiIsolates = <Isolate>{};
|
||||
for (final FlutterView view in device.views) {
|
||||
if (view.uiIsolate == null) {
|
||||
continue;
|
||||
}
|
||||
uiIsolatesIds.add(view.uiIsolate.id);
|
||||
uiIsolates.add(view.uiIsolate);
|
||||
// Reload the isolate.
|
||||
final Future<vm_service.Isolate> reloadIsolate = device.vmService
|
||||
.getIsolateOrNull(view.uiIsolate.id);
|
||||
operations.add(reloadIsolate.then((vm_service.Isolate isolate) async {
|
||||
if ((isolate != null) && isPauseEvent(isolate.pauseEvent.kind)) {
|
||||
operations.add(view.uiIsolate.reload().then((ServiceObject _) {
|
||||
final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent;
|
||||
if ((pauseEvent != null) && pauseEvent.isPauseEvent) {
|
||||
// Resume the isolate so that it can be killed by the embedder.
|
||||
await device.vmService.resume(view.uiIsolate.id);
|
||||
return device.vmService.resume(view.uiIsolate.id);
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
}
|
||||
|
||||
// The engine handles killing and recreating isolates that it has spawned
|
||||
// ("uiIsolates"). The isolates that were spawned from these uiIsolates
|
||||
// will not be restared, and so they must be manually killed.
|
||||
final vm_service.VM vm = await device.vmService.getVM();
|
||||
for (final vm_service.IsolateRef isolateRef in vm.isolates) {
|
||||
if (uiIsolatesIds.contains(isolateRef.id)) {
|
||||
continue;
|
||||
for (final Isolate isolate in device?.flutterDeprecatedVmService?.vm?.isolates ?? <Isolate>[]) {
|
||||
if (!uiIsolates.contains(isolate)) {
|
||||
operations.add(isolate.invokeRpcRaw('kill', params: <String, dynamic>{
|
||||
'isolateId': isolate.id,
|
||||
}));
|
||||
}
|
||||
operations.add(device.vmService.kill(isolateRef.id)
|
||||
.catchError((dynamic error, StackTrace stackTrace) {
|
||||
// Do nothing on a SentinelException since it means the isolate
|
||||
// has already been killed.
|
||||
}, test: (dynamic error) => error is vm_service.SentinelException));
|
||||
}
|
||||
}
|
||||
await Future.wait(operations);
|
||||
|
@ -592,11 +589,13 @@ class HotRunner extends ResidentRunner {
|
|||
} on vm_service.RPCError {
|
||||
// Do nothing, we're already subcribed.
|
||||
}
|
||||
isolateNotifications.add(
|
||||
device.vmService.onIsolateEvent.firstWhere((vm_service.Event event) {
|
||||
return event.kind == vm_service.EventKind.kIsolateRunnable;
|
||||
}),
|
||||
);
|
||||
for (final FlutterView view in device.views) {
|
||||
isolateNotifications.add(
|
||||
view.owner.vm.vmService.onIsolateEvent.firstWhere((vm_service.Event event) {
|
||||
return event.kind == vm_service.EventKind.kIsolateRunnable;
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
await Future.wait(isolateNotifications);
|
||||
}
|
||||
|
@ -819,9 +818,11 @@ class HotRunner extends ResidentRunner {
|
|||
|
||||
final Stopwatch reloadTimer = Stopwatch()..start();
|
||||
|
||||
globals.printTrace('Refreshing active FlutterViews before reloading.');
|
||||
await refreshVM();
|
||||
await refreshViews();
|
||||
if (!_isPaused()) {
|
||||
globals.printTrace('Refreshing active FlutterViews before reloading.');
|
||||
await refreshVM();
|
||||
await refreshViews();
|
||||
}
|
||||
|
||||
final Stopwatch devFSTimer = Stopwatch()..start();
|
||||
final UpdateFSReport updatedDevFS = await _updateDevFS();
|
||||
|
@ -913,19 +914,27 @@ 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(<Future<void>>[
|
||||
for (final FlutterDevice device in flutterDevices)
|
||||
device.refreshViews()
|
||||
]);
|
||||
// Reload the isolate.
|
||||
final List<Future<void>> allDevices = <Future<void>>[];
|
||||
for (final FlutterDevice device in flutterDevices) {
|
||||
globals.printTrace('Sending reload events to ${device.device.name}');
|
||||
final List<Future<ServiceObject>> futuresViews = <Future<ServiceObject>>[];
|
||||
for (final FlutterView view in device.views) {
|
||||
globals.printTrace('Sending reload event to "${view.uiIsolate.name}"');
|
||||
futuresViews.add(view.uiIsolate.reload());
|
||||
}
|
||||
allDevices.add(Future.wait(futuresViews).whenComplete(() {
|
||||
return device.refreshViews();
|
||||
}));
|
||||
}
|
||||
await Future.wait(allDevices);
|
||||
|
||||
globals.printTrace('Evicting dirty assets');
|
||||
await _evictDirtyAssets();
|
||||
|
||||
// Check if any isolates are paused and reassemble those
|
||||
// that aren't.
|
||||
final Map<FlutterView, vm_service.VmService> reassembleViews = <FlutterView, vm_service.VmService>{};
|
||||
final List<FlutterView> reassembleViews = <FlutterView>[];
|
||||
final List<Future<void>> reassembleFutures = <Future<void>>[];
|
||||
String serviceEventKind;
|
||||
int pausedIsolatesFound = 0;
|
||||
|
@ -934,12 +943,8 @@ class HotRunner extends ResidentRunner {
|
|||
for (final FlutterView view in device.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
|
||||
.getIsolateOrNull(view.uiIsolate.id);
|
||||
final vm_service.Event pauseEvent = isolate?.pauseEvent;
|
||||
if (pauseEvent != null
|
||||
&& isPauseEvent(pauseEvent.kind)
|
||||
&& pauseEvent.kind != vm_service.EventKind.kPausePostRequest) {
|
||||
final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent;
|
||||
if (pauseEvent != null && pauseEvent.isPauseEvent && pauseEvent.kind != ServiceEvent.kPausePostRequest) {
|
||||
pausedIsolatesFound += 1;
|
||||
if (serviceEventKind == null) {
|
||||
serviceEventKind = pauseEvent.kind;
|
||||
|
@ -947,7 +952,7 @@ class HotRunner extends ResidentRunner {
|
|||
serviceEventKind = ''; // many kinds
|
||||
}
|
||||
} else {
|
||||
reassembleViews[view] = device.vmService;
|
||||
reassembleViews.add(view);
|
||||
reassembleFutures.add(device.vmService.flutterReassemble(
|
||||
isolateId: view.uiIsolate.id,
|
||||
).catchError((dynamic error) {
|
||||
|
@ -969,7 +974,6 @@ class HotRunner extends ResidentRunner {
|
|||
assert(reassembleViews.isNotEmpty);
|
||||
|
||||
globals.printTrace('Reassembling application');
|
||||
|
||||
final Future<void> reassembleFuture = Future.wait<void>(reassembleFutures);
|
||||
await reassembleFuture.timeout(
|
||||
const Duration(seconds: 2),
|
||||
|
@ -982,17 +986,14 @@ class HotRunner extends ResidentRunner {
|
|||
globals.printTrace('This is taking a long time; will now check for paused isolates.');
|
||||
int postReloadPausedIsolatesFound = 0;
|
||||
String serviceEventKind;
|
||||
for (final FlutterView view in reassembleViews.keys) {
|
||||
final vm_service.Isolate isolate = await reassembleViews[view]
|
||||
.getIsolateOrNull(view.uiIsolate.id);
|
||||
if (isolate == null) {
|
||||
continue;
|
||||
}
|
||||
if (isolate.pauseEvent != null && isPauseEvent(isolate.pauseEvent.kind)) {
|
||||
for (final FlutterView view in reassembleViews) {
|
||||
await view.uiIsolate.reload();
|
||||
final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent;
|
||||
if (pauseEvent != null && pauseEvent.isPauseEvent) {
|
||||
postReloadPausedIsolatesFound += 1;
|
||||
if (serviceEventKind == null) {
|
||||
serviceEventKind = isolate.pauseEvent.kind;
|
||||
} else if (serviceEventKind != isolate.pauseEvent.kind) {
|
||||
serviceEventKind = pauseEvent.kind;
|
||||
} else if (serviceEventKind != pauseEvent.kind) {
|
||||
serviceEventKind = ''; // many kinds
|
||||
}
|
||||
}
|
||||
|
@ -1068,33 +1069,32 @@ class HotRunner extends ResidentRunner {
|
|||
}
|
||||
assert(serviceEventKind != null);
|
||||
switch (serviceEventKind) {
|
||||
case vm_service.EventKind.kPauseStart:
|
||||
message.write('paused (probably due to --start-paused)');
|
||||
break;
|
||||
case vm_service.EventKind.kPauseExit:
|
||||
message.write('paused because ${ plural ? 'they have' : 'it has' } terminated');
|
||||
break;
|
||||
case vm_service.EventKind.kPauseBreakpoint:
|
||||
message.write('paused in the debugger on a breakpoint');
|
||||
break;
|
||||
case vm_service.EventKind.kPauseInterrupted:
|
||||
message.write('paused due in the debugger');
|
||||
break;
|
||||
case vm_service.EventKind.kPauseException:
|
||||
message.write('paused in the debugger after an exception was thrown');
|
||||
break;
|
||||
case vm_service.EventKind.kPausePostRequest:
|
||||
message.write('paused');
|
||||
break;
|
||||
case '':
|
||||
message.write('paused for various reasons');
|
||||
break;
|
||||
case ServiceEvent.kPauseStart: message.write('paused (probably due to --start-paused)'); break;
|
||||
case ServiceEvent.kPauseExit: message.write('paused because ${ plural ? 'they have' : 'it has' } terminated'); break;
|
||||
case ServiceEvent.kPauseBreakpoint: message.write('paused in the debugger on a breakpoint'); break;
|
||||
case ServiceEvent.kPauseInterrupted: message.write('paused due in the debugger'); break;
|
||||
case ServiceEvent.kPauseException: message.write('paused in the debugger after an exception was thrown'); break;
|
||||
case ServiceEvent.kPausePostRequest: message.write('paused'); break;
|
||||
case '': message.write('paused for various reasons'); break;
|
||||
default:
|
||||
message.write('paused');
|
||||
}
|
||||
return message.toString();
|
||||
}
|
||||
|
||||
bool _isPaused() {
|
||||
for (final FlutterDevice device in flutterDevices) {
|
||||
for (final FlutterView view in device.views) {
|
||||
if (view.uiIsolate != null) {
|
||||
final ServiceEvent pauseEvent = view.uiIsolate.pauseEvent;
|
||||
if (pauseEvent != null && pauseEvent.isPauseEvent) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
void printHelp({ @required bool details }) {
|
||||
|
@ -1134,7 +1134,7 @@ class HotRunner extends ResidentRunner {
|
|||
}
|
||||
for (final String assetPath in device.devFS.assetPathsToEvict) {
|
||||
futures.add(
|
||||
device.vmService
|
||||
device.views.first.uiIsolate.vmService
|
||||
.flutterEvictAsset(
|
||||
assetPath,
|
||||
isolateId: device.views.first.uiIsolate.id,
|
||||
|
|
|
@ -53,9 +53,8 @@ class Tracing {
|
|||
}
|
||||
});
|
||||
bool done = false;
|
||||
final List<FlutterView> views = await vmService.getFlutterViews();
|
||||
for (final FlutterView view in views) {
|
||||
if (await vmService
|
||||
for (final FlutterView view in vmService.vm.views) {
|
||||
if (await view.uiIsolate.vmService
|
||||
.flutterAlreadyPaintedFirstUsefulFrame(
|
||||
isolateId: view.uiIsolate.id,
|
||||
)) {
|
||||
|
|
|
@ -19,7 +19,6 @@ const String kGetSkSLsMethod = '_flutter.getSkSLs';
|
|||
const String kSetAssetBundlePathMethod = '_flutter.setAssetBundlePath';
|
||||
const String kFlushUIThreadTasksMethod = '_flutter.flushUIThreadTasks';
|
||||
const String kRunInViewMethod = '_flutter.runInView';
|
||||
const String kListViewsMethod = '_flutter.listViews';
|
||||
|
||||
/// The error response code from an unrecoverable compilation failure.
|
||||
const int kIsolateReloadBarred = 1005;
|
||||
|
@ -510,10 +509,10 @@ class VMService implements vm_service.VmService {
|
|||
// getFromMap creates the Isolate if necessary.
|
||||
final Isolate isolate = vm.getFromMap(eventIsolate) as Isolate;
|
||||
event = ServiceObject._fromMap(isolate, eventData) as ServiceEvent;
|
||||
if (event.kind == vm_service.EventKind.kIsolateExit) {
|
||||
if (event.kind == ServiceEvent.kIsolateExit) {
|
||||
vm._isolateCache.remove(isolate.id);
|
||||
vm._buildIsolateList();
|
||||
} else if (event.kind == vm_service.EventKind.kIsolateRunnable) {
|
||||
} else if (event.kind == ServiceEvent.kIsolateRunnable) {
|
||||
// Force reload once the isolate becomes runnable so that we
|
||||
// update the root library.
|
||||
isolate.reload();
|
||||
|
@ -533,6 +532,8 @@ class VMService implements vm_service.VmService {
|
|||
/// Reloads the VM.
|
||||
Future<void> getVMOld() async => await vm.reload();
|
||||
|
||||
Future<void> refreshViews({ bool waitForViews = false }) => vm.refreshViews(waitForViews: waitForViews);
|
||||
|
||||
Future<void> close() async {
|
||||
_delegateService?.dispose();
|
||||
}
|
||||
|
@ -554,16 +555,6 @@ class VMService implements vm_service.VmService {
|
|||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<vm_service.Isolate> getIsolate(String isolateId) {
|
||||
return _delegateService.getIsolate(isolateId);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<vm_service.Success> resume(String isolateId, {String step, int frameIndex}) {
|
||||
return _delegateService.resume(isolateId, step: step, frameIndex: frameIndex);
|
||||
}
|
||||
|
||||
// To enable a gradual migration to package:vm_service
|
||||
@override
|
||||
dynamic noSuchMethod(Invocation invocation) {
|
||||
|
@ -653,6 +644,9 @@ abstract class ServiceObject {
|
|||
case 'Event':
|
||||
serviceObject = ServiceEvent._empty(owner);
|
||||
break;
|
||||
case 'FlutterView':
|
||||
serviceObject = FlutterView._empty(owner.vm);
|
||||
break;
|
||||
case 'Isolate':
|
||||
serviceObject = Isolate._empty(owner.vm);
|
||||
break;
|
||||
|
@ -796,6 +790,34 @@ class ServiceEvent extends ServiceObject {
|
|||
String _message;
|
||||
String get message => _message;
|
||||
|
||||
// The possible 'kind' values.
|
||||
static const String kVMUpdate = 'VMUpdate';
|
||||
static const String kIsolateStart = 'IsolateStart';
|
||||
static const String kIsolateRunnable = 'IsolateRunnable';
|
||||
static const String kIsolateExit = 'IsolateExit';
|
||||
static const String kIsolateUpdate = 'IsolateUpdate';
|
||||
static const String kIsolateReload = 'IsolateReload';
|
||||
static const String kIsolateSpawn = 'IsolateSpawn';
|
||||
static const String kServiceExtensionAdded = 'ServiceExtensionAdded';
|
||||
static const String kPauseStart = 'PauseStart';
|
||||
static const String kPauseExit = 'PauseExit';
|
||||
static const String kPauseBreakpoint = 'PauseBreakpoint';
|
||||
static const String kPauseInterrupted = 'PauseInterrupted';
|
||||
static const String kPauseException = 'PauseException';
|
||||
static const String kPausePostRequest = 'PausePostRequest';
|
||||
static const String kNone = 'None';
|
||||
static const String kResume = 'Resume';
|
||||
static const String kBreakpointAdded = 'BreakpointAdded';
|
||||
static const String kBreakpointResolved = 'BreakpointResolved';
|
||||
static const String kBreakpointRemoved = 'BreakpointRemoved';
|
||||
static const String kGraph = '_Graph';
|
||||
static const String kGC = 'GC';
|
||||
static const String kInspect = 'Inspect';
|
||||
static const String kDebuggerSettingsUpdate = '_DebuggerSettingsUpdate';
|
||||
static const String kConnectionClosed = 'ConnectionClosed';
|
||||
static const String kLogging = '_Logging';
|
||||
static const String kExtension = 'Extension';
|
||||
|
||||
@override
|
||||
void _update(Map<String, dynamic> map, bool mapIsRef) {
|
||||
_loaded = true;
|
||||
|
@ -820,6 +842,16 @@ class ServiceEvent extends ServiceObject {
|
|||
_message = utf8.decode(base64.decode(base64Bytes)).trim();
|
||||
}
|
||||
}
|
||||
|
||||
bool get isPauseEvent {
|
||||
return kind == kPauseStart ||
|
||||
kind == kPauseExit ||
|
||||
kind == kPauseBreakpoint ||
|
||||
kind == kPauseInterrupted ||
|
||||
kind == kPauseException ||
|
||||
kind == kPausePostRequest ||
|
||||
kind == kNone;
|
||||
}
|
||||
}
|
||||
|
||||
/// A ServiceObjectOwner is either a [VM] or an [Isolate]. Owners can cache
|
||||
|
@ -885,6 +917,9 @@ class VM extends ServiceObjectOwner {
|
|||
/// The list of live isolates, ordered by isolate start time.
|
||||
final List<Isolate> isolates = <Isolate>[];
|
||||
|
||||
/// The set of live views.
|
||||
final Map<String, FlutterView> _viewCache = <String, FlutterView>{};
|
||||
|
||||
/// The number of bytes allocated (e.g. by malloc) in the native heap.
|
||||
int _heapAllocatedMemoryUsage;
|
||||
int get heapAllocatedMemoryUsage => _heapAllocatedMemoryUsage ?? 0;
|
||||
|
@ -973,6 +1008,16 @@ class VM extends ServiceObjectOwner {
|
|||
isolate.updateFromMap(map);
|
||||
}
|
||||
return isolate;
|
||||
case 'FlutterView':
|
||||
FlutterView view = _viewCache[mapId];
|
||||
if (view == null) {
|
||||
// Add new view to the cache.
|
||||
view = ServiceObject._fromMap(this, map) as FlutterView;
|
||||
_viewCache[mapId] = view;
|
||||
} else {
|
||||
view.updateFromMap(map);
|
||||
}
|
||||
return view;
|
||||
default:
|
||||
// If we don't have a model object for this service object type, as a
|
||||
// fallback return a ServiceMap object.
|
||||
|
@ -1049,6 +1094,47 @@ class VM extends ServiceObjectOwner {
|
|||
Future<Map<String, dynamic>> getVMTimeline() {
|
||||
return invokeRpcRaw('getVMTimeline');
|
||||
}
|
||||
|
||||
Future<void> refreshViews({ bool waitForViews = false }) async {
|
||||
assert(waitForViews != null);
|
||||
assert(loaded);
|
||||
if (!isFlutterEngine) {
|
||||
return;
|
||||
}
|
||||
int failCount = 0;
|
||||
while (true) {
|
||||
_viewCache.clear();
|
||||
// When the future returned by invokeRpc() below returns,
|
||||
// the _viewCache will have been updated.
|
||||
// This message updates all the views of every isolate.
|
||||
await vmService.vm.invokeRpc<ServiceObject>(
|
||||
'_flutter.listViews', truncateLogs: false);
|
||||
if (_viewCache.values.isNotEmpty || !waitForViews) {
|
||||
return;
|
||||
}
|
||||
failCount += 1;
|
||||
if (failCount == 5) { // waited 200ms
|
||||
globals.printStatus('Flutter is taking longer than expected to report its views. Still trying...');
|
||||
}
|
||||
await Future<void>.delayed(const Duration(milliseconds: 50));
|
||||
await reload();
|
||||
}
|
||||
}
|
||||
|
||||
Iterable<FlutterView> get views => _viewCache.values;
|
||||
|
||||
FlutterView get firstView {
|
||||
return _viewCache.values.isEmpty ? null : _viewCache.values.first;
|
||||
}
|
||||
|
||||
List<FlutterView> allViewsWithName(String isolateFilter) {
|
||||
if (_viewCache.values.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
return _viewCache.values.where(
|
||||
(FlutterView v) => v.uiIsolate.name.contains(isolateFilter)
|
||||
).toList();
|
||||
}
|
||||
}
|
||||
|
||||
/// An isolate running inside the VM. Instances of the Isolate class are always
|
||||
|
@ -1198,39 +1284,23 @@ class ServiceMap extends ServiceObject implements Map<String, dynamic> {
|
|||
}
|
||||
|
||||
/// Peered to an Android/iOS FlutterView widget on a device.
|
||||
class FlutterView {
|
||||
FlutterView({
|
||||
@required this.id,
|
||||
@required this.uiIsolate,
|
||||
});
|
||||
class FlutterView extends ServiceObject {
|
||||
FlutterView._empty(ServiceObjectOwner owner) : super._empty(owner);
|
||||
|
||||
factory FlutterView.parse(Map<String, Object> json) {
|
||||
final Map<String, Object> rawIsolate = json['isolate'] as Map<String, Object>;
|
||||
vm_service.IsolateRef isolate;
|
||||
if (rawIsolate != null) {
|
||||
rawIsolate['number'] = rawIsolate['number']?.toString();
|
||||
isolate = vm_service.IsolateRef.parse(rawIsolate);
|
||||
}
|
||||
return FlutterView(
|
||||
id: json['id'] as String,
|
||||
uiIsolate: isolate,
|
||||
);
|
||||
Isolate _uiIsolate;
|
||||
Isolate get uiIsolate => _uiIsolate;
|
||||
|
||||
@override
|
||||
void _update(Map<String, dynamic> map, bool mapIsRef) {
|
||||
_loaded = !mapIsRef;
|
||||
_upgradeCollection(map, owner);
|
||||
_uiIsolate = map['isolate'] as Isolate;
|
||||
}
|
||||
|
||||
final vm_service.IsolateRef uiIsolate;
|
||||
final String id;
|
||||
|
||||
bool get hasIsolate => uiIsolate != null;
|
||||
bool get hasIsolate => _uiIsolate != null;
|
||||
|
||||
@override
|
||||
String toString() => id;
|
||||
|
||||
Map<String, Object> toJson() {
|
||||
return <String, Object>{
|
||||
'id': id,
|
||||
'isolate': uiIsolate?.toJson(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Flutter specific VM Service functionality.
|
||||
|
@ -1462,15 +1532,13 @@ extension FlutterVmService on vm_service.VmService {
|
|||
///
|
||||
/// This method is only supported by certain embedders. This is
|
||||
/// described by [Device.supportsFlutterExit].
|
||||
Future<void> flutterExit({
|
||||
Future<Map<String, dynamic>> flutterExit({
|
||||
@required String isolateId,
|
||||
}) {
|
||||
return invokeFlutterExtensionRpcRaw(
|
||||
'ext.flutter.exit',
|
||||
isolateId: isolateId,
|
||||
).catchError((dynamic error, StackTrace stackTrace) {
|
||||
// Do nothing on sentinel or exception, the isolate already exited.
|
||||
}, test: (dynamic error) => error is vm_service.SentinelException || error is vm_service.RPCError);
|
||||
);
|
||||
}
|
||||
|
||||
/// Return the current platform override for the flutter view running with
|
||||
|
@ -1520,37 +1588,4 @@ extension FlutterVmService on vm_service.VmService {
|
|||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// List all [FlutterView]s attached to the current VM.
|
||||
Future<List<FlutterView>> getFlutterViews() async {
|
||||
final vm_service.Response response = await callMethod(
|
||||
kListViewsMethod,
|
||||
);
|
||||
final List<Object> rawViews = response.json['views'] as List<Object>;
|
||||
return <FlutterView>[
|
||||
for (final Object rawView in rawViews)
|
||||
FlutterView.parse(rawView as Map<String, Object>)
|
||||
];
|
||||
}
|
||||
|
||||
/// Attempt to retrieve the isolate with id [isolateId], or `null` if it has
|
||||
/// been collected.
|
||||
Future<vm_service.Isolate> getIsolateOrNull(String isolateId) {
|
||||
return getIsolate(isolateId)
|
||||
.catchError((dynamic error, StackTrace stackTrace) {
|
||||
return null;
|
||||
}, test: (dynamic error) => error is vm_service.SentinelException);
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the event attached to an [Isolate.pauseEvent] should be considered
|
||||
/// a "pause" event.
|
||||
bool isPauseEvent(String kind) {
|
||||
return kind == vm_service.EventKind.kPauseStart ||
|
||||
kind == vm_service.EventKind.kPauseExit ||
|
||||
kind == vm_service.EventKind.kPauseBreakpoint ||
|
||||
kind == vm_service.EventKind.kPauseInterrupted ||
|
||||
kind == vm_service.EventKind.kPauseException ||
|
||||
kind == vm_service.EventKind.kPausePostRequest ||
|
||||
kind == vm_service.EventKind.kNone;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ import 'dart:async';
|
|||
import 'package:file/memory.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:process/process.dart';
|
||||
import 'package:quiver/testing/async.dart';
|
||||
import 'package:vm_service/vm_service.dart' as vm_service;
|
||||
|
@ -35,22 +34,6 @@ import '../../src/context.dart';
|
|||
import '../../src/fakes.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: <vm_service.Breakpoint>[],
|
||||
exceptionPauseMode: null,
|
||||
libraries: <vm_service.LibraryRef>[],
|
||||
livePorts: 0,
|
||||
name: 'test',
|
||||
number: '1',
|
||||
pauseOnExit: false,
|
||||
runnable: true,
|
||||
startTime: 0,
|
||||
);
|
||||
|
||||
void main() {
|
||||
group('attach', () {
|
||||
|
@ -508,7 +491,7 @@ void main() {
|
|||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => testFileSystem,
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
}, skip: const LocalPlatform().isWindows); // mDNS does not work on Windows.
|
||||
});
|
||||
|
||||
group('forwarding to given port', () {
|
||||
const int devicePort = 499;
|
||||
|
@ -822,21 +805,11 @@ VMServiceConnector getFakeVmServiceFactory({
|
|||
version: '',
|
||||
);
|
||||
});
|
||||
when(vmService.getIsolate(any))
|
||||
.thenAnswer((Invocation invocation) async {
|
||||
return fakeUnpausedIsolate;
|
||||
});
|
||||
when(vmService.callMethod(kListViewsMethod))
|
||||
.thenAnswer((_) async {
|
||||
return vm_service.Response.parse(<String, Object>{
|
||||
'views': <Object>[
|
||||
<String, Object>{
|
||||
'id': '1',
|
||||
'isolate': fakeUnpausedIsolate.toJson()
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
when(vm.refreshViews(waitForViews: anyNamed('waitForViews')))
|
||||
.thenAnswer((_) => Future<void>.value(null));
|
||||
when(vm.views)
|
||||
.thenReturn(<FlutterView>[FlutterViewMock()]);
|
||||
when(vm.createDevFS(any))
|
||||
.thenAnswer((_) => Future<Map<String, dynamic>>.value(<String, dynamic>{'uri': '/',}));
|
||||
|
||||
|
@ -886,6 +859,7 @@ class TestHotRunnerFactory extends HotRunnerFactory {
|
|||
|
||||
class VMMock extends Mock implements VM {}
|
||||
class VMServiceMock extends Mock implements VMService {}
|
||||
class FlutterViewMock extends Mock implements FlutterView {}
|
||||
class MockProcessManager extends Mock implements ProcessManager {}
|
||||
class MockProcess extends Mock implements Process {}
|
||||
class MockHttpClientRequest extends Mock implements HttpClientRequest {}
|
||||
|
|
|
@ -6,12 +6,6 @@ import 'dart:async';
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:vm_service/vm_service.dart' as vm_service;
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
import 'package:flutter_tools/src/application_package.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
|
@ -33,27 +27,14 @@ import 'package:flutter_tools/src/fuchsia/tiles_ctl.dart';
|
|||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/vmservice.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
|
||||
final vm_service.Isolate fakeIsolate = vm_service.Isolate(
|
||||
id: '1',
|
||||
pauseEvent: vm_service.Event(
|
||||
kind: vm_service.EventKind.kResume,
|
||||
timestamp: 0
|
||||
),
|
||||
breakpoints: <vm_service.Breakpoint>[],
|
||||
exceptionPauseMode: null,
|
||||
libraries: <vm_service.LibraryRef>[],
|
||||
livePorts: 0,
|
||||
name: 'wrong name',
|
||||
number: '1',
|
||||
pauseOnExit: false,
|
||||
runnable: true,
|
||||
startTime: 0,
|
||||
);
|
||||
|
||||
void main() {
|
||||
group('fuchsia device', () {
|
||||
MemoryFileSystem memoryFileSystem;
|
||||
|
@ -583,92 +564,71 @@ void main() {
|
|||
});
|
||||
|
||||
|
||||
group('FuchsiaIsolateDiscoveryProtocol', () {
|
||||
group(FuchsiaIsolateDiscoveryProtocol, () {
|
||||
MockPortForwarder portForwarder;
|
||||
MockVMService vmService;
|
||||
MockVM vm;
|
||||
|
||||
setUp(() {
|
||||
portForwarder = MockPortForwarder();
|
||||
vmService = MockVMService();
|
||||
vm = MockVM();
|
||||
|
||||
when(vm.vmService).thenReturn(vmService);
|
||||
when(vmService.vm).thenReturn(vm);
|
||||
});
|
||||
|
||||
Future<Uri> findUri(List<FlutterView> views, String expectedIsolateName) async {
|
||||
Future<Uri> findUri(List<MockFlutterView> views, String expectedIsolateName) async {
|
||||
when(vm.views).thenReturn(views);
|
||||
for (final MockFlutterView view in views) {
|
||||
when(view.owner).thenReturn(vm);
|
||||
}
|
||||
final MockFuchsiaDevice fuchsiaDevice =
|
||||
MockFuchsiaDevice('123', portForwarder, false);
|
||||
MockFuchsiaDevice('123', portForwarder, false);
|
||||
final FuchsiaIsolateDiscoveryProtocol discoveryProtocol =
|
||||
FuchsiaIsolateDiscoveryProtocol(
|
||||
FuchsiaIsolateDiscoveryProtocol(
|
||||
fuchsiaDevice,
|
||||
expectedIsolateName,
|
||||
(Uri uri) async => vmService,
|
||||
true, // only poll once.
|
||||
);
|
||||
|
||||
when(fuchsiaDevice.servicePorts())
|
||||
.thenAnswer((Invocation invocation) async => <int>[1]);
|
||||
when(portForwarder.forward(1))
|
||||
.thenAnswer((Invocation invocation) async => 2);
|
||||
when(vmService.getVMOld())
|
||||
.thenAnswer((Invocation invocation) => Future<void>.value(null));
|
||||
when(vmService.refreshViews())
|
||||
.thenAnswer((Invocation invocation) => Future<void>.value(null));
|
||||
when(vmService.httpAddress).thenReturn(Uri.parse('example'));
|
||||
when(vmService.callMethod(kListViewsMethod)).thenAnswer((Invocation invocation) async {
|
||||
return vm_service.Response.parse(<String, Object>{
|
||||
'views': <Object>[
|
||||
for (FlutterView view in views)
|
||||
view.toJson()
|
||||
],
|
||||
});
|
||||
});
|
||||
return await discoveryProtocol.uri;
|
||||
}
|
||||
|
||||
testUsingContext('can find flutter view with matching isolate name', () async {
|
||||
const String expectedIsolateName = 'foobar';
|
||||
final Uri uri = await findUri(<FlutterView>[
|
||||
// no ui isolate.
|
||||
FlutterView(id: '1', uiIsolate: null),
|
||||
// wrong name.
|
||||
FlutterView(
|
||||
id: '2',
|
||||
uiIsolate: vm_service.Isolate.parse(<String, dynamic>{
|
||||
...fakeIsolate.toJson(),
|
||||
'name': 'Wrong name',
|
||||
}),
|
||||
),
|
||||
// matching name.
|
||||
FlutterView(
|
||||
id: '3',
|
||||
uiIsolate: vm_service.Isolate.parse(<String, dynamic>{
|
||||
...fakeIsolate.toJson(),
|
||||
'name': expectedIsolateName,
|
||||
}),
|
||||
),
|
||||
final Uri uri = await findUri(<MockFlutterView>[
|
||||
MockFlutterView(null), // no ui isolate.
|
||||
MockFlutterView(MockIsolate('wrong name')), // wrong name.
|
||||
MockFlutterView(MockIsolate(expectedIsolateName)), // matching name.
|
||||
], expectedIsolateName);
|
||||
|
||||
expect(
|
||||
uri.toString(), 'http://${InternetAddress.loopbackIPv4.address}:0/');
|
||||
});
|
||||
|
||||
testUsingContext('can handle flutter view without matching isolate name', () async {
|
||||
const String expectedIsolateName = 'foobar';
|
||||
final Future<Uri> uri = findUri(<FlutterView>[
|
||||
// no ui isolate.
|
||||
FlutterView(id: '1', uiIsolate: null),
|
||||
// wrong name.
|
||||
FlutterView(id: '2', uiIsolate: vm_service.Isolate.parse(<String, Object>{
|
||||
...fakeIsolate.toJson(),
|
||||
'name': 'wrong name',
|
||||
})),
|
||||
final Future<Uri> uri = findUri(<MockFlutterView>[
|
||||
MockFlutterView(null), // no ui isolate.
|
||||
MockFlutterView(MockIsolate('wrong name')), // wrong name.
|
||||
], expectedIsolateName);
|
||||
|
||||
expect(uri, throwsException);
|
||||
});
|
||||
|
||||
testUsingContext('can handle non flutter view', () async {
|
||||
const String expectedIsolateName = 'foobar';
|
||||
final Future<Uri> uri = findUri(<FlutterView>[
|
||||
FlutterView(id: '1', uiIsolate: null), // no ui isolate.
|
||||
final Future<Uri> uri = findUri(<MockFlutterView>[
|
||||
MockFlutterView(null), // no ui isolate.
|
||||
], expectedIsolateName);
|
||||
|
||||
expect(uri, throwsException);
|
||||
});
|
||||
});
|
||||
|
@ -1080,6 +1040,22 @@ class MockPortForwarder extends Mock implements DevicePortForwarder {}
|
|||
|
||||
class MockVMService extends Mock implements VMService {}
|
||||
|
||||
class MockVM extends Mock implements VM {}
|
||||
|
||||
class MockFlutterView extends Mock implements FlutterView {
|
||||
MockFlutterView(this.uiIsolate);
|
||||
|
||||
@override
|
||||
final Isolate uiIsolate;
|
||||
}
|
||||
|
||||
class MockIsolate extends Mock implements Isolate {
|
||||
MockIsolate(this.name);
|
||||
|
||||
@override
|
||||
final String name;
|
||||
}
|
||||
|
||||
class FuchsiaDeviceWithFakeDiscovery extends FuchsiaDevice {
|
||||
FuchsiaDeviceWithFakeDiscovery(String id, {String name}) : super(id, name: name);
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:vm_service/vm_service.dart' as vm_service;
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
|
@ -171,52 +170,6 @@ void main() {
|
|||
});
|
||||
|
||||
testUsingContext('Does hot restarts when all devices support it', () async {
|
||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
const FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: kListViewsMethod,
|
||||
args: null,
|
||||
jsonResponse: <String, Object>{
|
||||
'views': <Object>[],
|
||||
}
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: kListViewsMethod,
|
||||
args: null,
|
||||
jsonResponse: <String, Object>{
|
||||
'views': <Object>[],
|
||||
}
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '3',
|
||||
method: 'getVM',
|
||||
args: null,
|
||||
jsonResponse: vm_service.VM.parse(<String, Object>{}).toJson()
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '4',
|
||||
method: 'getVM',
|
||||
args: null,
|
||||
jsonResponse: vm_service.VM.parse(<String, Object>{}).toJson()
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
id: '5',
|
||||
method: kListViewsMethod,
|
||||
args: null,
|
||||
jsonResponse: <String, Object>{
|
||||
'views': <Object>[],
|
||||
}
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
id: '6',
|
||||
method: kListViewsMethod,
|
||||
args: null,
|
||||
jsonResponse: <String, Object>{
|
||||
'views': <Object>[],
|
||||
}
|
||||
),
|
||||
]);
|
||||
// Setup mocks
|
||||
final MockDevice mockDevice = MockDevice();
|
||||
final MockDevice mockHotDevice = MockDevice();
|
||||
|
@ -226,12 +179,8 @@ void main() {
|
|||
when(mockHotDevice.supportsHotRestart).thenReturn(true);
|
||||
// Trigger a restart.
|
||||
final List<FlutterDevice> devices = <FlutterDevice>[
|
||||
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)
|
||||
..vmService = fakeVmServiceHost.vmService
|
||||
..devFS = mockDevFs,
|
||||
FlutterDevice(mockHotDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)
|
||||
..vmService = fakeVmServiceHost.vmService
|
||||
..devFS = mockDevFs,
|
||||
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = mockDevFs,
|
||||
FlutterDevice(mockHotDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = mockDevFs,
|
||||
];
|
||||
final HotRunner hotRunner = HotRunner(devices);
|
||||
final OperationResult result = await hotRunner.restart(fullRestart: true);
|
||||
|
@ -262,39 +211,13 @@ void main() {
|
|||
|
||||
testUsingContext('hot restart supported', () async {
|
||||
// Setup mocks
|
||||
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
const FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: kListViewsMethod,
|
||||
args: null,
|
||||
jsonResponse: <String, Object>{
|
||||
'views': <Object>[],
|
||||
}
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: 'getVM',
|
||||
args: null,
|
||||
jsonResponse: vm_service.VM.parse(<String, Object>{}).toJson()
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
id: '3',
|
||||
method: kListViewsMethod,
|
||||
args: null,
|
||||
jsonResponse: <String, Object>{
|
||||
'views': <Object>[],
|
||||
}
|
||||
),
|
||||
]);
|
||||
final MockDevice mockDevice = MockDevice();
|
||||
when(mockDevice.supportsHotReload).thenReturn(true);
|
||||
when(mockDevice.supportsHotRestart).thenReturn(true);
|
||||
when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester);
|
||||
// Trigger hot restart.
|
||||
final List<FlutterDevice> devices = <FlutterDevice>[
|
||||
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)
|
||||
..vmService = fakeVmServiceHost.vmService
|
||||
..devFS = mockDevFs,
|
||||
FlutterDevice(mockDevice, generator: residentCompiler, buildInfo: BuildInfo.debug)..devFS = mockDevFs,
|
||||
];
|
||||
final HotRunner hotRunner = HotRunner(devices);
|
||||
final OperationResult result = await hotRunner.restart(fullRestart: true);
|
||||
|
|
|
@ -31,53 +31,16 @@ import '../src/common.dart';
|
|||
import '../src/context.dart';
|
||||
import '../src/testbed.dart';
|
||||
|
||||
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,
|
||||
libraries: <vm_service.LibraryRef>[],
|
||||
livePorts: 0,
|
||||
name: 'test',
|
||||
number: '1',
|
||||
pauseOnExit: false,
|
||||
runnable: true,
|
||||
startTime: 0,
|
||||
);
|
||||
|
||||
final vm_service.Isolate fakePausedIsolate = vm_service.Isolate(
|
||||
id: '1',
|
||||
pauseEvent: vm_service.Event(
|
||||
kind: vm_service.EventKind.kPauseException,
|
||||
timestamp: 0
|
||||
),
|
||||
breakpoints: <vm_service.Breakpoint>[],
|
||||
exceptionPauseMode: null,
|
||||
libraries: <vm_service.LibraryRef>[],
|
||||
livePorts: 0,
|
||||
name: 'test',
|
||||
number: '1',
|
||||
pauseOnExit: false,
|
||||
runnable: true,
|
||||
startTime: 0,
|
||||
);
|
||||
|
||||
final FlutterView fakeFlutterView = FlutterView(
|
||||
id: 'a',
|
||||
uiIsolate: fakeUnpausedIsolate,
|
||||
);
|
||||
|
||||
void main() {
|
||||
final Uri testUri = Uri.parse('foo://bar');
|
||||
Testbed testbed;
|
||||
MockFlutterDevice mockFlutterDevice;
|
||||
MockVMService mockVMService;
|
||||
MockDevFS mockDevFS;
|
||||
MockFlutterView mockFlutterView;
|
||||
ResidentRunner residentRunner;
|
||||
MockDevice mockDevice;
|
||||
MockIsolate mockIsolate;
|
||||
FakeVmServiceHost fakeVmServiceHost;
|
||||
|
||||
setUp(() {
|
||||
|
@ -98,7 +61,8 @@ void main() {
|
|||
mockDevice = MockDevice();
|
||||
mockVMService = MockVMService();
|
||||
mockDevFS = MockDevFS();
|
||||
|
||||
mockFlutterView = MockFlutterView();
|
||||
mockIsolate = MockIsolate();
|
||||
// DevFS Mocks
|
||||
when(mockDevFS.lastCompiled).thenReturn(DateTime(2000));
|
||||
when(mockDevFS.sources).thenReturn(<Uri>[]);
|
||||
|
@ -128,10 +92,10 @@ void main() {
|
|||
});
|
||||
when(mockFlutterDevice.devFS).thenReturn(mockDevFS);
|
||||
when(mockFlutterDevice.views).thenReturn(<FlutterView>[
|
||||
// NB: still using mock FlutterDevice.
|
||||
fakeFlutterView,
|
||||
mockFlutterView,
|
||||
]);
|
||||
when(mockFlutterDevice.device).thenReturn(mockDevice);
|
||||
when(mockFlutterView.uiIsolate).thenReturn(mockIsolate);
|
||||
when(mockFlutterDevice.stopEchoingDeviceLog()).thenAnswer((Invocation invocation) async { });
|
||||
when(mockFlutterDevice.observatoryUris).thenAnswer((_) => Stream<Uri>.value(testUri));
|
||||
when(mockFlutterDevice.connect(
|
||||
|
@ -170,6 +134,9 @@ void main() {
|
|||
final Completer<void> result = Completer<void>.sync();
|
||||
return result.future;
|
||||
});
|
||||
when(mockIsolate.reload()).thenAnswer((Invocation invocation) {
|
||||
return Future<ServiceObject>.value(null);
|
||||
});
|
||||
});
|
||||
|
||||
test('ResidentRunner can attach to device successfully', () => testbed.run(() async {
|
||||
|
@ -193,32 +160,18 @@ void main() {
|
|||
|
||||
test('ResidentRunner can attach to device successfully with --fast-start', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: 'getIsolate',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
},
|
||||
jsonResponse: fakeUnpausedIsolate.toJson(),
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: 'getVM',
|
||||
args: null,
|
||||
jsonResponse: vm_service.VM.parse(<String, Object>{}).toJson(),
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
id: '3',
|
||||
id: '1',
|
||||
method: 'streamListen',
|
||||
args: <String, Object>{
|
||||
'streamId': 'Isolate',
|
||||
}
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '4',
|
||||
const FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: kRunInViewMethod,
|
||||
args: <String, Object>{
|
||||
'viewId': fakeFlutterView.id,
|
||||
'viewId': null,
|
||||
'mainScript': 'lib/main.dart.dill',
|
||||
'assetDirectory': 'build/flutter_assets',
|
||||
}
|
||||
|
@ -319,19 +272,11 @@ void main() {
|
|||
test('ResidentRunner can send target platform to analytics from hot reload', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
// Not all requests are present due to existing mocks
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: 'getIsolate',
|
||||
args: <String, Object>{
|
||||
'isolateId': '1',
|
||||
},
|
||||
jsonResponse: fakeUnpausedIsolate.toJson(),
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: 'ext.flutter.reassemble',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'isolateId': null,
|
||||
},
|
||||
),
|
||||
]);
|
||||
|
@ -366,32 +311,18 @@ 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: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: 'getIsolate',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
},
|
||||
jsonResponse: fakeUnpausedIsolate.toJson(),
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: 'getVM',
|
||||
args: null,
|
||||
jsonResponse: vm_service.VM.parse(<String, Object>{}).toJson(),
|
||||
),
|
||||
const FakeVmServiceRequest(
|
||||
id: '3',
|
||||
id: '1',
|
||||
method: 'streamListen',
|
||||
args: <String, Object>{
|
||||
'streamId': 'Isolate',
|
||||
},
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '4',
|
||||
const FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: kRunInViewMethod,
|
||||
args: <String, Object>{
|
||||
'viewId': fakeFlutterView.id,
|
||||
'viewId': null,
|
||||
'mainScript': 'lib/main.dart.dill',
|
||||
'assetDirectory': 'build/flutter_assets',
|
||||
},
|
||||
|
@ -568,11 +499,11 @@ void main() {
|
|||
|
||||
test('ResidentRunner handles writeSkSL returning no data', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: kGetSkSLsMethod,
|
||||
args: <String, Object>{
|
||||
'viewId': fakeFlutterView.id,
|
||||
'viewId': null,
|
||||
},
|
||||
jsonResponse: <String, Object>{
|
||||
'SkSLs': <String, Object>{}
|
||||
|
@ -586,11 +517,11 @@ void main() {
|
|||
|
||||
test('ResidentRunner can write SkSL data to a unique file with engine revision, platform, and device name', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: kGetSkSLsMethod,
|
||||
args: <String, Object>{
|
||||
'viewId': fakeFlutterView.id,
|
||||
'viewId': null,
|
||||
},
|
||||
jsonResponse: <String, Object>{
|
||||
'SkSLs': <String, Object>{
|
||||
|
@ -618,19 +549,19 @@ void main() {
|
|||
|
||||
test('ResidentRunner can take screenshot on debug device', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: 'ext.flutter.debugAllowBanner',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'isolateId': null,
|
||||
'enabled': 'false',
|
||||
},
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: 'ext.flutter.debugAllowBanner',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'isolateId': null,
|
||||
'enabled': 'true',
|
||||
},
|
||||
)
|
||||
|
@ -660,11 +591,11 @@ void main() {
|
|||
|
||||
test('ResidentRunner bails taking screenshot on debug device if debugAllowBanner throws RpcError', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: 'ext.flutter.debugAllowBanner',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'isolateId': null,
|
||||
'enabled': 'false',
|
||||
},
|
||||
// Failed response,
|
||||
|
@ -680,19 +611,19 @@ void main() {
|
|||
|
||||
test('ResidentRunner bails taking screenshot on debug device if debugAllowBanner during second request', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: 'ext.flutter.debugAllowBanner',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'isolateId': null,
|
||||
'enabled': 'false',
|
||||
},
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: 'ext.flutter.debugAllowBanner',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'isolateId': null,
|
||||
'enabled': 'true',
|
||||
},
|
||||
// Failed response,
|
||||
|
@ -707,19 +638,19 @@ void main() {
|
|||
|
||||
test('ResidentRunner bails taking screenshot on debug device if takeScreenshot throws', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: 'ext.flutter.debugAllowBanner',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'isolateId': null,
|
||||
'enabled': 'false',
|
||||
},
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: 'ext.flutter.debugAllowBanner',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'isolateId': null,
|
||||
'enabled': 'true',
|
||||
},
|
||||
),
|
||||
|
@ -763,31 +694,15 @@ void main() {
|
|||
}));
|
||||
|
||||
test('FlutterDevice will not exit a paused isolate', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: '_flutter.listViews',
|
||||
args: null,
|
||||
jsonResponse: <String, Object>{
|
||||
'views': <Object>[
|
||||
fakeFlutterView.toJson(),
|
||||
],
|
||||
},
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: 'getIsolate',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
},
|
||||
jsonResponse: fakePausedIsolate.toJson(),
|
||||
),
|
||||
]);
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
|
||||
final TestFlutterDevice flutterDevice = TestFlutterDevice(
|
||||
mockDevice,
|
||||
<FlutterView>[ fakeFlutterView ],
|
||||
<FlutterView>[ mockFlutterView ],
|
||||
);
|
||||
flutterDevice.vmService = fakeVmServiceHost.vmService;
|
||||
final MockServiceEvent mockServiceEvent = MockServiceEvent();
|
||||
when(mockServiceEvent.isPauseEvent).thenReturn(true);
|
||||
when(mockIsolate.pauseEvent).thenReturn(mockServiceEvent);
|
||||
when(mockDevice.supportsFlutterExit).thenReturn(true);
|
||||
|
||||
await flutterDevice.exitApps();
|
||||
|
@ -798,44 +713,26 @@ void main() {
|
|||
|
||||
test('FlutterDevice will exit an un-paused isolate', () => testbed.run(() async {
|
||||
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
|
||||
FakeVmServiceRequest(
|
||||
const FakeVmServiceRequest(
|
||||
id: '1',
|
||||
method: kListViewsMethod,
|
||||
args: null,
|
||||
jsonResponse: <String, Object>{
|
||||
'views': <Object>[
|
||||
fakeFlutterView.toJson(),
|
||||
],
|
||||
},
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '2',
|
||||
method: 'getIsolate',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id
|
||||
},
|
||||
jsonResponse: fakeUnpausedIsolate.toJson(),
|
||||
),
|
||||
FakeVmServiceRequest(
|
||||
id: '3',
|
||||
method: 'ext.flutter.exit',
|
||||
args: <String, Object>{
|
||||
'isolateId': fakeUnpausedIsolate.id,
|
||||
'isolateId': null,
|
||||
},
|
||||
close: true,
|
||||
)
|
||||
]);
|
||||
final TestFlutterDevice flutterDevice = TestFlutterDevice(
|
||||
mockDevice,
|
||||
<FlutterView> [fakeFlutterView ],
|
||||
<FlutterView> [mockFlutterView],
|
||||
);
|
||||
flutterDevice.vmService = fakeVmServiceHost.vmService;
|
||||
|
||||
final MockServiceEvent mockServiceEvent = MockServiceEvent();
|
||||
when(mockServiceEvent.isPauseEvent).thenReturn(false);
|
||||
when(mockIsolate.pauseEvent).thenReturn(mockServiceEvent);
|
||||
when(mockDevice.supportsFlutterExit).thenReturn(true);
|
||||
|
||||
final Future<void> exitFuture = flutterDevice.exitApps();
|
||||
|
||||
await expectLater(exitFuture, completes);
|
||||
await flutterDevice.exitApps();
|
||||
expect(fakeVmServiceHost.hasRemainingExpectations, false);
|
||||
}));
|
||||
|
||||
|
@ -1084,14 +981,17 @@ void main() {
|
|||
}
|
||||
|
||||
class MockFlutterDevice extends Mock implements FlutterDevice {}
|
||||
class MockFlutterView extends Mock implements FlutterView {}
|
||||
class MockVMService extends Mock implements VMService {}
|
||||
class MockDevFS extends Mock implements DevFS {}
|
||||
class MockIsolate extends Mock implements Isolate {}
|
||||
class MockDevice extends Mock implements Device {}
|
||||
class MockDeviceLogReader extends Mock implements DeviceLogReader {}
|
||||
class MockDevicePortForwarder extends Mock implements DevicePortForwarder {}
|
||||
class MockUsage extends Mock implements Usage {}
|
||||
class MockProcessManager extends Mock implements ProcessManager {}
|
||||
|
||||
class MockServiceEvent extends Mock implements ServiceEvent {}
|
||||
class MockVM extends Mock implements VM {}
|
||||
class TestFlutterDevice extends FlutterDevice {
|
||||
TestFlutterDevice(Device device, this.views, { Stream<Uri> observatoryUris })
|
||||
: super(device, buildInfo: BuildInfo.debug) {
|
||||
|
|
|
@ -91,6 +91,46 @@ final Map<String, Object> listViews = <String, dynamic>{
|
|||
typedef ServiceCallback = Future<Map<String, dynamic>> Function(Map<String, Object>);
|
||||
|
||||
void main() {
|
||||
testUsingContext('VMService can refreshViews', () async {
|
||||
final MockVMService mockVmService = MockVMService();
|
||||
final VMService vmService = VMService(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
mockVmService,
|
||||
Completer<void>(),
|
||||
const Stream<dynamic>.empty(),
|
||||
);
|
||||
|
||||
verify(mockVmService.registerService('flutterVersion', 'Flutter Tools')).called(1);
|
||||
|
||||
when(mockVmService.callServiceExtension('getVM',
|
||||
args: anyNamed('args'), // Empty
|
||||
isolateId: null
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return vm_service.Response.parse(vm);
|
||||
});
|
||||
await vmService.getVMOld();
|
||||
|
||||
|
||||
when(mockVmService.callServiceExtension('_flutter.listViews',
|
||||
args: anyNamed('args'),
|
||||
isolateId: anyNamed('isolateId')
|
||||
)).thenAnswer((Invocation invocation) async {
|
||||
return vm_service.Response.parse(listViews);
|
||||
});
|
||||
await vmService.refreshViews(waitForViews: true);
|
||||
|
||||
expect(vmService.vm.name, 'vm');
|
||||
expect(vmService.vm.views.single.id, '_flutterView/0x4a4c1f8');
|
||||
}, overrides: <Type, Generator>{
|
||||
Logger: () => BufferLogger.test()
|
||||
});
|
||||
|
||||
testUsingContext('VmService registers reloadSources', () {
|
||||
Future<void> reloadSources(String isolateId, { bool pause, bool force}) async {}
|
||||
final MockVMService mockVMService = MockVMService();
|
||||
|
|
|
@ -97,7 +97,6 @@ void main() {
|
|||
_project.scheduledBreakpointUri,
|
||||
_project.scheduledBreakpointLine,
|
||||
);
|
||||
await Future<void>.delayed(const Duration(seconds: 2));
|
||||
await _flutter.hotReload(); // reload triggers code which eventually hits the breakpoint
|
||||
isolate = await _flutter.waitForPause();
|
||||
expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
|
||||
|
|
|
@ -191,7 +191,7 @@ abstract class FlutterTestDriver {
|
|||
// ceases to be the case, this code will need changing.
|
||||
if (_flutterIsolateId == null) {
|
||||
final VM vm = await _vmService.getVM();
|
||||
_flutterIsolateId = vm.isolates.single.id;
|
||||
_flutterIsolateId = vm.isolates.first.id;
|
||||
}
|
||||
return _flutterIsolateId;
|
||||
}
|
||||
|
|
|
@ -242,11 +242,6 @@ class FakeVmServiceHost {
|
|||
.having((Map<String, Object> request) => request['id'], 'id', fakeRequest.id)
|
||||
.having((Map<String, Object> request) => request['params'], 'args', fakeRequest.args)
|
||||
);
|
||||
if (fakeRequest.close) {
|
||||
_vmService.dispose();
|
||||
expect(_requests, isEmpty);
|
||||
return;
|
||||
}
|
||||
if (fakeRequest.errorCode == null) {
|
||||
_input.add(json.encode(<String, Object>{
|
||||
'jsonrpc': '2.0',
|
||||
|
@ -303,15 +298,11 @@ class FakeVmServiceRequest implements VmServiceExpectation {
|
|||
@required this.args,
|
||||
this.jsonResponse,
|
||||
this.errorCode,
|
||||
this.close = false,
|
||||
});
|
||||
|
||||
final String method;
|
||||
final String id;
|
||||
|
||||
/// When true, the vm service is automatically closed.
|
||||
final bool close;
|
||||
|
||||
/// If non-null, the error code for a [vm_service.RPCError] in place of a
|
||||
/// standard response.
|
||||
final int errorCode;
|
||||
|
|
Loading…
Reference in a new issue