Unverified Commit 9202e547 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] move service extensions off of deprecated vm service (#55012)

parent 96a3b2ae
......@@ -34,6 +34,7 @@ import '../project.dart';
import '../reporting/reporting.dart';
import '../resident_runner.dart';
import '../run_hot.dart';
import '../vmservice.dart';
import '../web/chrome.dart';
import '../web/compile.dart';
import '../web/web_device.dart';
......@@ -195,9 +196,10 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugDumpApp() async {
try {
await _vmService?.callServiceExtension(
'ext.flutter.debugDumpApp',
);
await _vmService
?.flutterDebugDumpApp(
isolateId: null,
);
} on vmservice.RPCError {
return;
}
......@@ -206,9 +208,10 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugDumpRenderTree() async {
try {
await _vmService?.callServiceExtension(
'ext.flutter.debugDumpRenderTree',
);
await _vmService
?.flutterDebugDumpRenderTree(
isolateId: null,
);
} on vmservice.RPCError {
return;
}
......@@ -217,9 +220,10 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugDumpLayerTree() async {
try {
await _vmService?.callServiceExtension(
'ext.flutter.debugDumpLayerTree',
);
await _vmService
?.flutterDebugDumpLayerTree(
isolateId: null,
);
} on vmservice.RPCError {
return;
}
......@@ -228,8 +232,10 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugDumpSemanticsTreeInTraversalOrder() async {
try {
await _vmService?.callServiceExtension(
'ext.flutter.debugDumpSemanticsTreeInTraversalOrder');
await _vmService
?.flutterDebugDumpSemanticsTreeInTraversalOrder(
isolateId: null,
);
} on vmservice.RPCError {
return;
}
......@@ -238,14 +244,16 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugTogglePlatform() async {
try {
final vmservice.Response response = await _vmService
?.callServiceExtension('ext.flutter.platformOverride');
final String currentPlatform = response.json['value'] as String;
final String currentPlatform = await _vmService
?.flutterPlatformOverride(
isolateId: null,
);
final String platform = nextPlatform(currentPlatform, featureFlags);
await _vmService?.callServiceExtension('ext.flutter.platformOverride',
args: <String, Object>{
'value': platform,
});
await _vmService
?.flutterPlatformOverride(
platform: platform,
isolateId: null,
);
globals.printStatus('Switched operating system to $platform');
} on vmservice.RPCError {
return;
......@@ -261,8 +269,10 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugDumpSemanticsTreeInInverseHitTestOrder() async {
try {
await _vmService?.callServiceExtension(
'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder');
await _vmService
?.flutterDebugDumpSemanticsTreeInInverseHitTestOrder(
isolateId: null,
);
} on vmservice.RPCError {
return;
}
......@@ -271,16 +281,10 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugToggleDebugPaintSizeEnabled() async {
try {
final vmservice.Response response =
await _vmService?.callServiceExtension(
'ext.flutter.debugPaint',
);
await _vmService?.callServiceExtension(
'ext.flutter.debugPaint',
args: <dynamic, dynamic>{
'enabled': !(response.json['enabled'] == 'true')
},
);
await _vmService
?.flutterToggleDebugPaintSizeEnabled(
isolateId: null,
);
} on vmservice.RPCError {
return;
}
......@@ -289,16 +293,10 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugToggleDebugCheckElevationsEnabled() async {
try {
final vmservice.Response response =
await _vmService?.callServiceExtension(
'ext.flutter.debugCheckElevationsEnabled',
);
await _vmService?.callServiceExtension(
'ext.flutter.debugCheckElevationsEnabled',
args: <dynamic, dynamic>{
'enabled': !(response.json['enabled'] == 'true')
},
);
await _vmService
?.flutterToggleDebugCheckElevationsEnabled(
isolateId: null,
);
} on vmservice.RPCError {
return;
}
......@@ -307,14 +305,10 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugTogglePerformanceOverlayOverride() async {
try {
final vmservice.Response response = await _vmService
?.callServiceExtension('ext.flutter.showPerformanceOverlay');
await _vmService?.callServiceExtension(
'ext.flutter.showPerformanceOverlay',
args: <dynamic, dynamic>{
'enabled': !(response.json['enabled'] == 'true')
},
);
await _vmService
?.flutterTogglePerformanceOverlayOverride(
isolateId: null,
);
} on vmservice.RPCError {
return;
}
......@@ -323,14 +317,10 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugToggleWidgetInspector() async {
try {
final vmservice.Response response = await _vmService
?.callServiceExtension('ext.flutter.debugToggleWidgetInspector');
await _vmService?.callServiceExtension(
'ext.flutter.debugToggleWidgetInspector',
args: <dynamic, dynamic>{
'enabled': !(response.json['enabled'] == 'true')
},
);
await _vmService
?.flutterToggleWidgetInspector(
isolateId: null,
);
} on vmservice.RPCError {
return;
}
......@@ -339,14 +329,10 @@ abstract class ResidentWebRunner extends ResidentRunner {
@override
Future<void> debugToggleProfileWidgetBuilds() async {
try {
final vmservice.Response response = await _vmService
?.callServiceExtension('ext.flutter.profileWidgetBuilds');
await _vmService?.callServiceExtension(
'ext.flutter.profileWidgetBuilds',
args: <dynamic, dynamic>{
'enabled': !(response.json['enabled'] == 'true')
},
);
await _vmService
?.flutterToggleProfileWidgetBuilds(
isolateId: null,
);
} on vmservice.RPCError {
return;
}
......@@ -674,6 +660,14 @@ class _ResidentWebRunner extends ResidentWebRunner {
_connectionResult = await webDevFS.connect(useDebugExtension);
unawaited(_connectionResult.debugConnection.onDone.whenComplete(_cleanupAndExit));
_stdOutSub = _vmService.onStdoutEvent.listen((vmservice.Event log) {
final String message = utf8.decode(base64.decode(log.bytes));
globals.printStatus(message, newline: false);
});
_stdErrSub = _vmService.onStderrEvent.listen((vmservice.Event log) {
final String message = utf8.decode(base64.decode(log.bytes));
globals.printStatus(message, newline: false);
});
try {
await _vmService.streamListen(vmservice.EventStreams.kStdout);
} on vmservice.RPCError {
......@@ -692,14 +686,6 @@ class _ResidentWebRunner extends ResidentWebRunner {
// It is safe to ignore this error because we expect an error to be
// thrown if we're not already subscribed.
}
_stdOutSub = _vmService.onStdoutEvent.listen((vmservice.Event log) {
final String message = utf8.decode(base64.decode(log.bytes));
globals.printStatus(message, newline: false);
});
_stdErrSub = _vmService.onStderrEvent.listen((vmservice.Event log) {
final String message = utf8.decode(base64.decode(log.bytes));
globals.printStatus(message, newline: false);
});
unawaited(_vmService.registerService('reloadSources', 'FlutterTools'));
_vmService.registerServiceCallback('reloadSources', (Map<String, Object> params) async {
final bool pause = params['pause'] as bool ?? false;
......
......@@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import 'android/android_device_discovery.dart';
import 'android/android_workflow.dart';
......@@ -25,7 +26,6 @@ import 'linux/linux_device.dart';
import 'macos/macos_device.dart';
import 'project.dart';
import 'tester/flutter_tester.dart';
import 'vmservice.dart';
import 'web/web_device.dart';
import 'windows/windows_device.dart';
......@@ -755,7 +755,7 @@ abstract class DeviceLogReader {
/// Some logs can be obtained from a VM service stream.
/// Set this after the VM services are connected.
VMService connectedVMService;
vm_service.VmService connectedVMService;
@override
String toString() => name;
......@@ -785,7 +785,7 @@ class NoOpDeviceLogReader implements DeviceLogReader {
int appPid;
@override
VMService connectedVMService;
vm_service.VmService connectedVMService;
@override
Stream<String> get logLines => const Stream<String>.empty();
......
......@@ -24,7 +24,6 @@ import '../macos/xcode.dart';
import '../mdns_discovery.dart';
import '../project.dart';
import '../protocol_discovery.dart';
import '../vmservice.dart';
import 'fallback_discovery.dart';
import 'ios_deploy.dart';
import 'ios_workflow.dart';
......@@ -561,18 +560,18 @@ class IOSDeviceLogReader extends DeviceLogReader {
Stream<String> get logLines => _linesController.stream;
@override
VMService get connectedVMService => _connectedVMService;
VMService _connectedVMService;
vm_service.VmService get connectedVMService => _connectedVMService;
vm_service.VmService _connectedVMService;
@override
set connectedVMService(VMService connectedVmService) {
set connectedVMService(vm_service.VmService connectedVmService) {
_listenToUnifiedLoggingEvents(connectedVmService);
_connectedVMService = connectedVmService;
}
static const int _minimumUniversalLoggingSdkVersion = 13;
Future<void> _listenToUnifiedLoggingEvents(VMService connectedVmService) async {
Future<void> _listenToUnifiedLoggingEvents(vm_service.VmService connectedVmService) async {
if (_majorSdkVersion < _minimumUniversalLoggingSdkVersion) {
return;
}
......
......@@ -152,7 +152,7 @@ class FlutterDevice {
final ResidentCompiler generator;
final BuildInfo buildInfo;
Stream<Uri> observatoryUris;
VMService vmService;
vm_service.VmService vmService;
DevFS devFS;
ApplicationPackage package;
List<String> fileSystemRoots;
......@@ -226,24 +226,28 @@ class FlutterDevice {
return completer.future;
}
// TODO(jonahwilliams): remove once all callsites are updated.
VMService get flutterDeprecatedVmService => vmService as VMService;
Future<void> refreshViews() async {
if (vmService == null) {
return;
}
await vmService.vm.refreshViews(waitForViews: true);
await flutterDeprecatedVmService.vm.refreshViews(waitForViews: true);
}
List<FlutterView> get views {
if (vmService == null || vmService.isClosed) {
if (vmService == null || flutterDeprecatedVmService.isClosed) {
return <FlutterView>[];
}
return (viewFilter != null
? vmService.vm.allViewsWithName(viewFilter)
: vmService.vm.views).toList();
? flutterDeprecatedVmService.vm.allViewsWithName(viewFilter)
: flutterDeprecatedVmService.vm.views).toList();
}
Future<void> getVMs() => vmService.getVMOld();
Future<void> getVMs() => flutterDeprecatedVmService.getVMOld();
Future<void> exitApps() async {
if (!device.supportsFlutterExit) {
......@@ -270,7 +274,9 @@ class FlutterDevice {
for (final FlutterView view in flutterViews) {
if (view != null && view.uiIsolate != null) {
assert(!view.uiIsolate.pauseEvent.isPauseEvent);
futures.add(view.uiIsolate.flutterExit());
futures.add(vmService.flutterExit(
isolateId: view.uiIsolate.id,
));
}
}
// The flutterExit message only returns if it fails, so just wait a few
......@@ -286,7 +292,7 @@ class FlutterDevice {
}) {
// One devFS per device. Shared by all running instances.
devFS = DevFS(
vmService,
flutterDeprecatedVmService,
fsName,
rootDirectory,
osUtils: globals.os,
......@@ -301,7 +307,7 @@ class FlutterDevice {
final String deviceEntryUri = devFS.baseUri
.resolveUri(globals.fs.path.toUri(entryPath)).toString();
return <Future<vm_service.ReloadReport>>[
for (final Isolate isolate in vmService.vm.isolates)
for (final Isolate isolate in flutterDeprecatedVmService.vm.isolates)
vmService.reloadSources(
isolate.id,
pause: pause,
......@@ -325,68 +331,91 @@ class FlutterDevice {
Future<void> debugDumpApp() async {
for (final FlutterView view in views) {
await view.uiIsolate.flutterDebugDumpApp();
await vmService.flutterDebugDumpApp(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> debugDumpRenderTree() async {
for (final FlutterView view in views) {
await view.uiIsolate.flutterDebugDumpRenderTree();
await vmService.flutterDebugDumpRenderTree(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> debugDumpLayerTree() async {
for (final FlutterView view in views) {
await view.uiIsolate.flutterDebugDumpLayerTree();
await vmService.flutterDebugDumpLayerTree(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> debugDumpSemanticsTreeInTraversalOrder() async {
for (final FlutterView view in views) {
await view.uiIsolate.flutterDebugDumpSemanticsTreeInTraversalOrder();
await vmService.flutterDebugDumpSemanticsTreeInTraversalOrder(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> debugDumpSemanticsTreeInInverseHitTestOrder() async {
for (final FlutterView view in views) {
await view.uiIsolate.flutterDebugDumpSemanticsTreeInInverseHitTestOrder();
await vmService.flutterDebugDumpSemanticsTreeInInverseHitTestOrder(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> toggleDebugPaintSizeEnabled() async {
for (final FlutterView view in views) {
await view.uiIsolate.flutterToggleDebugPaintSizeEnabled();
await vmService.flutterToggleDebugPaintSizeEnabled(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> toggleDebugCheckElevationsEnabled() async {
for (final FlutterView view in views) {
await view.uiIsolate.flutterToggleDebugCheckElevationsEnabled();
await vmService.flutterToggleDebugCheckElevationsEnabled(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> debugTogglePerformanceOverlayOverride() async {
for (final FlutterView view in views) {
await view.uiIsolate.flutterTogglePerformanceOverlayOverride();
await vmService.flutterTogglePerformanceOverlayOverride(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> toggleWidgetInspector() async {
for (final FlutterView view in views) {
await view.uiIsolate.flutterToggleWidgetInspector();
await vmService.flutterToggleWidgetInspector(
isolateId: view.uiIsolate.id,
);
}
}
Future<void> toggleProfileWidgetBuilds() async {
for (final FlutterView view in views) {
await view.uiIsolate.flutterToggleProfileWidgetBuilds();
await vmService.flutterToggleProfileWidgetBuilds(
isolateId: view.uiIsolate.id,
);
}
}
Future<String> togglePlatform({ String from }) async {
final String to = nextPlatform(from, featureFlags);
for (final FlutterView view in views) {
await view.uiIsolate.flutterPlatformOverride(to);
await vmService.flutterPlatformOverride(
platform: to,
isolateId: view.uiIsolate.id,
);
}
return to;
}
......@@ -416,7 +445,9 @@ class FlutterDevice {
}
Future<void> initLogReader() async {
(await device.getLogReader(app: package)).appPid = vmService.vm.pid;
final vm_service.VM vm = await vmService.getVM();
final DeviceLogReader logReader = await device.getLogReader(app: package);
logReader.appPid = vm.pid;
}
Future<int> runHot({
......@@ -721,8 +752,16 @@ abstract class ResidentRunner {
String method, {
Map<String, dynamic> params,
}) {
return flutterDevices.first.views.first.uiIsolate
.invokeFlutterExtensionRpcRaw(method, params: params);
return flutterDevices
.first
.vmService
.invokeFlutterExtensionRpcRaw(
method,
args: params,
isolateId: flutterDevices
.first.views
.first.uiIsolate.id
);
}
/// Whether this runner can hot reload.
......@@ -812,7 +851,7 @@ abstract class ResidentRunner {
void writeVmserviceFile() {
if (debuggingOptions.vmserviceOutFile != null) {
try {
final String address = flutterDevices.first.vmService.wsAddress.toString();
final String address = flutterDevices.first.flutterDeprecatedVmService.wsAddress.toString();
final File vmserviceOutFile = globals.fs.file(debuggingOptions.vmserviceOutFile);
vmserviceOutFile.createSync(recursive: true);
vmserviceOutFile.writeAsStringSync(address);
......@@ -944,7 +983,10 @@ abstract class ResidentRunner {
await device.refreshViews();
try {
for (final FlutterView view in device.views) {
await view.uiIsolate.flutterDebugAllowBanner(false);
await device.vmService.flutterDebugAllowBanner(
false,
isolateId: view.uiIsolate.id,
);
}
} on Exception catch (error) {
status.cancel();
......@@ -958,7 +1000,10 @@ abstract class ResidentRunner {
if (supportsServiceProtocol && isRunningDebug) {
try {
for (final FlutterView view in device.views) {
await view.uiIsolate.flutterDebugAllowBanner(true);
await device.vmService.flutterDebugAllowBanner(
true,
isolateId: view.uiIsolate.id,
);
}
} on Exception catch (error) {
status.cancel();
......@@ -980,7 +1025,12 @@ abstract class ResidentRunner {
Future<void> debugTogglePlatform() async {
await refreshViews();
final String from = await flutterDevices[0].views[0].uiIsolate.flutterPlatformOverride();
final String isolateId = flutterDevices
.first.views.first.uiIsolate.id;
final String from = await flutterDevices
.first.vmService.flutterPlatformOverride(
isolateId: isolateId,
);
String to;
for (final FlutterDevice device in flutterDevices) {
to = await device.togglePlatform(from: from);
......@@ -1039,7 +1089,7 @@ abstract class ResidentRunner {
// This hooks up callbacks for when the connection stops in the future.
// We don't want to wait for them. We don't handle errors in those callbacks'
// futures either because they just print to logger and is not critical.
unawaited(device.vmService.done.then<void>(
unawaited(device.vmService.onDone.then<void>(
_serviceProtocolDone,
onError: _serviceProtocolError,
).whenComplete(_serviceDisconnected));
......@@ -1056,7 +1106,7 @@ abstract class ResidentRunner {
<String, dynamic>{
'reuseWindows': true,
},
flutterDevices.first.vmService.httpAddress,
flutterDevices.first.flutterDeprecatedVmService.httpAddress,
'http://${_devtoolsServer.address.host}:${_devtoolsServer.port}',
false, // headless mode,
false, // machine mode
......
......@@ -83,8 +83,8 @@ class ColdRunner extends ResidentRunner {
if (flutterDevices.first.observatoryUris != null) {
// For now, only support one debugger connection.
connectionInfoCompleter?.complete(DebugConnectionInfo(
httpUri: flutterDevices.first.vmService.httpAddress,
wsUri: flutterDevices.first.vmService.wsAddress,
httpUri: flutterDevices.first.flutterDeprecatedVmService.httpAddress,
wsUri: flutterDevices.first.flutterDeprecatedVmService.wsAddress,
));
}
......@@ -105,7 +105,7 @@ class ColdRunner extends ResidentRunner {
if (device.vmService != null) {
globals.printStatus('Tracing startup on ${device.device.name}.');
await downloadStartupTrace(
device.vmService,
device.flutterDeprecatedVmService,
awaitFirstFrame: awaitFirstFrameWhenTracing,
);
}
......@@ -197,7 +197,7 @@ class ColdRunner extends ResidentRunner {
// Caution: This log line is parsed by device lab tests.
globals.printStatus(
'An Observatory debugger and profiler on $dname is available at: '
'${device.vmService.httpAddress}',
'${device.flutterDeprecatedVmService.httpAddress}',
);
}
}
......
......@@ -205,7 +205,10 @@ class HotRunner extends ResidentRunner {
for (final FlutterDevice device in flutterDevices) {
for (final FlutterView view in device.views) {
await view.uiIsolate.flutterFastReassemble(classId);
await device.vmService.flutterFastReassemble(
classId,
isolateId: view.uiIsolate.id,
);
}
}
......@@ -260,8 +263,8 @@ class HotRunner extends ResidentRunner {
// Only handle one debugger connection.
connectionInfoCompleter.complete(
DebugConnectionInfo(
httpUri: flutterDevices.first.vmService.httpAddress,
wsUri: flutterDevices.first.vmService.wsAddress,
httpUri: flutterDevices.first.flutterDeprecatedVmService.httpAddress,
wsUri: flutterDevices.first.flutterDeprecatedVmService.wsAddress,
baseUri: baseUris.first.toString(),
),
);
......@@ -570,7 +573,7 @@ class HotRunner extends ResidentRunner {
// 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.
for (final Isolate isolate in device?.vmService?.vm?.isolates ?? <Isolate>[]) {
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,
......@@ -938,10 +941,16 @@ class HotRunner extends ResidentRunner {
}
await Future.wait(allDevices);
// Check if any isolates are paused.
globals.printTrace('Evicting dirty assets');
await _evictDirtyAssets();
// Check if any isolates are paused and reassemble those
// that aren't.
final List<FlutterView> reassembleViews = <FlutterView>[];
final List<Future<void>> reassembleFutures = <Future<void>>[];
String serviceEventKind;
int pausedIsolatesFound = 0;
bool failedReassemble = false;
for (final FlutterDevice device in flutterDevices) {
for (final FlutterView view in device.views) {
// Check if the isolate is paused, and if so, don't reassemble. Ignore the
......@@ -956,6 +965,12 @@ class HotRunner extends ResidentRunner {
}
} else {
reassembleViews.add(view);
reassembleFutures.add(device.vmService.flutterReassemble(
isolateId: view.uiIsolate.id,
).catchError((dynamic error) {
failedReassemble = true;
globals.printError('Reassembling ${view.uiIsolate.name} failed: $error');
}, test: (dynamic error) => error is Exception));
}
}
}
......@@ -968,24 +983,10 @@ class HotRunner extends ResidentRunner {
return OperationResult(OperationResult.ok.code, reloadMessage);
}
}
globals.printTrace('Evicting dirty assets');
await _evictDirtyAssets();
assert(reassembleViews.isNotEmpty);
globals.printTrace('Reassembling application');
bool failedReassemble = false;
final List<Future<void>> futures = <Future<void>>[
for (final FlutterView view in reassembleViews)
() async {
try {
await view.uiIsolate.flutterReassemble();
} on Exception catch (error) {
failedReassemble = true;
globals.printError('Reassembling ${view.uiIsolate.name} failed: $error');
return;
}
}(),
];
final Future<void> reassembleFuture = Future.wait<void>(futures);
final Future<void> reassembleFuture = Future.wait<void>(reassembleFutures);
await reassembleFuture.timeout(
const Duration(seconds: 2),
onTimeout: () async {
......@@ -1128,7 +1129,7 @@ class HotRunner extends ResidentRunner {
// Caution: This log line is parsed by device lab tests.
globals.printStatus(
'An Observatory debugger and profiler on $dname is available at: '
'${device.vmService.httpAddress}',
'${device.flutterDeprecatedVmService.httpAddress}',
);
}
}
......@@ -1144,7 +1145,13 @@ class HotRunner extends ResidentRunner {
continue;
}
for (final String assetPath in device.devFS.assetPathsToEvict) {
futures.add(device.views.first.uiIsolate.flutterEvictAsset(assetPath));
futures.add(
device.views.first.uiIsolate.vmService
.flutterEvictAsset(
assetPath,
isolateId: device.views.first.uiIsolate.id,
)
);
}
device.devFS.assetPathsToEvict.clear();
}
......
......@@ -54,7 +54,10 @@ class Tracing {
});
bool done = false;
for (final FlutterView view in vmService.vm.views) {
if (await view.uiIsolate.flutterAlreadyPaintedFirstUsefulFrame()) {
if (await view.uiIsolate.vmService
.flutterAlreadyPaintedFirstUsefulFrame(
isolateId: view.uiIsolate.id,
)) {
done = true;
break;
}
......
......@@ -5,12 +5,17 @@
import 'dart:async';
import 'package:file/memory.dart';
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import 'package:quiver/testing/async.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/net.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/attach.dart';
......@@ -22,10 +27,6 @@ import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/resident_runner.dart';
import 'package:flutter_tools/src/run_hot.dart';
import 'package:flutter_tools/src/vmservice.dart';
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import 'package:quiver/testing/async.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import '../../src/common.dart';
......@@ -142,7 +143,7 @@ void main() {
final Process dartProcess = MockProcess();
final StreamController<List<int>> compilerStdoutController = StreamController<List<int>>();
when(dartProcess.stdout).thenAnswer((_) => compilerStdoutController.stream);
when(dartProcess.stdout).thenAnswer((_) => compilerStdoutController.stream);
when(dartProcess.stderr)
.thenAnswer((_) => Stream<List<int>>.fromFuture(Future<List<int>>.value(const <int>[])));
......@@ -787,6 +788,23 @@ VMServiceConnector getFakeVmServiceFactory({
when(vmService.done).thenAnswer((_) {
return Future<void>.value(null);
});
when(vmService.onDone).thenAnswer((_) {
return Future<void>.value(null);
});
when(vmService.getVM()).thenAnswer((_) async {
return vm_service.VM(
pid: 1,
architectureBits: 64,
hostCPU: '',
name: '',
isolates: <vm_service.IsolateRef>[],
isolateGroups: <vm_service.IsolateGroupRef>[],
startTime: 0,
targetCPU: '',
operatingSystem: '',
version: '',
);
});
when(vm.refreshViews(waitForViews: anyNamed('waitForViews')))
.thenAnswer((_) => Future<void>.value(null));
......
......@@ -6,7 +6,6 @@ import 'dart:async';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/convert.dart';
import 'package:meta/meta.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import 'package:mockito/mockito.dart';
import 'package:flutter_tools/src/base/logger.dart';
......@@ -285,10 +284,10 @@ void main() {
testWithoutContext('runInView forwards arguments correctly', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
const FakeVmServiceRequest(method: 'streamListen', id: '1', params: <String, Object>{
const FakeVmServiceRequest(method: 'streamListen', id: '1', args: <String, Object>{
'streamId': 'Isolate'
}),
const FakeVmServiceRequest(method: kRunInViewMethod, id: '2', params: <String, Object>{
const FakeVmServiceRequest(method: kRunInViewMethod, id: '2', args: <String, Object>{
'viewId': '1234',
'mainScript': 'main.dart',
'assetDirectory': 'flutter_assets/',
......@@ -312,95 +311,6 @@ void main() {
});
}
class FakeVmServiceHost {
FakeVmServiceHost({
@required List<VmServiceExpectation> requests,
}) : _requests = requests {
_vmService = vm_service.VmService(
_input.stream,
_output.add,
);
_applyStreamListen();
_output.stream.listen((String data) {
final Map<String, Object> request = json.decode(data) as Map<String, Object>;
if (_requests.isEmpty) {
throw Exception('Unexpected request: $request');
}
final FakeVmServiceRequest fakeRequest = _requests.removeAt(0) as FakeVmServiceRequest;
expect(fakeRequest, isA<FakeVmServiceRequest>()
.having((FakeVmServiceRequest request) => request.method, 'method', request['method'])
.having((FakeVmServiceRequest request) => request.id, 'id', request['id'])
.having((FakeVmServiceRequest request) => request.params, 'params', request['params'])
);
_input.add(json.encode(<String, Object>{
'jsonrpc': '2.0',
'id': fakeRequest.id,
'result': fakeRequest.jsonResponse ?? <String, Object>{'type': 'Success'},
}));
_applyStreamListen();
});
}
final List<VmServiceExpectation> _requests;
final StreamController<String> _input = StreamController<String>();
final StreamController<String> _output = StreamController<String>();
vm_service.VmService get vmService => _vmService;
vm_service.VmService _vmService;
bool get hasRemainingExpectations => _requests.isNotEmpty;
// remove FakeStreamResponse objects from _requests until it is empty
// or until we hit a FakeRequest
void _applyStreamListen() {
while (_requests.isNotEmpty && !_requests.first.isRequest) {
final FakeVmServiceStreamResponse response = _requests.removeAt(0) as FakeVmServiceStreamResponse;
_input.add(json.encode(<String, Object>{
'jsonrpc': '2.0',
'method': 'streamNotify',
'params': <String, Object>{
'streamId': response.streamId,
'event': response.event.toJson(),
},
}));
}
}
}
abstract class VmServiceExpectation {
bool get isRequest;
}
class FakeVmServiceRequest implements VmServiceExpectation {
const FakeVmServiceRequest({
@required this.method,
@required this.id,
@required this.params,
this.jsonResponse,
});
final String method;
final String id;
final Map<String, Object> params;
final Map<String, Object> jsonResponse;
@override
bool get isRequest => true;
}
class FakeVmServiceStreamResponse implements VmServiceExpectation {
const FakeVmServiceStreamResponse({
@required this.event,
@required this.streamId,
});
final vm_service.Event event;
final String streamId;
@override
bool get isRequest => false;
}
class MockDevice extends Mock implements Device {}
class MockVMService extends Mock implements vm_service.VmService {}
class MockFlutterVersion extends Mock implements FlutterVersion {
......
......@@ -5,10 +5,12 @@
import 'dart:async';
import 'package:args/command_runner.dart';
import 'package:flutter_tools/src/convert.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/commands/create.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
......@@ -217,3 +219,109 @@ class NoContext implements AppContext {
return body();
}
}
/// A fake implementation of a vm_service that mocks the JSON-RPC request
/// and response structure.
class FakeVmServiceHost {
FakeVmServiceHost({
@required List<VmServiceExpectation> requests,
}) : _requests = requests {
_vmService = vm_service.VmService(
_input.stream,
_output.add,
);
_applyStreamListen();
_output.stream.listen((String data) {
final Map<String, Object> request = json.decode(data) as Map<String, Object>;
if (_requests.isEmpty) {
throw Exception('Unexpected request: $request');
}
final FakeVmServiceRequest fakeRequest = _requests.removeAt(0) as FakeVmServiceRequest;
expect(request, isA<Map<String, Object>>()
.having((Map<String, Object> request) => request['method'], 'method', fakeRequest.method)
.having((Map<String, Object> request) => request['id'], 'id', fakeRequest.id)
.having((Map<String, Object> request) => request['params'], 'args', fakeRequest.args)
);
if (fakeRequest.errorCode == null) {
_input.add(json.encode(<String, Object>{
'jsonrpc': '2.0',
'id': fakeRequest.id,
'result': fakeRequest.jsonResponse ?? <String, Object>{'type': 'Success'},
}));
} else {
_input.add(json.encode(<String, Object>{
'jsonrpc': '2.0',
'id': fakeRequest.id,
'error': <String, Object>{
'code': fakeRequest.errorCode,
}
}));
}
_applyStreamListen();
});
}
final List<VmServiceExpectation> _requests;
final StreamController<String> _input = StreamController<String>();
final StreamController<String> _output = StreamController<String>();
vm_service.VmService get vmService => _vmService;
vm_service.VmService _vmService;
bool get hasRemainingExpectations => _requests.isNotEmpty;
// remove FakeStreamResponse objects from _requests until it is empty
// or until we hit a FakeRequest
void _applyStreamListen() {
while (_requests.isNotEmpty && !_requests.first.isRequest) {
final FakeVmServiceStreamResponse response = _requests.removeAt(0) as FakeVmServiceStreamResponse;
_input.add(json.encode(<String, Object>{
'jsonrpc': '2.0',
'method': 'streamNotify',
'params': <String, Object>{
'streamId': response.streamId,
'event': response.event.toJson(),
},
}));
}
}
}
abstract class VmServiceExpectation {
bool get isRequest;
}
class FakeVmServiceRequest implements VmServiceExpectation {
const FakeVmServiceRequest({
@required this.method,
@required this.id,
@required this.args,
this.jsonResponse,
this.errorCode,
});
final String method;
final String id;
/// If non-null, the error code for a [vm_service.RPCError] in place of a
/// standard response.
final int errorCode;
final Map<String, Object> args;
final Map<String, Object> jsonResponse;
@override
bool get isRequest => true;
}
class FakeVmServiceStreamResponse implements VmServiceExpectation {
const FakeVmServiceStreamResponse({
@required this.event,
@required this.streamId,
});
final vm_service.Event event;
final String streamId;
@override
bool get isRequest => false;
}
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