[flutter_tools] move service extensions off of deprecated vm service (#55012)
diff --git a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
index e856412..26f1dbd 100644
--- a/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
+++ b/packages/flutter_tools/lib/src/build_runner/resident_web_runner.dart
@@ -34,6 +34,7 @@
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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
_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 @@
// 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;
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 3286a17..976e941 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -6,6 +6,7 @@
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 '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 @@
/// 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 @@
int appPid;
@override
- VMService connectedVMService;
+ vm_service.VmService connectedVMService;
@override
Stream<String> get logLines => const Stream<String>.empty();
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart
index 4a35480..0bfd341 100644
--- a/packages/flutter_tools/lib/src/ios/devices.dart
+++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -24,7 +24,6 @@
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 @@
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;
}
diff --git a/packages/flutter_tools/lib/src/resident_runner.dart b/packages/flutter_tools/lib/src/resident_runner.dart
index 816e488..0650e27 100644
--- a/packages/flutter_tools/lib/src/resident_runner.dart
+++ b/packages/flutter_tools/lib/src/resident_runner.dart
@@ -152,7 +152,7 @@
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 @@
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 @@
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 @@
}) {
// One devFS per device. Shared by all running instances.
devFS = DevFS(
- vmService,
+ flutterDeprecatedVmService,
fsName,
rootDirectory,
osUtils: globals.os,
@@ -301,7 +307,7 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
<String, dynamic>{
'reuseWindows': true,
},
- flutterDevices.first.vmService.httpAddress,
+ flutterDevices.first.flutterDeprecatedVmService.httpAddress,
'http://${_devtoolsServer.address.host}:${_devtoolsServer.port}',
false, // headless mode,
false, // machine mode
diff --git a/packages/flutter_tools/lib/src/run_cold.dart b/packages/flutter_tools/lib/src/run_cold.dart
index 1779426..1b84279 100644
--- a/packages/flutter_tools/lib/src/run_cold.dart
+++ b/packages/flutter_tools/lib/src/run_cold.dart
@@ -83,8 +83,8 @@
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 @@
if (device.vmService != null) {
globals.printStatus('Tracing startup on ${device.device.name}.');
await downloadStartupTrace(
- device.vmService,
+ device.flutterDeprecatedVmService,
awaitFirstFrame: awaitFirstFrameWhenTracing,
);
}
@@ -197,7 +197,7 @@
// 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}',
);
}
}
diff --git a/packages/flutter_tools/lib/src/run_hot.dart b/packages/flutter_tools/lib/src/run_hot.dart
index 55094f4..9b11efe 100644
--- a/packages/flutter_tools/lib/src/run_hot.dart
+++ b/packages/flutter_tools/lib/src/run_hot.dart
@@ -205,7 +205,10 @@
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 @@
// 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 @@
// 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 @@
}
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 @@
}
} 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 @@
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 @@
// 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 @@
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();
}
diff --git a/packages/flutter_tools/lib/src/tracing.dart b/packages/flutter_tools/lib/src/tracing.dart
index 5ea6cfb..aa41645 100644
--- a/packages/flutter_tools/lib/src/tracing.dart
+++ b/packages/flutter_tools/lib/src/tracing.dart
@@ -54,7 +54,10 @@
});
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;
}
diff --git a/packages/flutter_tools/lib/src/vmservice.dart b/packages/flutter_tools/lib/src/vmservice.dart
index 3de2238..75b6f92 100644
--- a/packages/flutter_tools/lib/src/vmservice.dart
+++ b/packages/flutter_tools/lib/src/vmservice.dart
@@ -3,7 +3,6 @@
// found in the LICENSE file.
import 'dart:async';
-import 'dart:math' as math;
import 'package:meta/meta.dart' show required;
import 'package:vm_service/vm_service.dart' as vm_service;
@@ -476,6 +475,18 @@
return _delegateService.callMethod(method, isolateId: isolateId, args: args);
}
+ @override
+ Future<void> get onDone => _delegateService.onDone;
+
+ @override
+ Future<vm_service.Response> callServiceExtension(String method,
+ {String isolateId, Map<Object, Object> args}) {
+ return _delegateService.callServiceExtension(method, isolateId: isolateId, args: args);
+ }
+
+ @override
+ Future<vm_service.VM> getVM() => _delegateService.getVM();
+
StreamController<ServiceEvent> _getEventController(String eventName) {
StreamController<ServiceEvent> controller = _eventControllers[eventName];
if (controller == null) {
@@ -890,7 +901,6 @@
_upgradeCollection(map, this);
_loaded = true;
- _pid = map['pid'] as int;
if (map['_heapAllocatedMemoryUsage'] != null) {
_heapAllocatedMemoryUsage = map['_heapAllocatedMemoryUsage'] as int;
}
@@ -910,10 +920,6 @@
/// The set of live views.
final Map<String, FlutterView> _viewCache = <String, FlutterView>{};
- /// The pid of the VM's process.
- int _pid;
- int get pid => _pid;
-
/// The number of bytes allocated (e.g. by malloc) in the native heap.
int _heapAllocatedMemoryUsage;
int get heapAllocatedMemoryUsage => _heapAllocatedMemoryUsage ?? 0;
@@ -1131,44 +1137,6 @@
}
}
-class HeapSpace extends ServiceObject {
- HeapSpace._empty(ServiceObjectOwner owner) : super._empty(owner);
-
- int _used = 0;
- int _capacity = 0;
- int _external = 0;
- int _collections = 0;
- double _totalCollectionTimeInSeconds = 0.0;
- double _averageCollectionPeriodInMillis = 0.0;
-
- int get used => _used;
- int get capacity => _capacity;
- int get external => _external;
-
- Duration get avgCollectionTime {
- final double mcs = _totalCollectionTimeInSeconds *
- Duration.microsecondsPerSecond /
- math.max(_collections, 1);
- return Duration(microseconds: mcs.ceil());
- }
-
- Duration get avgCollectionPeriod {
- final double mcs = _averageCollectionPeriodInMillis *
- Duration.microsecondsPerMillisecond;
- return Duration(microseconds: mcs.ceil());
- }
-
- @override
- void _update(Map<String, dynamic> map, bool mapIsRef) {
- _used = map['used'] as int;
- _capacity = map['capacity'] as int;
- _external = map['external'] as int;
- _collections = map['collections'] as int;
- _totalCollectionTimeInSeconds = map['time'] as double;
- _averageCollectionPeriodInMillis = map['avgCollectionPeriodMillis'] as double;
- }
-}
-
/// An isolate running inside the VM. Instances of the Isolate class are always
/// canonicalized.
class Isolate extends ServiceObjectOwner {
@@ -1250,118 +1218,6 @@
pauseEvent = map['pauseEvent'] as ServiceEvent;
}
- // Flutter extension methods.
-
- // Invoke a flutter extension method, if the flutter extension is not
- // available, returns null.
- Future<Map<String, dynamic>> invokeFlutterExtensionRpcRaw(
- String method, {
- Map<String, dynamic> params,
- }) async {
- try {
- return await invokeRpcRaw(method, params: params);
- } on vm_service.RPCError catch (err) {
- // If an application is not using the framework
- if (err.code == RPCErrorCodes.kMethodNotFound) {
- return null;
- }
- rethrow;
- }
- }
-
- Future<Map<String, dynamic>> flutterDebugDumpApp() {
- return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpApp');
- }
-
- Future<Map<String, dynamic>> flutterDebugDumpRenderTree() {
- return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpRenderTree');
- }
-
- Future<Map<String, dynamic>> flutterDebugDumpLayerTree() {
- return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpLayerTree');
- }
-
- Future<Map<String, dynamic>> flutterDebugDumpSemanticsTreeInTraversalOrder() {
- return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpSemanticsTreeInTraversalOrder');
- }
-
- Future<Map<String, dynamic>> flutterDebugDumpSemanticsTreeInInverseHitTestOrder() {
- return invokeFlutterExtensionRpcRaw('ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder');
- }
-
- Future<Map<String, dynamic>> _flutterToggle(String name) async {
- Map<String, dynamic> state = await invokeFlutterExtensionRpcRaw('ext.flutter.$name');
- if (state != null && state.containsKey('enabled') && state['enabled'] is String) {
- state = await invokeFlutterExtensionRpcRaw(
- 'ext.flutter.$name',
- params: <String, dynamic>{'enabled': state['enabled'] == 'true' ? 'false' : 'true'},
- );
- }
- return state;
- }
-
- Future<Map<String, dynamic>> flutterToggleDebugPaintSizeEnabled() => _flutterToggle('debugPaint');
-
- Future<Map<String, dynamic>> flutterToggleDebugCheckElevationsEnabled() => _flutterToggle('debugCheckElevationsEnabled');
-
- Future<Map<String, dynamic>> flutterTogglePerformanceOverlayOverride() => _flutterToggle('showPerformanceOverlay');
-
- Future<Map<String, dynamic>> flutterToggleWidgetInspector() => _flutterToggle('inspector.show');
-
- Future<Map<String, dynamic>> flutterToggleProfileWidgetBuilds() => _flutterToggle('profileWidgetBuilds');
-
- Future<Map<String, dynamic>> flutterDebugAllowBanner(bool show) {
- return invokeFlutterExtensionRpcRaw(
- 'ext.flutter.debugAllowBanner',
- params: <String, dynamic>{'enabled': show ? 'true' : 'false'},
- );
- }
-
- Future<Map<String, dynamic>> flutterReassemble() {
- return invokeFlutterExtensionRpcRaw('ext.flutter.reassemble');
- }
-
- Future<Map<String, dynamic>> flutterFastReassemble(String classId) {
- return invokeFlutterExtensionRpcRaw('ext.flutter.fastReassemble', params: <String, Object>{
- 'class': classId,
- });
- }
-
- Future<bool> flutterAlreadyPaintedFirstUsefulFrame() async {
- final Map<String, dynamic> result = await invokeFlutterExtensionRpcRaw('ext.flutter.didSendFirstFrameRasterizedEvent');
- // result might be null when the service extension is not initialized
- return result != null && result['enabled'] == 'true';
- }
-
- Future<Map<String, dynamic>> uiWindowScheduleFrame() {
- return invokeFlutterExtensionRpcRaw('ext.ui.window.scheduleFrame');
- }
-
- Future<Map<String, dynamic>> flutterEvictAsset(String assetPath) {
- return invokeFlutterExtensionRpcRaw(
- 'ext.flutter.evict',
- params: <String, dynamic>{
- 'value': assetPath,
- },
- );
- }
-
- // Application control extension methods.
- Future<Map<String, dynamic>> flutterExit() {
- return invokeFlutterExtensionRpcRaw('ext.flutter.exit');
- }
-
- Future<String> flutterPlatformOverride([ String platform ]) async {
- final Map<String, dynamic> result = await invokeFlutterExtensionRpcRaw(
- 'ext.flutter.platformOverride',
- params: platform != null ? <String, dynamic>{'value': platform} : <String, String>{},
- );
- if (result != null && result['value'] is String) {
- return result['value'] as String;
- }
- return 'unknown';
- }
-
@override
String toString() => 'Isolate $id';
}
@@ -1523,4 +1379,213 @@
);
await onRunnable;
}
+
+ Future<Map<String, dynamic>> flutterDebugDumpApp({
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.debugDumpApp',
+ isolateId: isolateId,
+ );
+ }
+
+ Future<Map<String, dynamic>> flutterDebugDumpRenderTree({
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.debugDumpRenderTree',
+ isolateId: isolateId,
+ );
+ }
+
+ Future<Map<String, dynamic>> flutterDebugDumpLayerTree({
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.debugDumpLayerTree',
+ isolateId: isolateId,
+ );
+ }
+
+ Future<Map<String, dynamic>> flutterDebugDumpSemanticsTreeInTraversalOrder({
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder',
+ isolateId: isolateId,
+ );
+ }
+
+ Future<Map<String, dynamic>> flutterDebugDumpSemanticsTreeInInverseHitTestOrder({
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder',
+ isolateId: isolateId,
+ );
+ }
+
+ Future<Map<String, dynamic>> _flutterToggle(String name, {
+ @required String isolateId,
+ }) async {
+ Map<String, dynamic> state = await invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.$name',
+ isolateId: isolateId,
+ );
+ if (state != null && state.containsKey('enabled') && state['enabled'] is String) {
+ state = await invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.$name',
+ isolateId: isolateId,
+ args: <String, dynamic>{
+ 'enabled': state['enabled'] == 'true' ? 'false' : 'true',
+ },
+ );
+ }
+
+ return state;
+ }
+
+ Future<Map<String, dynamic>> flutterToggleDebugPaintSizeEnabled({
+ @required String isolateId,
+ }) => _flutterToggle('debugPaint', isolateId: isolateId);
+
+ Future<Map<String, dynamic>> flutterToggleDebugCheckElevationsEnabled({
+ @required String isolateId,
+ }) => _flutterToggle('debugCheckElevationsEnabled', isolateId: isolateId);
+
+ Future<Map<String, dynamic>> flutterTogglePerformanceOverlayOverride({
+ @required String isolateId,
+ }) => _flutterToggle('showPerformanceOverlay', isolateId: isolateId);
+
+ Future<Map<String, dynamic>> flutterToggleWidgetInspector({
+ @required String isolateId,
+ }) => _flutterToggle('inspector.show', isolateId: isolateId);
+
+ Future<Map<String, dynamic>> flutterToggleProfileWidgetBuilds({
+ @required String isolateId,
+ }) => _flutterToggle('profileWidgetBuilds', isolateId: isolateId);
+
+ Future<Map<String, dynamic>> flutterDebugAllowBanner(bool show, {
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.debugAllowBanner',
+ isolateId: isolateId,
+ args: <String, dynamic>{'enabled': show ? 'true' : 'false'},
+ );
+ }
+
+ Future<Map<String, dynamic>> flutterReassemble({
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.reassemble',
+ isolateId: isolateId,
+ );
+ }
+
+ Future<Map<String, dynamic>> flutterFastReassemble(String classId, {
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.fastReassemble',
+ isolateId: isolateId,
+ args: <String, Object>{
+ 'class': classId,
+ },
+ );
+ }
+
+ Future<bool> flutterAlreadyPaintedFirstUsefulFrame({
+ @required String isolateId,
+ }) async {
+ final Map<String, dynamic> result = await invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.didSendFirstFrameRasterizedEvent',
+ isolateId: isolateId,
+ );
+ // result might be null when the service extension is not initialized
+ return result != null && result['enabled'] == 'true';
+ }
+
+ Future<Map<String, dynamic>> uiWindowScheduleFrame({
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.ui.window.scheduleFrame',
+ isolateId: isolateId,
+ );
+ }
+
+ Future<Map<String, dynamic>> flutterEvictAsset(String assetPath, {
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.evict',
+ isolateId: isolateId,
+ args: <String, dynamic>{
+ 'value': assetPath,
+ },
+ );
+ }
+
+ /// Exit the application by calling [exit] from `dart:io`.
+ ///
+ /// This method is only supported by certain embedders. This is
+ /// described by [Device.supportsFlutterExit].
+ Future<Map<String, dynamic>> flutterExit({
+ @required String isolateId,
+ }) {
+ return invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.exit',
+ isolateId: isolateId,
+ );
+ }
+
+ /// Return the current platform override for the flutter view running with
+ /// the main isolate [isolateId].
+ ///
+ /// If a non-null value is provided for [platform], the platform override
+ /// is updated with this value.
+ Future<String> flutterPlatformOverride({
+ String platform,
+ @required String isolateId,
+ }) async {
+ final Map<String, dynamic> result = await invokeFlutterExtensionRpcRaw(
+ 'ext.flutter.platformOverride',
+ isolateId: isolateId,
+ args: platform != null
+ ? <String, dynamic>{'value': platform}
+ : <String, String>{},
+ );
+ if (result != null && result['value'] is String) {
+ return result['value'] as String;
+ }
+ return 'unknown';
+ }
+
+ /// Invoke a flutter extension method, if the flutter extension is not
+ /// available, returns null.
+ Future<Map<String, dynamic>> invokeFlutterExtensionRpcRaw(
+ String method, {
+ @required String isolateId,
+ Map<String, dynamic> args,
+ }) async {
+ try {
+
+ final vm_service.Response response = await callServiceExtension(
+ method,
+ args: <String, Object>{
+ 'isolateId': isolateId,
+ ...?args,
+ },
+ );
+ return response.json;
+ } on vm_service.RPCError catch (err) {
+ // If an application is not using the framework
+ if (err.code == RPCErrorCodes.kMethodNotFound) {
+ return null;
+ }
+ rethrow;
+ }
+ }
}
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
index edef46e..5bc54f0 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
@@ -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/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 @@
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 @@
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));
diff --git a/packages/flutter_tools/test/general.shard/resident_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_runner_test.dart
index 5a33104..93f1c7a 100644
--- a/packages/flutter_tools/test/general.shard/resident_runner_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_runner_test.dart
@@ -41,6 +41,7 @@
ResidentRunner residentRunner;
MockDevice mockDevice;
MockIsolate mockIsolate;
+ FakeVmServiceHost fakeVmServiceHost;
setUp(() {
testbed = Testbed(setup: () {
@@ -89,31 +90,12 @@
invalidatedSourcesCount: 0,
);
});
- // TODO(jonahwilliams): replace mock with FakeVmServiceHost once all methods
- // are moved to real vm service.
when(mockFlutterDevice.devFS).thenReturn(mockDevFS);
when(mockFlutterDevice.views).thenReturn(<FlutterView>[
mockFlutterView,
]);
when(mockFlutterDevice.device).thenReturn(mockDevice);
when(mockFlutterView.uiIsolate).thenReturn(mockIsolate);
- final MockVM mockVM = MockVM();
- when(mockVMService.vm).thenReturn(mockVM);
- when(mockVM.isolates).thenReturn(<Isolate>[mockIsolate]);
- when(mockVMService.streamListen('Isolate')).thenAnswer((Invocation invocation) async {
- return vm_service.Success();
- });
- when(mockVMService.onIsolateEvent).thenAnswer((Invocation invocation) {
- return Stream<vm_service.Event>.fromIterable(<vm_service.Event>[
- vm_service.Event(kind: vm_service.EventKind.kIsolateRunnable, timestamp: 0),
- ]);
- });
- when(mockVMService.callMethod(
- kRunInViewMethod,
- args: anyNamed('args'),
- )).thenAnswer((Invocation invocation) async {
- return vm_service.Success();
- });
when(mockFlutterDevice.stopEchoingDeviceLog()).thenAnswer((Invocation invocation) async { });
when(mockFlutterDevice.observatoryUris).thenAnswer((_) => Stream<Uri>.value(testUri));
when(mockFlutterDevice.connect(
@@ -125,7 +107,12 @@
.thenAnswer((Invocation invocation) async {
return testUri;
});
- when(mockFlutterDevice.vmService).thenReturn(mockVMService);
+ when(mockFlutterDevice.vmService).thenAnswer((Invocation invocation) {
+ return fakeVmServiceHost.vmService;
+ });
+ when(mockFlutterDevice.flutterDeprecatedVmService).thenAnswer((Invocation invocation) {
+ return mockVMService;
+ });
when(mockFlutterDevice.refreshViews()).thenAnswer((Invocation invocation) async { });
when(mockFlutterDevice.getVMs()).thenAnswer((Invocation invocation) async { });
when(mockFlutterDevice.reloadSources(any, pause: anyNamed('pause'))).thenReturn(<Future<vm_service.ReloadReport>>[
@@ -147,15 +134,13 @@
final Completer<void> result = Completer<void>.sync();
return result.future;
});
- when(mockIsolate.flutterExit()).thenAnswer((Invocation invocation) {
- return Future<Map<String, Object>>.value(null);
- });
when(mockIsolate.reload()).thenAnswer((Invocation invocation) {
return Future<ServiceObject>.value(null);
});
});
test('ResidentRunner can attach to device successfully', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync();
final Future<int> result = residentRunner.attach(
@@ -174,6 +159,31 @@
}));
test('ResidentRunner can attach to device successfully with --fast-start', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ const FakeVmServiceRequest(
+ id: '1',
+ method: 'streamListen',
+ args: <String, Object>{
+ 'streamId': 'Isolate',
+ }
+ ),
+ const FakeVmServiceRequest(
+ id: '2',
+ method: kRunInViewMethod,
+ args: <String, Object>{
+ 'viewId': null,
+ 'mainScript': 'lib/main.dart.dill',
+ 'assetDirectory': 'build/flutter_assets',
+ }
+ ),
+ FakeVmServiceStreamResponse(
+ streamId: 'Isolate',
+ event: vm_service.Event(
+ timestamp: 0,
+ kind: vm_service.EventKind.kIsolateRunnable,
+ )
+ ),
+ ]);
when(mockDevice.supportsHotRestart).thenReturn(true);
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
return 'Example';
@@ -189,7 +199,11 @@
mockFlutterDevice,
],
stayResident: false,
- debuggingOptions: DebuggingOptions.enabled(BuildInfo.debug, fastStart: true, startPaused: true),
+ debuggingOptions: DebuggingOptions.enabled(
+ BuildInfo.debug,
+ fastStart: true,
+ startPaused: true,
+ ),
);
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync();
final Completer<void> onAppStart = Completer<void>.sync();
@@ -209,6 +223,7 @@
}));
test('ResidentRunner can handle an RPC exception from hot reload', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
return 'Example';
});
@@ -255,6 +270,16 @@
}));
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
+ const FakeVmServiceRequest(
+ id: '1',
+ method: 'ext.flutter.reassemble',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ ),
+ ]);
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
return 'Example';
});
@@ -284,6 +309,32 @@
}));
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>[
+ const FakeVmServiceRequest(
+ id: '1',
+ method: 'streamListen',
+ args: <String, Object>{
+ 'streamId': 'Isolate',
+ },
+ ),
+ const FakeVmServiceRequest(
+ id: '2',
+ method: kRunInViewMethod,
+ args: <String, Object>{
+ 'viewId': null,
+ 'mainScript': 'lib/main.dart.dill',
+ 'assetDirectory': 'build/flutter_assets',
+ },
+ ),
+ FakeVmServiceStreamResponse(
+ streamId: 'Isolate',
+ event: vm_service.Event(
+ timestamp: 0,
+ kind: vm_service.EventKind.kIsolateRunnable,
+ )
+ )
+ ]);
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
return 'Example';
});
@@ -314,6 +365,7 @@
}));
test('ResidentRunner Can handle an RPC exception from hot restart', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
return 'Example';
});
@@ -361,6 +413,7 @@
}));
test('ResidentRunner uses temp directory when there is no output dill path', () => testbed.run(() {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(residentRunner.artifactDirectory.path, contains('flutter_tool.'));
final ResidentRunner otherRunner = HotRunner(
@@ -375,6 +428,7 @@
}));
test('ResidentRunner copies output dill to cache location during preExit', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner.artifactDirectory.childFile('app.dill').writeAsStringSync('hello');
await residentRunner.preExit();
final File cacheDill = globals.fs.file(globals.fs.path.join(getBuildDirectory(), 'cache.dill'));
@@ -384,6 +438,7 @@
}));
test('ResidentRunner handles output dill missing during preExit', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.preExit();
final File cacheDill = globals.fs.file(globals.fs.path.join(getBuildDirectory(), 'cache.dill'));
@@ -391,6 +446,7 @@
}));
test('ResidentRunner printHelpDetails', () => testbed.run(() {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
when(mockDevice.supportsHotRestart).thenReturn(true);
when(mockDevice.supportsScreenshot).thenReturn(true);
@@ -436,39 +492,48 @@
}));
test('ResidentRunner does support CanvasKit', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
expect(() => residentRunner.toggleCanvaskit(),
throwsA(isA<Exception>()));
}));
test('ResidentRunner handles writeSkSL returning no data', () => testbed.run(() async {
- when(mockVMService.callMethod(
- kGetSkSLsMethod,
- args: anyNamed('args'),
- )).thenAnswer((Invocation invocation) async {
- return vm_service.Response.parse(<String, Object>{
- 'SkSLs': <String, Object>{}
- });
- });
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ const FakeVmServiceRequest(
+ id: '1',
+ method: kGetSkSLsMethod,
+ args: <String, Object>{
+ 'viewId': null,
+ },
+ jsonResponse: <String, Object>{
+ 'SkSLs': <String, Object>{}
+ }
+ )
+ ]);
await residentRunner.writeSkSL();
expect(testLogger.statusText, contains('No data was receieved'));
}));
test('ResidentRunner can write SkSL data to a unique file with engine revision, platform, and device name', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ const FakeVmServiceRequest(
+ id: '1',
+ method: kGetSkSLsMethod,
+ args: <String, Object>{
+ 'viewId': null,
+ },
+ jsonResponse: <String, Object>{
+ 'SkSLs': <String, Object>{
+ 'A': 'B',
+ }
+ }
+ )
+ ]);
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
return TargetPlatform.android_arm;
});
when(mockDevice.name).thenReturn('test device');
- when(mockVMService.callMethod(
- kGetSkSLsMethod,
- args: anyNamed('args'),
- )).thenAnswer((Invocation invocation) async {
- return vm_service.Response.parse(<String, Object>{
- 'SkSLs': <String, Object>{
- 'A': 'B',
- }
- });
- });
await residentRunner.writeSkSL();
expect(testLogger.statusText, contains('flutter_01.sksl'));
@@ -479,9 +544,28 @@
'engineRevision': '42.2', // From FakeFlutterVersion
'data': <String, Object>{'A': 'B'}
});
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('ResidentRunner can take screenshot on debug device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ const FakeVmServiceRequest(
+ id: '1',
+ method: 'ext.flutter.debugAllowBanner',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'false',
+ },
+ ),
+ const FakeVmServiceRequest(
+ id: '2',
+ method: 'ext.flutter.debugAllowBanner',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'true',
+ },
+ )
+ ]);
when(mockDevice.supportsScreenshot).thenReturn(true);
when(mockDevice.takeScreenshot(any))
.thenAnswer((Invocation invocation) async {
@@ -491,23 +575,12 @@
await residentRunner.screenshot(mockFlutterDevice);
- // disables debug banner.
- verify(mockIsolate.flutterDebugAllowBanner(false)).called(1);
- // Enables debug banner.
- verify(mockIsolate.flutterDebugAllowBanner(true)).called(1);
expect(testLogger.statusText, contains('1kB'));
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
- test('ResidentRunner bails taking screenshot on debug device if debugAllowBanner throws pre', () => testbed.run(() async {
- when(mockDevice.supportsScreenshot).thenReturn(true);
- when(mockIsolate.flutterDebugAllowBanner(false)).thenThrow(Exception());
-
- await residentRunner.screenshot(mockFlutterDevice);
-
- expect(testLogger.errorText, contains('Error'));
- }));
-
- test('ResidentTunner clears the screen when it should', () => testbed.run(() async {
+ test('ResidentRunner clears the screen when it should', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
const String message = 'This should be cleared';
expect(testLogger.statusText, equals(''));
testLogger.printStatus(message);
@@ -516,16 +589,72 @@
expect(testLogger.statusText, equals(''));
}));
- test('ResidentRunner bails taking screenshot on debug device if debugAllowBanner throws post', () => testbed.run(() async {
+ test('ResidentRunner bails taking screenshot on debug device if debugAllowBanner throws RpcError', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ const FakeVmServiceRequest(
+ id: '1',
+ method: 'ext.flutter.debugAllowBanner',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'false',
+ },
+ // Failed response,
+ errorCode: RPCErrorCodes.kInternalError,
+ )
+ ]);
when(mockDevice.supportsScreenshot).thenReturn(true);
- when(mockIsolate.flutterDebugAllowBanner(true)).thenThrow(Exception());
+ await residentRunner.screenshot(mockFlutterDevice);
+ expect(testLogger.errorText, contains('Error'));
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
+ }));
+
+ test('ResidentRunner bails taking screenshot on debug device if debugAllowBanner during second request', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ const FakeVmServiceRequest(
+ id: '1',
+ method: 'ext.flutter.debugAllowBanner',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'false',
+ },
+ ),
+ const FakeVmServiceRequest(
+ id: '2',
+ method: 'ext.flutter.debugAllowBanner',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'true',
+ },
+ // Failed response,
+ errorCode: RPCErrorCodes.kInternalError,
+ )
+ ]);
+ when(mockDevice.supportsScreenshot).thenReturn(true);
await residentRunner.screenshot(mockFlutterDevice);
expect(testLogger.errorText, contains('Error'));
}));
test('ResidentRunner bails taking screenshot on debug device if takeScreenshot throws', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ const FakeVmServiceRequest(
+ id: '1',
+ method: 'ext.flutter.debugAllowBanner',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'false',
+ },
+ ),
+ const FakeVmServiceRequest(
+ id: '2',
+ method: 'ext.flutter.debugAllowBanner',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'true',
+ },
+ ),
+ ]);
when(mockDevice.supportsScreenshot).thenReturn(true);
when(mockDevice.takeScreenshot(any)).thenThrow(Exception());
@@ -535,6 +664,7 @@
}));
test("ResidentRunner can't take screenshot on device without support", () => testbed.run(() {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
when(mockDevice.supportsScreenshot).thenReturn(false);
expect(() => residentRunner.screenshot(mockFlutterDevice),
@@ -542,6 +672,7 @@
}));
test('ResidentRunner does not toggle banner in non-debug mode', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentRunner = HotRunner(
<FlutterDevice>[
mockFlutterDevice,
@@ -558,18 +689,17 @@
await residentRunner.screenshot(mockFlutterDevice);
- // doesn't disabled debug banner.
- verifyNever(mockIsolate.flutterDebugAllowBanner(false));
- // doesn't enable debug banner.
- verifyNever(mockIsolate.flutterDebugAllowBanner(true));
expect(testLogger.statusText, contains('1kB'));
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('FlutterDevice will not exit a paused isolate', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final TestFlutterDevice flutterDevice = TestFlutterDevice(
mockDevice,
<FlutterView>[ mockFlutterView ],
);
+ flutterDevice.vmService = fakeVmServiceHost.vmService;
final MockServiceEvent mockServiceEvent = MockServiceEvent();
when(mockServiceEvent.isPauseEvent).thenReturn(true);
when(mockIsolate.pauseEvent).thenReturn(mockServiceEvent);
@@ -577,15 +707,25 @@
await flutterDevice.exitApps();
- verifyNever(mockIsolate.flutterExit());
verify(mockDevice.stopApp(any)).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('FlutterDevice will exit an un-paused isolate', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ const FakeVmServiceRequest(
+ id: '1',
+ method: 'ext.flutter.exit',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ )
+ ]);
final TestFlutterDevice flutterDevice = TestFlutterDevice(
mockDevice,
- <FlutterView> [mockFlutterView ],
+ <FlutterView> [mockFlutterView],
);
+ flutterDevice.vmService = fakeVmServiceHost.vmService;
final MockServiceEvent mockServiceEvent = MockServiceEvent();
when(mockServiceEvent.isPauseEvent).thenReturn(false);
@@ -593,17 +733,18 @@
when(mockDevice.supportsFlutterExit).thenReturn(true);
await flutterDevice.exitApps();
-
- verify(mockIsolate.flutterExit()).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('ResidentRunner refreshViews calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.refreshViews();
verify(mockFlutterDevice.refreshViews()).called(1);
}));
test('ResidentRunner debugDumpApp calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugDumpApp();
verify(mockFlutterDevice.refreshViews()).called(1);
@@ -611,6 +752,7 @@
}));
test('ResidentRunner debugDumpRenderTree calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugDumpRenderTree();
verify(mockFlutterDevice.refreshViews()).called(1);
@@ -618,6 +760,7 @@
}));
test('ResidentRunner debugDumpLayerTree calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugDumpLayerTree();
verify(mockFlutterDevice.refreshViews()).called(1);
@@ -625,6 +768,7 @@
}));
test('ResidentRunner debugDumpSemanticsTreeInTraversalOrder calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugDumpSemanticsTreeInTraversalOrder();
verify(mockFlutterDevice.refreshViews()).called(1);
@@ -632,6 +776,7 @@
}));
test('ResidentRunner debugDumpSemanticsTreeInInverseHitTestOrder calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugDumpSemanticsTreeInInverseHitTestOrder();
verify(mockFlutterDevice.refreshViews()).called(1);
@@ -639,6 +784,7 @@
}));
test('ResidentRunner debugToggleDebugPaintSizeEnabled calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugToggleDebugPaintSizeEnabled();
verify(mockFlutterDevice.refreshViews()).called(1);
@@ -646,13 +792,15 @@
}));
test('ResidentRunner debugToggleDebugCheckElevationsEnabled calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugToggleDebugCheckElevationsEnabled();
verify(mockFlutterDevice.refreshViews()).called(1);
verify(mockFlutterDevice.toggleDebugCheckElevationsEnabled()).called(1);
}));
- test('ResidentRunner debugTogglePerformanceOverlayOverride calls flutter device', () => testbed.run(()async {
+ test('ResidentRunner debugTogglePerformanceOverlayOverride calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugTogglePerformanceOverlayOverride();
verify(mockFlutterDevice.refreshViews()).called(1);
@@ -660,6 +808,7 @@
}));
test('ResidentRunner debugToggleWidgetInspector calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugToggleWidgetInspector();
verify(mockFlutterDevice.refreshViews()).called(1);
@@ -667,6 +816,7 @@
}));
test('ResidentRunner debugToggleProfileWidgetBuilds calls flutter device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
await residentRunner.debugToggleProfileWidgetBuilds();
verify(mockFlutterDevice.refreshViews()).called(1);
@@ -674,6 +824,7 @@
}));
test('HotRunner writes vm service file when providing debugging option', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
residentRunner = HotRunner(
<FlutterDevice>[
@@ -694,6 +845,7 @@
}));
test('HotRunner unforwards device ports', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final MockDevicePortForwarder mockPortForwarder = MockDevicePortForwarder();
when(mockDevice.portForwarder).thenReturn(mockPortForwarder);
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
@@ -721,6 +873,7 @@
}));
test('HotRunner handles failure to write vmservice file', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
residentRunner = HotRunner(
<FlutterDevice>[
@@ -744,6 +897,7 @@
test('ColdRunner writes vm service file when providing debugging option', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
residentRunner = ColdRunner(
<FlutterDevice>[
@@ -764,6 +918,7 @@
}));
test('FlutterDevice uses dartdevc configuration when targeting web', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final MockDevice mockDevice = MockDevice();
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
return TargetPlatform.web_javascript;
@@ -792,6 +947,7 @@
}));
test('connect sets up log reader', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final MockDevice mockDevice = MockDevice();
final MockDeviceLogReader mockLogReader = MockDeviceLogReader();
when(mockDevice.getLogReader(app: anyNamed('app'))).thenReturn(mockLogReader);
diff --git a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
index b018a46..30362a7 100644
--- a/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
+++ b/packages/flutter_tools/test/general.shard/resident_web_runner_test.dart
@@ -5,6 +5,8 @@
import 'dart:async';
import 'dart:convert';
+import 'package:flutter_tools/src/vmservice.dart';
+import 'package:vm_service/vm_service.dart' as vm_service;
import 'package:dwds/dwds.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/io.dart';
@@ -32,11 +34,50 @@
import '../src/context.dart';
import '../src/testbed.dart';
+const List<VmServiceExpectation> kAttachLogExpectations = <VmServiceExpectation>[
+ FakeVmServiceRequest(
+ id: '1',
+ method: 'streamListen',
+ args: <String, Object>{
+ 'streamId': 'Stdout',
+ },
+ ),
+ FakeVmServiceRequest(
+ id: '2',
+ method: 'streamListen',
+ args: <String, Object>{
+ 'streamId': 'Stderr',
+ },
+ )
+];
+
+const List<VmServiceExpectation> kAttachIsolateExpectations = <VmServiceExpectation>[
+ FakeVmServiceRequest(
+ id: '3',
+ method: 'streamListen',
+ args: <String, Object>{
+ 'streamId': 'Isolate'
+ }
+ ),
+ FakeVmServiceRequest(
+ id: '4',
+ method: 'registerService',
+ args: <String, Object>{
+ 'service': 'reloadSources',
+ 'alias': 'FlutterTools',
+ }
+ )
+];
+
+const List<VmServiceExpectation> kAttachExpectations = <VmServiceExpectation>[
+ ...kAttachLogExpectations,
+ ...kAttachIsolateExpectations,
+];
+
void main() {
Testbed testbed;
ResidentWebRunner residentWebRunner;
MockDebugConnection mockDebugConnection;
- MockVmService mockVmService;
MockChromeDevice mockChromeDevice;
MockAppConnection mockAppConnection;
MockFlutterDevice mockFlutterDevice;
@@ -49,11 +90,11 @@
MockWipDebugger mockWipDebugger;
MockWebServerDevice mockWebServerDevice;
MockDevice mockDevice;
+ FakeVmServiceHost fakeVmServiceHost;
setUp(() {
resetChromeForTesting();
mockDebugConnection = MockDebugConnection();
- mockVmService = MockVmService();
mockDevice = MockDevice();
mockAppConnection = MockAppConnection();
mockFlutterDevice = MockFlutterDevice();
@@ -110,24 +151,12 @@
)).thenAnswer((Invocation _) async {
return UpdateFSReport(success: true, syncedBytes: 0)..invalidatedModules = <String>[];
});
- when(mockDebugConnection.vmService).thenReturn(mockVmService);
+ when(mockDebugConnection.vmService).thenAnswer((Invocation invocation) {
+ return fakeVmServiceHost.vmService;
+ });
when(mockDebugConnection.onDone).thenAnswer((Invocation invocation) {
return Completer<void>().future;
});
- when(mockVmService.onStdoutEvent).thenAnswer((Invocation _) {
- return const Stream<Event>.empty();
- });
- when(mockVmService.onStderrEvent).thenAnswer((Invocation _) {
- return const Stream<Event>.empty();
- });
- when(mockVmService.onDebugEvent).thenAnswer((Invocation _) {
- return const Stream<Event>.empty();
- });
- when(mockVmService.onIsolateEvent).thenAnswer((Invocation _) {
- return Stream<Event>.fromIterable(<Event>[
- Event(kind: EventKind.kIsolateStart, timestamp: 1),
- ]);
- });
when(mockDebugConnection.uri).thenReturn('ws://127.0.0.1/abcd/');
when(mockFlutterDevice.devFS).thenReturn(mockWebDevFS);
when(mockWebDevFS.sources).thenReturn(<Uri>[]);
@@ -144,6 +173,7 @@
}
test('runner with web server device does not support debugging without --start-paused', () => testbed.run(() {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
when(mockFlutterDevice.device).thenReturn(WebServerDevice());
final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
@@ -158,9 +188,11 @@
when(mockFlutterDevice.device).thenReturn(MockChromeDevice());
expect(residentWebRunner.debuggingEnabled, true);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('runner with web server device supports debugging with --start-paused', () => testbed.run(() {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
when(mockFlutterDevice.device).thenReturn(WebServerDevice());
final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner(
@@ -177,6 +209,7 @@
}));
test('profile does not supportsServiceProtocol', () => testbed.run(() {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
when(mockFlutterDevice.device).thenReturn(mockChromeDevice);
final ResidentRunner profileResidentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
@@ -192,6 +225,7 @@
}));
test('Exits on run if application does not support the web', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
globals.fs.file('pubspec.yaml').createSync();
expect(await residentWebRunner.run(), 1);
@@ -199,6 +233,7 @@
}));
test('Exits on run if target file does not exist', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
globals.fs.file('pubspec.yaml').createSync();
globals.fs.file(globals.fs.path.join('web', 'index.html')).createSync(recursive: true);
@@ -208,6 +243,7 @@
}));
test('Can successfully run and connect to vmservice', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
_setupMocks();
final DelegateLogger delegateLogger = globals.logger as DelegateLogger;
final BufferLogger bufferLogger = delegateLogger.delegate as BufferLogger;
@@ -220,7 +256,6 @@
final DebugConnectionInfo debugConnectionInfo = await connectionInfoCompleter.future;
verify(mockAppConnection.runMain()).called(1);
- verify(mockVmService.registerService('reloadSources', 'FlutterTools')).called(1);
verify(status.stop()).called(1);
verify(pub.get(
context: PubContext.pubGet,
@@ -240,6 +275,7 @@
}));
test('Can successfully run and disconnect with --no-resident', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
_setupMocks();
residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
@@ -254,30 +290,28 @@
}));
test('Listens to stdout and stderr streams before running main', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachLogExpectations,
+ FakeVmServiceStreamResponse(
+ streamId: 'Stdout',
+ event: vm_service.Event(
+ timestamp: 0,
+ kind: vm_service.EventStreams.kStdout,
+ bytes: base64.encode(utf8.encode('THIS MESSAGE IS IMPORTANT'))
+ ),
+ ),
+ FakeVmServiceStreamResponse(
+ streamId: 'Stderr',
+ event: vm_service.Event(
+ timestamp: 0,
+ kind: vm_service.EventStreams.kStderr,
+ bytes: base64.encode(utf8.encode('SO IS THIS'))
+ ),
+ ),
+ ...kAttachIsolateExpectations,
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
- final StreamController<Event> stdoutController = StreamController<Event>.broadcast();
- final StreamController<Event> stderrController = StreamController<Event>.broadcast();
- when(mockVmService.onStdoutEvent).thenAnswer((Invocation _) {
- return stdoutController.stream;
- });
- when(mockVmService.onStderrEvent).thenAnswer((Invocation _) {
- return stderrController.stream;
- });
- when(mockAppConnection.runMain()).thenAnswer((Invocation invocation) {
- stdoutController.add(Event.parse(<String, Object>{
- 'type': 'Event',
- 'kind': 'WriteEvent',
- 'timestamp': 1569473488296,
- 'bytes': base64.encode('THIS MESSAGE IS IMPORTANT'.codeUnits),
- }));
- stderrController.add(Event.parse(<String, Object>{
- 'type': 'Event',
- 'kind': 'WriteEvent',
- 'timestamp': 1569473488296,
- 'bytes': base64.encode('SO IS THIS'.codeUnits),
- }));
- });
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
@@ -288,6 +322,7 @@
}));
test('Does not run main with --start-paused', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
residentWebRunner = DwdsWebRunnerFactory().createWebRunner(
mockFlutterDevice,
flutterProject: FlutterProject.current(),
@@ -298,10 +333,7 @@
) as ResidentWebRunner;
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
- final StreamController<Event> controller = StreamController<Event>.broadcast();
- when(mockVmService.onStdoutEvent).thenAnswer((Invocation _) {
- return controller.stream;
- });
+
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
@@ -311,6 +343,17 @@
}));
test('Can hot reload after attaching', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ method: 'hotRestart',
+ id: '5',
+ args: null,
+ jsonResponse: <String, Object>{
+ 'type': 'Success',
+ }
+ ),
+ ]);
_setupMocks();
launchChromeInstance(mockChrome);
when(mockWebDevFS.update(
@@ -362,6 +405,17 @@
}));
test('Can hot restart after attaching', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ method: 'hotRestart',
+ id: '5',
+ args: null,
+ jsonResponse: <String, Object>{
+ 'type': 'Success',
+ }
+ ),
+ ]);
_setupMocks();
launchChromeInstance(mockChrome);
Uri entrypointFileUri;
@@ -417,6 +471,7 @@
}));
test('Can hot restart after attaching with web-server device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests :kAttachExpectations);
_setupMocks();
when(mockFlutterDevice.device).thenReturn(mockWebServerDevice);
when(mockWebDevFS.update(
@@ -454,10 +509,13 @@
}));
test('web resident runner is debuggable', () => testbed.run(() {
+ fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
+
expect(residentWebRunner.debuggingEnabled, true);
}));
test('web resident runner can toggle CanvasKit', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
final WebAssetServer webAssetServer = WebAssetServer(null, null, null, null, null);
when(mockWebDevFS.webAssetServer).thenReturn(webAssetServer);
@@ -471,6 +529,7 @@
}));
test('Exits when initial compile fails', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
when(mockWebDevFS.update(
mainUri: anyNamed('mainUri'),
@@ -501,30 +560,34 @@
}));
test('Faithfully displays stdout messages with leading/trailing spaces', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachLogExpectations,
+ FakeVmServiceStreamResponse(
+ streamId: 'Stdout',
+ event: vm_service.Event(
+ timestamp: 0,
+ kind: vm_service.EventStreams.kStdout,
+ bytes: base64.encode(
+ utf8.encode(' This is a message with 4 leading and trailing spaces '),
+ ),
+ ),
+ ),
+ ...kAttachIsolateExpectations,
+ ]);
_setupMocks();
- final StreamController<Event> stdoutController = StreamController<Event>();
- when(mockVmService.onStdoutEvent).thenAnswer((Invocation invocation) {
- return stdoutController.stream;
- });
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
- stdoutController.add(Event(
- timestamp: 0,
- kind: 'Stdout',
- bytes: base64.encode(utf8.encode(' This is a message with 4 leading and trailing spaces '))),
- );
- // Wait one event loop for the stream listener to fire.
- await null;
-
expect(testLogger.statusText,
contains(' This is a message with 4 leading and trailing spaces '));
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('Fails on compilation errors in hot restart', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: kAttachExpectations.toList());
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
@@ -559,62 +622,55 @@
}));
test('Fails non-fatally on vmservice response error for hot restart', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'hotRestart',
+ args: null,
+ jsonResponse: <String, Object>{
+ 'type': 'Failed',
+ }
+ )
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
- when(mockVmService.callMethod('hotRestart')).thenAnswer((Invocation _) async {
- return Response.parse(<String, Object>{'type': 'Failed'});
- });
+
final OperationResult result = await residentWebRunner.restart(fullRestart: false);
expect(result.code, 0);
}));
- test('Fails fatally on vmservice RpcError', () => testbed.run(() async {
+ test('Fails fatally on Vm Service error response', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'hotRestart',
+ args: null,
+ // Failed response,
+ errorCode: RPCErrorCodes.kInternalError,
+ ),
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
- when(mockVmService.callMethod('hotRestart')).thenThrow(RPCError('Something went wrong', 2, '123'));
final OperationResult result = await residentWebRunner.restart(fullRestart: false);
expect(result.code, 1);
- expect(result.message, contains('Something went wrong'));
- }));
-
- test('Fails fatally on vmservice WipError', () => testbed.run(() async {
- _setupMocks();
- final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
- unawaited(residentWebRunner.run(
- connectionInfoCompleter: connectionInfoCompleter,
- ));
- await connectionInfoCompleter.future;
- when(mockVmService.callMethod('hotRestart')).thenThrow(WipError(<String, String>{}));
- final OperationResult result = await residentWebRunner.restart(fullRestart: false);
-
- expect(result.code, 1);
- }));
-
- test('Fails fatally on vmservice Exception', () => testbed.run(() async {
- _setupMocks();
- final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
- unawaited(residentWebRunner.run(
- connectionInfoCompleter: connectionInfoCompleter,
- ));
- await connectionInfoCompleter.future;
- when(mockVmService.callMethod('hotRestart')).thenThrow(Exception('Something went wrong'));
- final OperationResult result = await residentWebRunner.restart(fullRestart: false);
-
- expect(result.code, 1);
- expect(result.message, contains('Something went wrong'));
+ expect(result.message,
+ contains(RPCErrorCodes.kInternalError.toString()));
}));
test('printHelp without details has web warning', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
residentWebRunner.printHelp(details: false);
expect(testLogger.statusText, contains('Warning'));
@@ -623,6 +679,16 @@
}));
test('debugDumpApp', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'ext.flutter.debugDumpApp',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ ),
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
@@ -631,10 +697,20 @@
await connectionInfoCompleter.future;
await residentWebRunner.debugDumpApp();
- verify(mockVmService.callServiceExtension('ext.flutter.debugDumpApp')).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('debugDumpLayerTree', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'ext.flutter.debugDumpLayerTree',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ ),
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
@@ -643,10 +719,20 @@
await connectionInfoCompleter.future;
await residentWebRunner.debugDumpLayerTree();
- verify(mockVmService.callServiceExtension('ext.flutter.debugDumpLayerTree')).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('debugDumpRenderTree', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'ext.flutter.debugDumpRenderTree',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ ),
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
@@ -655,10 +741,20 @@
await connectionInfoCompleter.future;
await residentWebRunner.debugDumpRenderTree();
- verify(mockVmService.callServiceExtension('ext.flutter.debugDumpRenderTree')).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('debugDumpSemanticsTreeInTraversalOrder', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'ext.flutter.debugDumpSemanticsTreeInTraversalOrder',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ ),
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
@@ -667,113 +763,224 @@
await connectionInfoCompleter.future;
await residentWebRunner.debugDumpSemanticsTreeInTraversalOrder();
- verify(mockVmService.callServiceExtension('ext.flutter.debugDumpSemanticsTreeInTraversalOrder')).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('debugDumpSemanticsTreeInInverseHitTestOrder', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ ),
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
+
await connectionInfoCompleter.future;
await residentWebRunner.debugDumpSemanticsTreeInInverseHitTestOrder();
- verify(mockVmService.callServiceExtension('ext.flutter.debugDumpSemanticsTreeInInverseHitTestOrder')).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('debugToggleDebugPaintSizeEnabled', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'ext.flutter.debugPaint',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ jsonResponse: <String, Object>{
+ 'enabled': 'false'
+ },
+ ),
+ const FakeVmServiceRequest(
+ id: '6',
+ method: 'ext.flutter.debugPaint',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'true',
+ },
+ jsonResponse: <String, Object>{
+ 'value': 'true'
+ },
+ )
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
- when(mockVmService.callServiceExtension('ext.flutter.debugPaint'))
- .thenAnswer((Invocation _) async {
- return Response.parse(<String, Object>{'enabled': false});
- });
+
await residentWebRunner.debugToggleDebugPaintSizeEnabled();
- verify(mockVmService.callServiceExtension('ext.flutter.debugPaint',
- args: <String, Object>{'enabled': true})).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('debugTogglePerformanceOverlayOverride', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'ext.flutter.showPerformanceOverlay',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ jsonResponse: <String, Object>{
+ 'enabled': 'false'
+ },
+ ),
+ const FakeVmServiceRequest(
+ id: '6',
+ method: 'ext.flutter.showPerformanceOverlay',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'true',
+ },
+ jsonResponse: <String, Object>{
+ 'enabled': 'true'
+ },
+ )
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
- when(mockVmService.callServiceExtension('ext.flutter.showPerformanceOverlay'))
- .thenAnswer((Invocation _) async {
- return Response.parse(<String, Object>{'enabled': false});
- });
await residentWebRunner.debugTogglePerformanceOverlayOverride();
- verify(mockVmService.callServiceExtension('ext.flutter.showPerformanceOverlay',
- args: <String, Object>{'enabled': true})).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('debugToggleWidgetInspector', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'ext.flutter.inspector.show',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ jsonResponse: <String, Object>{
+ 'enabled': 'false'
+ },
+ ),
+ const FakeVmServiceRequest(
+ id: '6',
+ method: 'ext.flutter.inspector.show',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'true',
+ },
+ jsonResponse: <String, Object>{
+ 'enabled': 'true'
+ },
+ )
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
- when(mockVmService.callServiceExtension('ext.flutter.debugToggleWidgetInspector'))
- .thenAnswer((Invocation _) async {
- return Response.parse(<String, Object>{'enabled': false});
- });
await residentWebRunner.debugToggleWidgetInspector();
- verify(mockVmService.callServiceExtension('ext.flutter.debugToggleWidgetInspector',
- args: <String, Object>{'enabled': true})).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('debugToggleProfileWidgetBuilds', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'ext.flutter.profileWidgetBuilds',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ jsonResponse: <String, Object>{
+ 'enabled': 'false'
+ },
+ ),
+ const FakeVmServiceRequest(
+ id: '6',
+ method: 'ext.flutter.profileWidgetBuilds',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'enabled': 'true',
+ },
+ jsonResponse: <String, Object>{
+ 'enabled': 'true'
+ },
+ )
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
- when(mockVmService.callServiceExtension('ext.flutter.profileWidgetBuilds'))
- .thenAnswer((Invocation _) async {
- return Response.parse(<String, Object>{'enabled': false});
- });
await residentWebRunner.debugToggleProfileWidgetBuilds();
- verify(mockVmService.callServiceExtension('ext.flutter.profileWidgetBuilds',
- args: <String, Object>{'enabled': true})).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('debugTogglePlatform', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ const FakeVmServiceRequest(
+ id: '5',
+ method: 'ext.flutter.platformOverride',
+ args: <String, Object>{
+ 'isolateId': null,
+ },
+ jsonResponse: <String, Object>{
+ 'value': 'iOS'
+ },
+ ),
+ const FakeVmServiceRequest(
+ id: '6',
+ method: 'ext.flutter.platformOverride',
+ args: <String, Object>{
+ 'isolateId': null,
+ 'value': 'fuchsia',
+ },
+ jsonResponse: <String, Object>{
+ 'value': 'fuchsia'
+ },
+ ),
+ ]);
_setupMocks();
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
unawaited(residentWebRunner.run(
connectionInfoCompleter: connectionInfoCompleter,
));
await connectionInfoCompleter.future;
- when(mockVmService.callServiceExtension('ext.flutter.platformOverride'))
- .thenAnswer((Invocation _) async {
- return Response.parse(<String, Object>{'value': 'iOS'});
- });
await residentWebRunner.debugTogglePlatform();
- expect(testLogger.statusText, contains('Switched operating system to fuchsia'));
- verify(mockVmService.callServiceExtension('ext.flutter.platformOverride',
- args: <String, Object>{'value': 'fuchsia'})).called(1);
+ expect(testLogger.statusText,
+ contains('Switched operating system to fuchsia'));
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('cleanup of resources is safe to call multiple times', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ ]);
_setupMocks();
bool debugClosed = false;
when(mockDevice.stopApp(any)).thenAnswer((Invocation invocation) async {
@@ -793,9 +1000,13 @@
await residentWebRunner.exit();
verifyNever(mockDebugConnection.close());
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('cleans up Chrome if tab is closed', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ ]);
_setupMocks();
final Completer<void> onDone = Completer<void>();
when(mockDebugConnection.onDone).thenAnswer((Invocation invocation) {
@@ -809,9 +1020,13 @@
onDone.complete();
await result;
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('Prints target and device name on run', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachExpectations,
+ ]);
_setupMocks();
when(mockDevice.name).thenReturn('Chromez');
final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
@@ -820,10 +1035,32 @@
));
await connectionInfoCompleter.future;
- expect(testLogger.statusText, contains('Launching ${globals.fs.path.join('lib', 'main.dart')} on Chromez in debug mode'));
+ expect(testLogger.statusText, contains(
+ 'Launching ${globals.fs.path.join('lib', 'main.dart')} on '
+ 'Chromez in debug mode',
+ ));
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('Sends launched app.webLaunchUrl event for Chrome device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[
+ ...kAttachLogExpectations,
+ const FakeVmServiceRequest(
+ id: '3',
+ method: 'streamListen',
+ args: <String, Object>{
+ 'streamId': 'Isolate'
+ }
+ ),
+ const FakeVmServiceRequest(
+ id: '4',
+ method: 'registerService',
+ args: <String, Object>{
+ 'service': 'reloadSources',
+ 'alias': 'FlutterTools',
+ }
+ )
+ ]);
_setupMocks();
when(mockFlutterDevice.device).thenReturn(ChromeDevice());
when(mockWebDevFS.create()).thenAnswer((Invocation invocation) async {
@@ -870,12 +1107,14 @@
},
},
)));
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
Logger: () => DelegateLogger(BufferLogger.test()),
ChromeLauncher: () => MockChromeLauncher(),
}));
test('Sends unlaunched app.webLaunchUrl event for Web Server device', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
when(mockFlutterDevice.device).thenReturn(WebServerDevice());
when(mockWebDevFS.create()).thenAnswer((Invocation invocation) async {
@@ -910,106 +1149,65 @@
},
},
)));
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
Logger: () => DelegateLogger(BufferLogger.test())
}));
test('Successfully turns WebSocketException into ToolExit', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
- final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
- final Completer<void> unhandledErrorCompleter = Completer<void>();
- when(mockWebDevFS.connect(any)).thenAnswer((Invocation _) async {
- unawaited(unhandledErrorCompleter.future.then((void value) {
- throw const WebSocketException();
- }));
- return ConnectionResult(mockAppConnection, mockDebugConnection);
- });
- final Future<void> expectation = expectLater(() => residentWebRunner.run(
- connectionInfoCompleter: connectionInfoCompleter,
- ), throwsToolExit());
+ when(mockWebDevFS.connect(any))
+ .thenThrow(const WebSocketException());
- unhandledErrorCompleter.complete();
- await expectation;
+ await expectLater(() => residentWebRunner.run(), throwsToolExit());
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('Successfully turns AppConnectionException into ToolExit', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
- final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
- final Completer<void> unhandledErrorCompleter = Completer<void>();
- when(mockWebDevFS.connect(any)).thenAnswer((Invocation _) async {
- unawaited(unhandledErrorCompleter.future.then((void value) {
- throw AppConnectionException('Could not connect to application with appInstanceId: c0ae0750-ee91-11e9-cea6-35d95a968356');
- }));
- return ConnectionResult(mockAppConnection, mockDebugConnection);
- });
- final Future<void> expectation = expectLater(() => residentWebRunner.run(
- connectionInfoCompleter: connectionInfoCompleter,
- ), throwsToolExit());
+ when(mockWebDevFS.connect(any))
+ .thenThrow(AppConnectionException(''));
- unhandledErrorCompleter.complete();
- await expectation;
+ await expectLater(() => residentWebRunner.run(), throwsToolExit());
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
test('Successfully turns ChromeDebugError into ToolExit', () => testbed.run(() async {
- _setupMocks();
- final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
- final Completer<void> unhandledErrorCompleter = Completer<void>();
- when(mockWebDevFS.connect(any)).thenAnswer((Invocation _) async {
- unawaited(unhandledErrorCompleter.future.then((void value) {
- throw ChromeDebugException(<String, dynamic>{});
- }));
- return ConnectionResult(mockAppConnection, mockDebugConnection);
- });
-
- final Future<void> expectation = expectLater(() => residentWebRunner.run(
- connectionInfoCompleter: connectionInfoCompleter,
- ), throwsToolExit());
-
- unhandledErrorCompleter.complete();
- await expectation;
- }));
-
- test('Rethrows Exception type', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
- final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
- final Completer<void> unhandledErrorCompleter = Completer<void>();
- when(mockWebDevFS.connect(any)).thenAnswer((Invocation _) async {
- unawaited(unhandledErrorCompleter.future.then((void value) {
- throw Exception('Something went wrong');
- }));
- return ConnectionResult(mockAppConnection, mockDebugConnection);
- });
- final Future<void> expectation = expectLater(() => residentWebRunner.run(
- connectionInfoCompleter: connectionInfoCompleter,
- ), throwsException);
+ when(mockWebDevFS.connect(any))
+ .thenThrow(ChromeDebugException(<String, dynamic>{}));
- unhandledErrorCompleter.complete();
- await expectation;
+ await expectLater(() => residentWebRunner.run(), throwsToolExit());
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}));
- test('Rethrows unknown exception type from web tooling', () => testbed.run(() async {
+
+ test('Rethrows unknown Exception type from dwds', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
+ _setupMocks();
+ when(mockWebDevFS.connect(any)).thenThrow(Exception());
+
+ await expectLater(() => residentWebRunner.run(), throwsException);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
+ }));
+
+ test('Rethrows unknown Error type from dwds tooling', () => testbed.run(() async {
+ fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks();
final DelegateLogger delegateLogger = globals.logger as DelegateLogger;
final MockStatus mockStatus = MockStatus();
delegateLogger.status = mockStatus;
- final Completer<DebugConnectionInfo> connectionInfoCompleter = Completer<DebugConnectionInfo>();
- final Completer<void> unhandledErrorCompleter = Completer<void>();
- when(mockWebDevFS.connect(any)).thenAnswer((Invocation _) async {
- unawaited(unhandledErrorCompleter.future.then((void value) {
- throw StateError('Something went wrong');
- }));
- return ConnectionResult(mockAppConnection, mockDebugConnection);
- });
- final Future<void> expectation = expectLater(() => residentWebRunner.run(
- connectionInfoCompleter: connectionInfoCompleter,
- ), throwsStateError);
+ when(mockWebDevFS.connect(any)).thenThrow(StateError(''));
- unhandledErrorCompleter.complete();
- await expectation;
+ await expectLater(() => residentWebRunner.run(), throwsStateError);
verify(mockStatus.stop()).called(1);
+ expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{
Logger: () => DelegateLogger(BufferLogger(
terminal: AnsiTerminal(
diff --git a/packages/flutter_tools/test/general.shard/vmservice_test.dart b/packages/flutter_tools/test/general.shard/vmservice_test.dart
index 9439e85..88db3c1 100644
--- a/packages/flutter_tools/test/general.shard/vmservice_test.dart
+++ b/packages/flutter_tools/test/general.shard/vmservice_test.dart
@@ -6,7 +6,6 @@
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 @@
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 @@
});
}
-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 {
diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart
index ecf413d..298b5e7 100644
--- a/packages/flutter_tools/test/src/common.dart
+++ b/packages/flutter_tools/test/src/common.dart
@@ -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 @@
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;
+}