Unverified Commit 534b0608 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] remove vm service (#55794)

Finishes the gradual vm service migration by deleting the flutter tooling's vm_service
parent c55b3220
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
import 'dart:async'; import 'dart:async';
import 'package:vm_service/vm_service.dart' as vm_service;
import '../base/common.dart'; import '../base/common.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../convert.dart'; import '../convert.dart';
...@@ -123,39 +125,37 @@ class ScreenshotCommand extends FlutterCommand { ...@@ -123,39 +125,37 @@ class ScreenshotCommand extends FlutterCommand {
} }
Future<void> runSkia(File outputFile) async { Future<void> runSkia(File outputFile) async {
final Map<String, dynamic> skp = await _invokeVmServiceRpc('_flutter.screenshotSkp'); final Uri observatoryUri = Uri.parse(stringArg(_kObservatoryUri));
final vm_service.VmService vmService = await connectToVmService(observatoryUri);
final vm_service.Response skp = await vmService.screenshotSkp();
outputFile ??= globals.fsUtils.getUniqueFile( outputFile ??= globals.fsUtils.getUniqueFile(
globals.fs.currentDirectory, globals.fs.currentDirectory,
'flutter', 'flutter',
'skp', 'skp',
); );
final IOSink sink = outputFile.openWrite(); final IOSink sink = outputFile.openWrite();
sink.add(base64.decode(skp['skp'] as String)); sink.add(base64.decode(skp.json['skp'] as String));
await sink.close(); await sink.close();
_showOutputFileInfo(outputFile); _showOutputFileInfo(outputFile);
_ensureOutputIsNotJsonRpcError(outputFile); _ensureOutputIsNotJsonRpcError(outputFile);
} }
Future<void> runRasterizer(File outputFile) async { Future<void> runRasterizer(File outputFile) async {
final Map<String, dynamic> response = await _invokeVmServiceRpc('_flutter.screenshot'); final Uri observatoryUri = Uri.parse(stringArg(_kObservatoryUri));
final vm_service.VmService vmService = await connectToVmService(observatoryUri);
final vm_service.Response response = await vmService.screenshot();
outputFile ??= globals.fsUtils.getUniqueFile( outputFile ??= globals.fsUtils.getUniqueFile(
globals.fs.currentDirectory, globals.fs.currentDirectory,
'flutter', 'flutter',
'png', 'png',
); );
final IOSink sink = outputFile.openWrite(); final IOSink sink = outputFile.openWrite();
sink.add(base64.decode(response['screenshot'] as String)); sink.add(base64.decode(response.json['screenshot'] as String));
await sink.close(); await sink.close();
_showOutputFileInfo(outputFile); _showOutputFileInfo(outputFile);
_ensureOutputIsNotJsonRpcError(outputFile); _ensureOutputIsNotJsonRpcError(outputFile);
} }
Future<Map<String, dynamic>> _invokeVmServiceRpc(String method) async {
final Uri observatoryUri = Uri.parse(stringArg(_kObservatoryUri));
final VMService vmService = await VMService.connect(observatoryUri);
return await vmService.vm.invokeRpcRaw(method);
}
void _ensureOutputIsNotJsonRpcError(File outputFile) { void _ensureOutputIsNotJsonRpcError(File outputFile) {
if (outputFile.lengthSync() >= 1000) { if (outputFile.lengthSync() >= 1000) {
return; return;
......
...@@ -6,7 +6,7 @@ import 'dart:async'; ...@@ -6,7 +6,7 @@ import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:package_config/package_config.dart'; import 'package:package_config/package_config.dart';
import 'package:vm_service/vm_service.dart' as vmservice; import 'package:vm_service/vm_service.dart' as vm_service;
import 'asset.dart'; import 'asset.dart';
import 'base/context.dart'; import 'base/context.dart';
...@@ -223,40 +223,22 @@ abstract class DevFSOperations { ...@@ -223,40 +223,22 @@ abstract class DevFSOperations {
class ServiceProtocolDevFSOperations implements DevFSOperations { class ServiceProtocolDevFSOperations implements DevFSOperations {
ServiceProtocolDevFSOperations(this.vmService); ServiceProtocolDevFSOperations(this.vmService);
final VMService vmService; final vm_service.VmService vmService;
@override @override
Future<Uri> create(String fsName) async { Future<Uri> create(String fsName) async {
final Map<String, dynamic> response = await vmService.vm.createDevFS(fsName); final vm_service.Response response = await vmService.createDevFS(fsName);
return Uri.parse(response['uri'] as String); return Uri.parse(response.json['uri'] as String);
} }
@override @override
Future<dynamic> destroy(String fsName) async { Future<dynamic> destroy(String fsName) async {
await vmService.vm.deleteDevFS(fsName); await vmService.deleteDevFS(fsName);
} }
@override @override
Future<dynamic> writeFile(String fsName, Uri deviceUri, DevFSContent content) async { Future<dynamic> writeFile(String fsName, Uri deviceUri, DevFSContent content) async {
List<int> bytes; throw UnsupportedError('Use the HTTP devFS api.');
try {
bytes = await content.contentsAsBytes();
} on Exception catch (e) {
return e;
}
final String fileContents = base64.encode(bytes);
try {
return await vmService.vm.invokeRpcRaw(
'_writeDevFSFile',
params: <String, dynamic>{
'fsName': fsName,
'uri': deviceUri.toString(),
'fileContents': fileContents,
},
);
} on Exception catch (error) {
globals.printTrace('DevFS: Failed to write $deviceUri: $error');
}
} }
} }
...@@ -270,7 +252,7 @@ class DevFSException implements Exception { ...@@ -270,7 +252,7 @@ class DevFSException implements Exception {
class _DevFSHttpWriter { class _DevFSHttpWriter {
_DevFSHttpWriter( _DevFSHttpWriter(
this.fsName, this.fsName,
VMService serviceProtocol, { vm_service.VmService serviceProtocol, {
@required OperatingSystemUtils osUtils, @required OperatingSystemUtils osUtils,
}) })
: httpAddress = serviceProtocol.httpAddress, : httpAddress = serviceProtocol.httpAddress,
...@@ -392,22 +374,25 @@ class UpdateFSReport { ...@@ -392,22 +374,25 @@ class UpdateFSReport {
class DevFS { class DevFS {
/// Create a [DevFS] named [fsName] for the local files in [rootDirectory]. /// Create a [DevFS] named [fsName] for the local files in [rootDirectory].
DevFS( DevFS(
VMService serviceProtocol, vm_service.VmService serviceProtocol,
this.fsName, this.fsName,
this.rootDirectory, { this.rootDirectory, {
@required OperatingSystemUtils osUtils, @required OperatingSystemUtils osUtils,
@visibleForTesting bool disableUpload = false,
}) : _operations = ServiceProtocolDevFSOperations(serviceProtocol), }) : _operations = ServiceProtocolDevFSOperations(serviceProtocol),
_httpWriter = _DevFSHttpWriter( _httpWriter = _DevFSHttpWriter(
fsName, fsName,
serviceProtocol, serviceProtocol,
osUtils: osUtils, osUtils: osUtils,
); ),
_disableUpload = disableUpload;
DevFS.operations( DevFS.operations(
this._operations, this._operations,
this.fsName, this.fsName,
this.rootDirectory, this.rootDirectory,
) : _httpWriter = null; ) : _httpWriter = null,
_disableUpload = false;
final DevFSOperations _operations; final DevFSOperations _operations;
final _DevFSHttpWriter _httpWriter; final _DevFSHttpWriter _httpWriter;
...@@ -417,6 +402,7 @@ class DevFS { ...@@ -417,6 +402,7 @@ class DevFS {
List<Uri> sources = <Uri>[]; List<Uri> sources = <Uri>[];
DateTime lastCompiled; DateTime lastCompiled;
PackageConfig lastPackageConfig; PackageConfig lastPackageConfig;
final bool _disableUpload;
Uri _baseUri; Uri _baseUri;
Uri get baseUri => _baseUri; Uri get baseUri => _baseUri;
...@@ -435,7 +421,7 @@ class DevFS { ...@@ -435,7 +421,7 @@ class DevFS {
globals.printTrace('DevFS: Creating new filesystem on the device ($_baseUri)'); globals.printTrace('DevFS: Creating new filesystem on the device ($_baseUri)');
try { try {
_baseUri = await _operations.create(fsName); _baseUri = await _operations.create(fsName);
} on vmservice.RPCError catch (rpcException) { } on vm_service.RPCError catch (rpcException) {
// 1001 is kFileSystemAlreadyExists in //dart/runtime/vm/json_stream.h // 1001 is kFileSystemAlreadyExists in //dart/runtime/vm/json_stream.h
if (rpcException.code != 1001) { if (rpcException.code != 1001) {
rethrow; rethrow;
...@@ -542,7 +528,9 @@ class DevFS { ...@@ -542,7 +528,9 @@ class DevFS {
globals.printTrace('Updating files'); globals.printTrace('Updating files');
if (dirtyEntries.isNotEmpty) { if (dirtyEntries.isNotEmpty) {
try { try {
await _httpWriter.write(dirtyEntries); if (!_disableUpload) {
await _httpWriter.write(dirtyEntries);
}
} on SocketException catch (socketException, stackTrace) { } on SocketException catch (socketException, stackTrace) {
globals.printTrace('DevFS sync failed. Lost connection to device: $socketException'); globals.printTrace('DevFS sync failed. Lost connection to device: $socketException');
throw DevFSException('Lost connection to device.', socketException, stackTrace); throw DevFSException('Lost connection to device.', socketException, stackTrace);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import '../application_package.dart'; import '../application_package.dart';
import '../artifacts.dart'; import '../artifacts.dart';
...@@ -46,8 +47,8 @@ final String _ipv4Loopback = InternetAddress.loopbackIPv4.address; ...@@ -46,8 +47,8 @@ final String _ipv4Loopback = InternetAddress.loopbackIPv4.address;
final String _ipv6Loopback = InternetAddress.loopbackIPv6.address; final String _ipv6Loopback = InternetAddress.loopbackIPv6.address;
// Enables testing the fuchsia isolate discovery // Enables testing the fuchsia isolate discovery
Future<VMService> _kDefaultFuchsiaIsolateDiscoveryConnector(Uri uri) { Future<vm_service.VmService> _kDefaultFuchsiaIsolateDiscoveryConnector(Uri uri) {
return VMService.connect(uri); return connectToVmService(uri);
} }
/// Read the log for a particular device. /// Read the log for a particular device.
...@@ -619,15 +620,14 @@ class FuchsiaDevice extends Device { ...@@ -619,15 +620,14 @@ class FuchsiaDevice extends Device {
// netstat shows that the local port is actually being used on the IPv6 // netstat shows that the local port is actually being used on the IPv6
// loopback (::1). // loopback (::1).
final Uri uri = Uri.parse('http://[$_ipv6Loopback]:$port'); final Uri uri = Uri.parse('http://[$_ipv6Loopback]:$port');
final VMService vmService = await VMService.connect(uri); final vm_service.VmService vmService = await connectToVmService(uri);
final List<FlutterView> flutterViews = await vmService.getFlutterViews(); final List<FlutterView> flutterViews = await vmService.getFlutterViews();
for (final FlutterView flutterView in flutterViews) { for (final FlutterView flutterView in flutterViews) {
if (flutterView.uiIsolate == null) { if (flutterView.uiIsolate == null) {
continue; continue;
} }
final Uri address = vmService.httpAddress;
if (flutterView.uiIsolate.name.contains(isolateName)) { if (flutterView.uiIsolate.name.contains(isolateName)) {
return address.port; return vmService.httpAddress.port;
} }
} }
} on SocketException catch (err) { } on SocketException catch (err) {
...@@ -662,11 +662,11 @@ class FuchsiaIsolateDiscoveryProtocol { ...@@ -662,11 +662,11 @@ class FuchsiaIsolateDiscoveryProtocol {
]); ]);
static const Duration _pollDuration = Duration(seconds: 10); static const Duration _pollDuration = Duration(seconds: 10);
final Map<int, VMService> _ports = <int, VMService>{}; final Map<int, vm_service.VmService> _ports = <int, vm_service.VmService>{};
final FuchsiaDevice _device; final FuchsiaDevice _device;
final String _isolateName; final String _isolateName;
final Completer<Uri> _foundUri = Completer<Uri>(); final Completer<Uri> _foundUri = Completer<Uri>();
final Future<VMService> Function(Uri) _vmServiceConnector; final Future<vm_service.VmService> Function(Uri) _vmServiceConnector;
// whether to only poll once. // whether to only poll once.
final bool _pollOnce; final bool _pollOnce;
Timer _pollingTimer; Timer _pollingTimer;
...@@ -702,7 +702,7 @@ class FuchsiaIsolateDiscoveryProtocol { ...@@ -702,7 +702,7 @@ class FuchsiaIsolateDiscoveryProtocol {
Future<void> _findIsolate() async { Future<void> _findIsolate() async {
final List<int> ports = await _device.servicePorts(); final List<int> ports = await _device.servicePorts();
for (final int port in ports) { for (final int port in ports) {
VMService service; vm_service.VmService service;
if (_ports.containsKey(port)) { if (_ports.containsKey(port)) {
service = _ports[port]; service = _ports[port];
} else { } else {
...@@ -721,11 +721,10 @@ class FuchsiaIsolateDiscoveryProtocol { ...@@ -721,11 +721,10 @@ class FuchsiaIsolateDiscoveryProtocol {
if (flutterView.uiIsolate == null) { if (flutterView.uiIsolate == null) {
continue; continue;
} }
final Uri address = service.httpAddress;
if (flutterView.uiIsolate.name.contains(_isolateName)) { if (flutterView.uiIsolate.name.contains(_isolateName)) {
_foundUri.complete(_device.ipv6 _foundUri.complete(_device.ipv6
? Uri.parse('http://[$_ipv6Loopback]:${address.port}/') ? Uri.parse('http://[$_ipv6Loopback]:${service.httpAddress.port}/')
: Uri.parse('http://$_ipv4Loopback:${address.port}/')); : Uri.parse('http://$_ipv4Loopback:${service.httpAddress.port}/'));
_status.stop(); _status.stop();
return; return;
} }
......
...@@ -187,10 +187,10 @@ class FlutterDevice { ...@@ -187,10 +187,10 @@ class FlutterDevice {
// FYI, this message is used as a sentinel in tests. // FYI, this message is used as a sentinel in tests.
globals.printTrace('Connecting to service protocol: $observatoryUri'); globals.printTrace('Connecting to service protocol: $observatoryUri');
isWaitingForVm = true; isWaitingForVm = true;
VMService service; vm_service.VmService service;
try { try {
service = await VMService.connect( service = await connectToVmService(
observatoryUri, observatoryUri,
reloadSources: reloadSources, reloadSources: reloadSources,
restart: restart, restart: restart,
...@@ -226,9 +226,6 @@ class FlutterDevice { ...@@ -226,9 +226,6 @@ class FlutterDevice {
return completer.future; return completer.future;
} }
// TODO(jonahwilliams): remove once all callsites are updated.
VMService get flutterDeprecatedVmService => vmService as VMService;
Future<void> refreshViews() async { Future<void> refreshViews() async {
if (vmService == null) { if (vmService == null) {
return; return;
...@@ -254,8 +251,6 @@ class FlutterDevice { ...@@ -254,8 +251,6 @@ class FlutterDevice {
return _views; return _views;
} }
Future<void> getVMs() => flutterDeprecatedVmService.getVMOld();
Future<void> exitApps() async { Future<void> exitApps() async {
if (!device.supportsFlutterExit) { if (!device.supportsFlutterExit) {
await device.stopApp(package); await device.stopApp(package);
...@@ -308,7 +303,7 @@ class FlutterDevice { ...@@ -308,7 +303,7 @@ class FlutterDevice {
}) { }) {
// One devFS per device. Shared by all running instances. // One devFS per device. Shared by all running instances.
devFS = DevFS( devFS = DevFS(
flutterDeprecatedVmService, vmService,
fsName, fsName,
rootDirectory, rootDirectory,
osUtils: globals.os, osUtils: globals.os,
...@@ -316,16 +311,17 @@ class FlutterDevice { ...@@ -316,16 +311,17 @@ class FlutterDevice {
return devFS.create(); return devFS.create();
} }
List<Future<vm_service.ReloadReport>> reloadSources( Future<List<Future<vm_service.ReloadReport>>> reloadSources(
String entryPath, { String entryPath, {
bool pause = false, bool pause = false,
}) { }) async {
final String deviceEntryUri = devFS.baseUri final String deviceEntryUri = devFS.baseUri
.resolveUri(globals.fs.path.toUri(entryPath)).toString(); .resolveUri(globals.fs.path.toUri(entryPath)).toString();
final vm_service.VM vm = await vmService.getVM();
return <Future<vm_service.ReloadReport>>[ return <Future<vm_service.ReloadReport>>[
for (final Isolate isolate in flutterDeprecatedVmService.vm.isolates) for (final vm_service.IsolateRef isolateRef in vm.isolates)
vmService.reloadSources( vmService.reloadSources(
isolate.id, isolateRef.id,
pause: pause, pause: pause,
rootLibUri: deviceEntryUri, rootLibUri: deviceEntryUri,
) )
...@@ -867,7 +863,7 @@ abstract class ResidentRunner { ...@@ -867,7 +863,7 @@ abstract class ResidentRunner {
void writeVmserviceFile() { void writeVmserviceFile() {
if (debuggingOptions.vmserviceOutFile != null) { if (debuggingOptions.vmserviceOutFile != null) {
try { try {
final String address = flutterDevices.first.flutterDeprecatedVmService.wsAddress.toString(); final String address = flutterDevices.first.vmService.wsAddress.toString();
final File vmserviceOutFile = globals.fs.file(debuggingOptions.vmserviceOutFile); final File vmserviceOutFile = globals.fs.file(debuggingOptions.vmserviceOutFile);
vmserviceOutFile.createSync(recursive: true); vmserviceOutFile.createSync(recursive: true);
vmserviceOutFile.writeAsStringSync(address); vmserviceOutFile.writeAsStringSync(address);
...@@ -899,13 +895,6 @@ abstract class ResidentRunner { ...@@ -899,13 +895,6 @@ abstract class ResidentRunner {
await Future.wait(futures); await Future.wait(futures);
} }
Future<void> refreshVM() async {
final List<Future<void>> futures = <Future<void>>[
for (final FlutterDevice device in flutterDevices) device.getVMs(),
];
await Future.wait(futures);
}
Future<void> debugDumpApp() async { Future<void> debugDumpApp() async {
await refreshViews(); await refreshViews();
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
...@@ -1086,7 +1075,6 @@ abstract class ResidentRunner { ...@@ -1086,7 +1075,6 @@ abstract class ResidentRunner {
compileExpression: compileExpression, compileExpression: compileExpression,
reloadMethod: reloadMethod, reloadMethod: reloadMethod,
); );
await device.getVMs();
await device.refreshViews(); await device.refreshViews();
if (device.views.isNotEmpty) { if (device.views.isNotEmpty) {
viewFound = true; viewFound = true;
...@@ -1122,7 +1110,7 @@ abstract class ResidentRunner { ...@@ -1122,7 +1110,7 @@ abstract class ResidentRunner {
<String, dynamic>{ <String, dynamic>{
'reuseWindows': true, 'reuseWindows': true,
}, },
flutterDevices.first.flutterDeprecatedVmService.httpAddress, flutterDevices.first.vmService.httpAddress,
'http://${_devtoolsServer.address.host}:${_devtoolsServer.port}', 'http://${_devtoolsServer.address.host}:${_devtoolsServer.port}',
false, // headless mode, false, // headless mode,
false, // machine mode false, // machine mode
......
...@@ -83,8 +83,8 @@ class ColdRunner extends ResidentRunner { ...@@ -83,8 +83,8 @@ class ColdRunner extends ResidentRunner {
if (flutterDevices.first.observatoryUris != null) { if (flutterDevices.first.observatoryUris != null) {
// For now, only support one debugger connection. // For now, only support one debugger connection.
connectionInfoCompleter?.complete(DebugConnectionInfo( connectionInfoCompleter?.complete(DebugConnectionInfo(
httpUri: flutterDevices.first.flutterDeprecatedVmService.httpAddress, httpUri: flutterDevices.first.vmService.httpAddress,
wsUri: flutterDevices.first.flutterDeprecatedVmService.wsAddress, wsUri: flutterDevices.first.vmService.wsAddress,
)); ));
} }
...@@ -105,7 +105,7 @@ class ColdRunner extends ResidentRunner { ...@@ -105,7 +105,7 @@ class ColdRunner extends ResidentRunner {
if (device.vmService != null) { if (device.vmService != null) {
globals.printStatus('Tracing startup on ${device.device.name}.'); globals.printStatus('Tracing startup on ${device.device.name}.');
await downloadStartupTrace( await downloadStartupTrace(
device.flutterDeprecatedVmService, device.vmService,
awaitFirstFrame: awaitFirstFrameWhenTracing, awaitFirstFrame: awaitFirstFrameWhenTracing,
); );
} }
...@@ -197,7 +197,7 @@ class ColdRunner extends ResidentRunner { ...@@ -197,7 +197,7 @@ class ColdRunner extends ResidentRunner {
// Caution: This log line is parsed by device lab tests. // Caution: This log line is parsed by device lab tests.
globals.printStatus( globals.printStatus(
'An Observatory debugger and profiler on $dname is available at: ' 'An Observatory debugger and profiler on $dname is available at: '
'${device.flutterDeprecatedVmService.httpAddress}', '${device.vmService.httpAddress}',
); );
} }
} }
......
...@@ -186,7 +186,7 @@ class HotRunner extends ResidentRunner { ...@@ -186,7 +186,7 @@ class HotRunner extends ResidentRunner {
from: projectRootPath, from: projectRootPath,
); );
for (final FlutterDevice device in flutterDevices) { for (final FlutterDevice device in flutterDevices) {
final List<Future<vm_service.ReloadReport>> reportFutures = device.reloadSources( final List<Future<vm_service.ReloadReport>> reportFutures = await device.reloadSources(
entryPath, pause: false, entryPath, pause: false,
); );
final List<vm_service.ReloadReport> reports = await Future.wait(reportFutures); final List<vm_service.ReloadReport> reports = await Future.wait(reportFutures);
...@@ -257,8 +257,8 @@ class HotRunner extends ResidentRunner { ...@@ -257,8 +257,8 @@ class HotRunner extends ResidentRunner {
// Only handle one debugger connection. // Only handle one debugger connection.
connectionInfoCompleter.complete( connectionInfoCompleter.complete(
DebugConnectionInfo( DebugConnectionInfo(
httpUri: flutterDevices.first.flutterDeprecatedVmService.httpAddress, httpUri: flutterDevices.first.vmService.httpAddress,
wsUri: flutterDevices.first.flutterDeprecatedVmService.wsAddress, wsUri: flutterDevices.first.vmService.wsAddress,
baseUri: baseUris.first.toString(), baseUri: baseUris.first.toString(),
), ),
); );
...@@ -820,7 +820,6 @@ class HotRunner extends ResidentRunner { ...@@ -820,7 +820,6 @@ class HotRunner extends ResidentRunner {
final Stopwatch reloadTimer = Stopwatch()..start(); final Stopwatch reloadTimer = Stopwatch()..start();
globals.printTrace('Refreshing active FlutterViews before reloading.'); globals.printTrace('Refreshing active FlutterViews before reloading.');
await refreshVM();
await refreshViews(); await refreshViews();
final Stopwatch devFSTimer = Stopwatch()..start(); final Stopwatch devFSTimer = Stopwatch()..start();
...@@ -847,7 +846,7 @@ class HotRunner extends ResidentRunner { ...@@ -847,7 +846,7 @@ class HotRunner extends ResidentRunner {
await device.resetAssetDirectory(); await device.resetAssetDirectory();
_shouldResetAssetDirectory = false; _shouldResetAssetDirectory = false;
} }
final List<Future<vm_service.ReloadReport>> reportFutures = device.reloadSources( final List<Future<vm_service.ReloadReport>> reportFutures = await device.reloadSources(
entryPath, pause: pause, entryPath, pause: pause,
); );
allReportsFutures.add(Future.wait(reportFutures).then( allReportsFutures.add(Future.wait(reportFutures).then(
...@@ -1117,7 +1116,7 @@ class HotRunner extends ResidentRunner { ...@@ -1117,7 +1116,7 @@ class HotRunner extends ResidentRunner {
// Caution: This log line is parsed by device lab tests. // Caution: This log line is parsed by device lab tests.
globals.printStatus( globals.printStatus(
'An Observatory debugger and profiler on $dname is available at: ' 'An Observatory debugger and profiler on $dname is available at: '
'${device.flutterDeprecatedVmService.httpAddress}', '${device.vmService.httpAddress}',
); );
} }
} }
......
...@@ -185,29 +185,27 @@ class CoverageCollector extends TestWatcher { ...@@ -185,29 +185,27 @@ class CoverageCollector extends TestWatcher {
Future<void> handleTestTimedOut(ProcessEvent event) async { } Future<void> handleTestTimedOut(ProcessEvent event) async { }
} }
Future<VMService> _defaultConnect(Uri serviceUri) { Future<vm_service.VmService> _defaultConnect(Uri serviceUri) {
return VMService.connect( return connectToVmService(
serviceUri, compression: CompressionOptions.compressionOff); serviceUri, compression: CompressionOptions.compressionOff);
} }
Future<Map<String, dynamic>> collect(Uri serviceUri, bool Function(String) libraryPredicate, { Future<Map<String, dynamic>> collect(Uri serviceUri, bool Function(String) libraryPredicate, {
bool waitPaused = false, bool waitPaused = false,
String debugName, String debugName,
Future<VMService> Function(Uri) connector = _defaultConnect, Future<vm_service.VmService> Function(Uri) connector = _defaultConnect,
}) async { }) async {
final VMService vmService = await connector(serviceUri); final vm_service.VmService vmService = await connector(serviceUri);
await vmService.getVMOld();
final Map<String, dynamic> result = await _getAllCoverage( final Map<String, dynamic> result = await _getAllCoverage(
vmService, libraryPredicate); vmService, libraryPredicate);
await vmService.close(); vmService.dispose();
return result; return result;
} }
Future<Map<String, dynamic>> _getAllCoverage(VMService service, bool Function(String) libraryPredicate) async { Future<Map<String, dynamic>> _getAllCoverage(vm_service.VmService service, bool Function(String) libraryPredicate) async {
await service.getVMOld(); final vm_service.VM vm = await service.getVM();
final List<Map<String, dynamic>> coverage = <Map<String, dynamic>>[]; final List<Map<String, dynamic>> coverage = <Map<String, dynamic>>[];
for (final Isolate isolateRef in service.vm.isolates) { for (final vm_service.IsolateRef isolateRef in vm.isolates) {
await isolateRef.load();
Map<String, Object> scriptList; Map<String, Object> scriptList;
try { try {
final vm_service.ScriptList actualScriptList = await service.getScripts(isolateRef.id); final vm_service.ScriptList actualScriptList = await service.getScripts(isolateRef.id);
...@@ -228,24 +226,22 @@ Future<Map<String, dynamic>> _getAllCoverage(VMService service, bool Function(St ...@@ -228,24 +226,22 @@ Future<Map<String, dynamic>> _getAllCoverage(VMService service, bool Function(St
} }
final String scriptId = script['id'] as String; final String scriptId = script['id'] as String;
futures.add( futures.add(
isolateRef.invokeRpcRaw('getSourceReport', params: <String, dynamic>{ service.getSourceReport(
'forceCompile': true, isolateRef.id,
'scriptId': scriptId, <String>['Coverage'],
'isolateId': isolateRef.id, scriptId: scriptId,
'reports': <String>['Coverage'], forceCompile: true,
}) )
.then((Map<String, dynamic> report) { .then((vm_service.SourceReport report) {
sourceReports[scriptId] = report; sourceReports[scriptId] = report.json;
}) })
); );
futures.add( futures.add(
isolateRef.invokeRpcRaw('getObject', params: <String, dynamic>{ service
'isolateId': isolateRef.id, .getObject(isolateRef.id, scriptId)
'objectId': scriptId, .then((vm_service.Obj script) {
}) scripts[scriptId] = script.json;
.then((Map<String, dynamic> script) { })
scripts[scriptId] = script;
})
); );
} }
await Future.wait(futures); await Future.wait(futures);
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:stream_channel/stream_channel.dart'; import 'package:stream_channel/stream_channel.dart';
import 'package:vm_service/vm_service.dart' as vm_service;
import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports import 'package:test_api/src/backend/suite_platform.dart'; // ignore: implementation_imports
import 'package:test_core/src/runner/runner_suite.dart'; // ignore: implementation_imports import 'package:test_core/src/runner/runner_suite.dart'; // ignore: implementation_imports
...@@ -521,9 +522,9 @@ class FlutterPlatform extends PlatformPlugin { ...@@ -521,9 +522,9 @@ class FlutterPlatform extends PlatformPlugin {
processObservatoryUri = detectedUri; processObservatoryUri = detectedUri;
{ {
globals.printTrace('Connecting to service protocol: $processObservatoryUri'); globals.printTrace('Connecting to service protocol: $processObservatoryUri');
final Future<VMService> localVmService = VMService.connect(processObservatoryUri, final Future<vm_service.VmService> localVmService = connectToVmService(processObservatoryUri,
compileExpression: _compileExpressionService); compileExpression: _compileExpressionService);
localVmService.then((VMService vmservice) { localVmService.then((vm_service.VmService vmservice) {
globals.printTrace('Successfully connected to service protocol: $processObservatoryUri'); globals.printTrace('Successfully connected to service protocol: $processObservatoryUri');
}); });
} }
......
...@@ -24,15 +24,15 @@ class Tracing { ...@@ -24,15 +24,15 @@ class Tracing {
static const String firstUsefulFrameEventName = kFirstFrameRasterizedEventName; static const String firstUsefulFrameEventName = kFirstFrameRasterizedEventName;
static Future<Tracing> connect(Uri uri) async { static Future<Tracing> connect(Uri uri) async {
final VMService observatory = await VMService.connect(uri); final vm_service.VmService observatory = await connectToVmService(uri);
return Tracing(observatory); return Tracing(observatory);
} }
final VMService vmService; final vm_service.VmService vmService;
Future<void> startTracing() async { Future<void> startTracing() async {
await vmService.vm.setVMTimelineFlags(<String>['Compiler', 'Dart', 'Embedder', 'GC']); await vmService.setVMTimelineFlags(<String>['Compiler', 'Dart', 'Embedder', 'GC']);
await vmService.vm.clearVMTimeline(); await vmService.clearVMTimeline();
} }
/// Stops tracing; optionally wait for first frame. /// Stops tracing; optionally wait for first frame.
...@@ -73,15 +73,15 @@ class Tracing { ...@@ -73,15 +73,15 @@ class Tracing {
} }
status.stop(); status.stop();
} }
final Map<String, dynamic> timeline = await vmService.vm.getVMTimeline(); final vm_service.Timeline timeline = await vmService.getVMTimeline();
await vmService.vm.setVMTimelineFlags(<String>[]); await vmService.setVMTimelineFlags(<String>[]);
return timeline; return timeline.json;
} }
} }
/// Download the startup trace information from the given observatory client and /// Download the startup trace information from the given observatory client and
/// store it to build/start_up_info.json. /// store it to build/start_up_info.json.
Future<void> downloadStartupTrace(VMService observatory, { bool awaitFirstFrame = true }) async { Future<void> downloadStartupTrace(vm_service.VmService vmService, { bool awaitFirstFrame = true }) async {
final String traceInfoFilePath = globals.fs.path.join(getBuildDirectory(), 'start_up_info.json'); final String traceInfoFilePath = globals.fs.path.join(getBuildDirectory(), 'start_up_info.json');
final File traceInfoFile = globals.fs.file(traceInfoFilePath); final File traceInfoFile = globals.fs.file(traceInfoFilePath);
...@@ -95,7 +95,7 @@ Future<void> downloadStartupTrace(VMService observatory, { bool awaitFirstFrame ...@@ -95,7 +95,7 @@ Future<void> downloadStartupTrace(VMService observatory, { bool awaitFirstFrame
traceInfoFile.parent.createSync(); traceInfoFile.parent.createSync();
} }
final Tracing tracing = Tracing(observatory); final Tracing tracing = Tracing(vmService);
final Map<String, dynamic> timeline = await tracing.stopTracingAndDownloadTimeline( final Map<String, dynamic> timeline = await tracing.stopTracingAndDownloadTimeline(
awaitFirstFrame: awaitFirstFrame, awaitFirstFrame: awaitFirstFrame,
......
...@@ -4,13 +4,11 @@ ...@@ -4,13 +4,11 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart' show required; import 'package:meta/meta.dart' show required, visibleForTesting;
import 'package:vm_service/vm_service.dart' as vm_service; import 'package:vm_service/vm_service.dart' as vm_service;
import 'base/context.dart'; import 'base/context.dart';
import 'base/io.dart' as io; import 'base/io.dart' as io;
import 'base/utils.dart';
import 'convert.dart' show base64, json, utf8;
import 'device.dart'; import 'device.dart';
import 'globals.dart' as globals; import 'globals.dart' as globals;
import 'version.dart'; import 'version.dart';
...@@ -20,6 +18,8 @@ const String kSetAssetBundlePathMethod = '_flutter.setAssetBundlePath'; ...@@ -20,6 +18,8 @@ const String kSetAssetBundlePathMethod = '_flutter.setAssetBundlePath';
const String kFlushUIThreadTasksMethod = '_flutter.flushUIThreadTasks'; const String kFlushUIThreadTasksMethod = '_flutter.flushUIThreadTasks';
const String kRunInViewMethod = '_flutter.runInView'; const String kRunInViewMethod = '_flutter.runInView';
const String kListViewsMethod = '_flutter.listViews'; const String kListViewsMethod = '_flutter.listViews';
const String kScreenshotSkpMethod = '_flutter.screenshotSkp';
const String kScreenshotMethod = '_flutter.screenshot';
/// The error response code from an unrecoverable compilation failure. /// The error response code from an unrecoverable compilation failure.
const int kIsolateReloadBarred = 1005; const int kIsolateReloadBarred = 1005;
...@@ -121,7 +121,7 @@ Future<io.WebSocket> _defaultOpenChannel(String url, { ...@@ -121,7 +121,7 @@ Future<io.WebSocket> _defaultOpenChannel(String url, {
/// Override `VMServiceConnector` in [context] to return a different VMService /// Override `VMServiceConnector` in [context] to return a different VMService
/// from [VMService.connect] (used by tests). /// from [VMService.connect] (used by tests).
typedef VMServiceConnector = Future<VMService> Function(Uri httpUri, { typedef VMServiceConnector = Future<vm_service.VmService> Function(Uri httpUri, {
ReloadSources reloadSources, ReloadSources reloadSources,
Restart restart, Restart restart,
CompileExpression compileExpression, CompileExpression compileExpression,
...@@ -130,1076 +130,275 @@ typedef VMServiceConnector = Future<VMService> Function(Uri httpUri, { ...@@ -130,1076 +130,275 @@ typedef VMServiceConnector = Future<VMService> Function(Uri httpUri, {
Device device, Device device,
}); });
/// A connection to the Dart VM Service. final Expando<Uri> _httpAddressExpando = Expando<Uri>();
///
/// This also implements the package:vm_service API to enable a gradual migration.
class VMService implements vm_service.VmService {
VMService(
this.httpAddress,
this.wsAddress,
ReloadSources reloadSources,
Restart restart,
CompileExpression compileExpression,
Device device,
ReloadMethod reloadMethod,
this._delegateService,
this.streamClosedCompleter,
Stream<dynamic> secondary,
) {
_vm = VM._empty(this);
// TODO(jonahwilliams): this is temporary to support the current vm_service
// semantics of update-in-place.
secondary.listen((dynamic rawData) {
final String message = rawData as String;
final dynamic map = json.decode(message);
if (map != null && map['method'] == 'streamNotify') {
_handleStreamNotify(map['params'] as Map<String, dynamic>);
}
});
if (reloadSources != null) { final Expando<Uri> _wsAddressExpando = Expando<Uri>();
_delegateService.registerServiceCallback('reloadSources', (Map<String, dynamic> params) async {
final String isolateId = params['isolateId'].value as String;
final bool force = params['force'] as bool ?? false;
final bool pause = params['pause'] as bool ?? false;
if (isolateId.isEmpty) {
throw vm_service.RPCError(
"Invalid 'isolateId': $isolateId",
RPCErrorCodes.kInvalidParams,
'',
);
}
try {
await reloadSources(isolateId, force: force, pause: pause);
return <String, String>{'type': 'Success'};
} on vm_service.RPCError {
rethrow;
} on Exception catch (e, st) {
throw vm_service.RPCError(
'Error during Sources Reload: $e\n$st',
RPCErrorCodes.kServerError,
'',
);
}
});
_delegateService.registerService('reloadSources', 'Flutter Tools');
}
if (reloadMethod != null) { @visibleForTesting
// Register a special method for hot UI. while this is implemented void setHttpAddress(Uri uri, vm_service.VmService vmService) {
// currently in the same way as hot reload, it leaves the tool free _httpAddressExpando[vmService] = uri;
// to change to a more efficient implementation in the future. }
//
// `library` should be the file URI of the updated code.
// `class` should be the name of the Widget subclass to be marked dirty. For example,
// if the build method of a StatelessWidget is updated, this is the name of class.
// If the build method of a StatefulWidget is updated, then this is the name
// of the Widget class that created the State object.
_delegateService.registerServiceCallback('reloadMethod', (Map<String, dynamic> params) async {
final String libraryId = params['library'] as String;
final String classId = params['class'] as String;
if (libraryId.isEmpty) {
throw vm_service.RPCError(
"Invalid 'libraryId': $libraryId",
RPCErrorCodes.kInvalidParams,
'',
);
}
if (classId.isEmpty) {
throw vm_service.RPCError(
"Invalid 'classId': $classId",
RPCErrorCodes.kInvalidParams,
'',
);
}
globals.printTrace('reloadMethod not yet supported, falling back to hot reload');
try {
await reloadMethod(
libraryId: libraryId,
classId: classId,
);
return <String, String>{'type': 'Success'};
} on vm_service.RPCError {
rethrow;
} on Exception catch (e, st) {
throw vm_service.RPCError('Error during Sources Reload: $e\n$st', -32000, '');
}
});
_delegateService.registerService('reloadMethod', 'Flutter Tools');
}
if (restart != null) { @visibleForTesting
_delegateService.registerServiceCallback('hotRestart', (Map<String, dynamic> params) async { void setWsAddress(Uri uri, vm_service.VmService vmService) {
final bool pause = params['pause'] as bool ?? false; _wsAddressExpando[vmService] = uri;
try { }
await restart(pause: pause);
return <String, String>{'type': 'Success'};
} on vm_service.RPCError {
rethrow;
} on Exception catch (e, st) {
throw vm_service.RPCError(
'Error during Hot Restart: $e\n$st',
RPCErrorCodes.kServerError,
'',
);
}
});
_delegateService.registerService('hotRestart', 'Flutter Tools');
}
_delegateService.registerServiceCallback('flutterVersion', (Map<String, dynamic> params) async { /// A connection to the Dart VM Service.
final FlutterVersion version = context.get<FlutterVersion>() ?? FlutterVersion(); vm_service.VmService setUpVmService(
final Map<String, Object> versionJson = version.toJson(); ReloadSources reloadSources,
versionJson['frameworkRevisionShort'] = version.frameworkRevisionShort; Restart restart,
versionJson['engineRevisionShort'] = version.engineRevisionShort; CompileExpression compileExpression,
return <String, dynamic>{ Device device,
'result': <String, Object>{ ReloadMethod reloadMethod,
'type': 'Success', vm_service.VmService vmService
...versionJson, ) {
} if (reloadSources != null) {
}; vmService.registerServiceCallback('reloadSources', (Map<String, dynamic> params) async {
final String isolateId = params['isolateId'].value as String;
final bool force = params['force'] as bool ?? false;
final bool pause = params['pause'] as bool ?? false;
if (isolateId.isEmpty) {
throw vm_service.RPCError(
"Invalid 'isolateId': $isolateId",
RPCErrorCodes.kInvalidParams,
'',
);
}
try {
await reloadSources(isolateId, force: force, pause: pause);
return <String, String>{'type': 'Success'};
} on vm_service.RPCError {
rethrow;
} on Exception catch (e, st) {
throw vm_service.RPCError(
'Error during Sources Reload: $e\n$st',
RPCErrorCodes.kServerError,
'',
);
}
}); });
_delegateService.registerService('flutterVersion', 'Flutter Tools'); vmService.registerService('reloadSources', 'Flutter Tools');
if (compileExpression != null) { }
_delegateService.registerServiceCallback('compileExpression', (Map<String, dynamic> params) async {
final String isolateId = params['isolateId'] as String; if (reloadMethod != null) {
if (isolateId is! String || isolateId.isEmpty) { // Register a special method for hot UI. while this is implemented
throw throw vm_service.RPCError( // currently in the same way as hot reload, it leaves the tool free
"Invalid 'isolateId': $isolateId", // to change to a more efficient implementation in the future.
RPCErrorCodes.kInvalidParams, //
'', // `library` should be the file URI of the updated code.
); // `class` should be the name of the Widget subclass to be marked dirty. For example,
} // if the build method of a StatelessWidget is updated, this is the name of class.
final String expression = params['expression'] as String; // If the build method of a StatefulWidget is updated, then this is the name
if (expression is! String || expression.isEmpty) { // of the Widget class that created the State object.
throw throw vm_service.RPCError( vmService.registerServiceCallback('reloadMethod', (Map<String, dynamic> params) async {
"Invalid 'expression': $expression", final String libraryId = params['library'] as String;
RPCErrorCodes.kInvalidParams, final String classId = params['class'] as String;
'',
); if (libraryId.isEmpty) {
} throw vm_service.RPCError(
final List<String> definitions = List<String>.from(params['definitions'] as List<dynamic>); "Invalid 'libraryId': $libraryId",
final List<String> typeDefinitions = List<String>.from(params['typeDefinitions'] as List<dynamic>); RPCErrorCodes.kInvalidParams,
final String libraryUri = params['libraryUri'] as String; '',
final String klass = params['klass'] as String; );
final bool isStatic = params['isStatic'] as bool ?? false; }
try { if (classId.isEmpty) {
final String kernelBytesBase64 = await compileExpression(isolateId, throw vm_service.RPCError(
expression, definitions, typeDefinitions, libraryUri, klass, "Invalid 'classId': $classId",
isStatic); RPCErrorCodes.kInvalidParams,
return <String, dynamic>{ '',
'type': 'Success', );
'result': <String, dynamic>{ }
'result': <String, dynamic>{'kernelBytes': kernelBytesBase64},
},
};
} on vm_service.RPCError {
rethrow;
} on Exception catch (e, st) {
throw vm_service.RPCError(
'Error during expression compilation: $e\n$st',
RPCErrorCodes.kServerError,
'',
);
}
});
_delegateService.registerService('compileExpression', 'Flutter Tools');
}
if (device != null) {
_delegateService.registerServiceCallback('flutterMemoryInfo', (Map<String, dynamic> params) async {
try {
final MemoryInfo result = await device.queryMemoryInfo();
return <String, dynamic>{
'result': <String, Object>{
'type': 'Success',
...result.toJson(),
}
};
} on Exception catch (e, st) {
throw vm_service.RPCError(
'Error during memory info query $e\n$st',
RPCErrorCodes.kServerError,
'',
);
}
});
_delegateService.registerService('flutterMemoryInfo', 'Flutter Tools');
}
}
/// Connect to a Dart VM Service at [httpUri]. globals.printTrace('reloadMethod not yet supported, falling back to hot reload');
///
/// If the [reloadSources] parameter is not null, the 'reloadSources' service
/// will be registered. The VM Service Protocol allows clients to register
/// custom services that can be invoked by other clients through the service
/// protocol itself.
///
/// See: https://github.com/dart-lang/sdk/commit/df8bf384eb815cf38450cb50a0f4b62230fba217
static Future<VMService> connect(
Uri httpUri, {
ReloadSources reloadSources,
Restart restart,
CompileExpression compileExpression,
ReloadMethod reloadMethod,
io.CompressionOptions compression = io.CompressionOptions.compressionDefault,
Device device,
}) async {
final VMServiceConnector connector = context.get<VMServiceConnector>() ?? VMService._connect;
return connector(httpUri,
reloadSources: reloadSources,
restart: restart,
compileExpression: compileExpression,
compression: compression,
device: device,
reloadMethod: reloadMethod,
);
}
static Future<VMService> _connect( try {
Uri httpUri, { await reloadMethod(
ReloadSources reloadSources, libraryId: libraryId,
Restart restart, classId: classId,
CompileExpression compileExpression, );
ReloadMethod reloadMethod, return <String, String>{'type': 'Success'};
io.CompressionOptions compression = io.CompressionOptions.compressionDefault, } on vm_service.RPCError {
Device device, rethrow;
}) async { } on Exception catch (e, st) {
final Uri wsUri = httpUri.replace(scheme: 'ws', path: globals.fs.path.join(httpUri.path, 'ws')); throw vm_service.RPCError('Error during Sources Reload: $e\n$st', -32000, '');
final io.WebSocket channel = await _openChannel(wsUri.toString(), compression: compression);
final StreamController<dynamic> primary = StreamController<dynamic>();
final StreamController<dynamic> secondary = StreamController<dynamic>();
// Create an instance of the package:vm_service API in addition to the flutter
// tool's to allow gradual migration.
final Completer<void> streamClosedCompleter = Completer<void>();
channel.listen((dynamic data) {
primary.add(data);
secondary.add(data);
}, onDone: () {
primary.close();
secondary.close();
if (!streamClosedCompleter.isCompleted) {
streamClosedCompleter.complete();
} }
}, onError: (dynamic error, StackTrace stackTrace) {
primary.addError(error, stackTrace);
secondary.addError(error, stackTrace);
}); });
final vm_service.VmService delegateService = vm_service.VmService( vmService.registerService('reloadMethod', 'Flutter Tools');
primary.stream,
channel.add,
log: null,
disposeHandler: () async {
if (!streamClosedCompleter.isCompleted) {
streamClosedCompleter.complete();
}
await channel.close();
},
);
final VMService service = VMService(
httpUri,
wsUri,
reloadSources,
restart,
compileExpression,
device,
reloadMethod,
delegateService,
streamClosedCompleter,
secondary.stream,
);
// This call is to ensure we are able to establish a connection instead of
// keeping on trucking and failing farther down the process.
await delegateService.getVersion();
return service;
}
final vm_service.VmService _delegateService;
final Uri httpAddress;
final Uri wsAddress;
final Completer<void> streamClosedCompleter;
VM _vm;
/// The singleton [VM] object. Owns [Isolate] and [FlutterView] objects.
VM get vm => _vm;
final Map<String, StreamController<ServiceEvent>> _eventControllers =
<String, StreamController<ServiceEvent>>{};
/// Whether our connection to the VM service has been closed;
bool get isClosed => streamClosedCompleter.isCompleted;
Future<void> get done async {
return streamClosedCompleter.future;
}
@override
Stream<vm_service.Event> get onDebugEvent => onEvent('Debug');
@override
Stream<vm_service.Event> get onExtensionEvent => onEvent('Extension');
@override
Stream<vm_service.Event> get onIsolateEvent => onEvent('Isolate');
@override
Stream<vm_service.Event> get onTimelineEvent => onEvent('Timeline');
@override
Stream<vm_service.Event> get onStdoutEvent => onEvent('Stdout');
@override
Future<vm_service.Success> streamListen(String streamId) {
return _delegateService.streamListen(streamId);
}
@override
Stream<vm_service.Event> onEvent(String streamId) {
return _delegateService.onEvent(streamId);
} }
@override if (restart != null) {
Future<vm_service.Response> callMethod(String method, { vmService.registerServiceCallback('hotRestart', (Map<String, dynamic> params) async {
String isolateId, final bool pause = params['pause'] as bool ?? false;
Map<dynamic, dynamic> args, try {
}) { await restart(pause: pause);
return _delegateService.callMethod(method, isolateId: isolateId, args: args); return <String, String>{'type': 'Success'};
} } on vm_service.RPCError {
rethrow;
@override } on Exception catch (e, st) {
Future<void> get onDone => _delegateService.onDone; throw vm_service.RPCError(
'Error during Hot Restart: $e\n$st',
@override RPCErrorCodes.kServerError,
Future<vm_service.Response> callServiceExtension(String method, '',
{String isolateId, Map<Object, Object> args}) { );
return _delegateService.callServiceExtension(method, isolateId: isolateId, args: args); }
} });
vmService.registerService('hotRestart', 'Flutter Tools');
@override }
Future<vm_service.VM> getVM() => _delegateService.getVM();
vmService.registerServiceCallback('flutterVersion', (Map<String, dynamic> params) async {
StreamController<ServiceEvent> _getEventController(String eventName) { final FlutterVersion version = context.get<FlutterVersion>() ?? FlutterVersion();
StreamController<ServiceEvent> controller = _eventControllers[eventName]; final Map<String, Object> versionJson = version.toJson();
if (controller == null) { versionJson['frameworkRevisionShort'] = version.frameworkRevisionShort;
controller = StreamController<ServiceEvent>.broadcast(); versionJson['engineRevisionShort'] = version.engineRevisionShort;
_eventControllers[eventName] = controller; return <String, dynamic>{
} 'result': <String, Object>{
return controller; 'type': 'Success',
} ...versionJson,
void _handleStreamNotify(Map<String, dynamic> data) {
final String streamId = data['streamId'] as String;
final Map<String, dynamic> eventData = castStringKeyedMap(data['event']);
final Map<String, dynamic> eventIsolate = castStringKeyedMap(eventData['isolate']);
// Log event information.
globals.printTrace('Notification from VM: $data');
ServiceEvent event;
if (eventIsolate != null) {
// getFromMap creates the Isolate if necessary.
final Isolate isolate = vm.getFromMap(eventIsolate) as Isolate;
event = ServiceObject._fromMap(isolate, eventData) as ServiceEvent;
if (event.kind == vm_service.EventKind.kIsolateExit) {
vm._isolateCache.remove(isolate.id);
vm._buildIsolateList();
} else if (event.kind == vm_service.EventKind.kIsolateRunnable) {
// Force reload once the isolate becomes runnable so that we
// update the root library.
isolate.reload();
} }
} else {
// The event doesn't have an isolate, so it is owned by the VM.
event = ServiceObject._fromMap(vm, eventData) as ServiceEvent;
}
_getEventController(streamId).add(event);
}
@override
Future<vm_service.ScriptList> getScripts(String isolateId) {
return _delegateService.getScripts(isolateId);
}
/// Reloads the VM.
Future<void> getVMOld() async => await vm.reload();
Future<void> close() async {
_delegateService?.dispose();
}
@override
Future<vm_service.ReloadReport> reloadSources(
String isolateId, {
bool force,
bool pause,
String rootLibUri,
String packagesUri,
}) {
return _delegateService.reloadSources(
isolateId,
force: force,
pause: pause,
rootLibUri: rootLibUri,
packagesUri: packagesUri,
);
}
@override
Future<vm_service.Isolate> getIsolate(String isolateId) {
return _delegateService.getIsolate(isolateId);
}
@override
Future<vm_service.Success> resume(String isolateId, {String step, int frameIndex}) {
return _delegateService.resume(isolateId, step: step, frameIndex: frameIndex);
}
@override
Future<vm_service.Success> kill(String isolateId) {
return _delegateService.kill(isolateId);
}
// To enable a gradual migration to package:vm_service
@override
dynamic noSuchMethod(Invocation invocation) {
throw UnsupportedError('${invocation.memberName} is not currently supported');
}
}
/// An error that is thrown when constructing/updating a service object.
class VMServiceObjectLoadError {
VMServiceObjectLoadError(this.message, this.map);
final String message;
final Map<String, dynamic> map;
}
bool _isServiceMap(Map<String, dynamic> m) {
return (m != null) && (m['type'] != null);
}
bool _hasRef(String type) => (type != null) && type.startsWith('@');
String _stripRef(String type) => _hasRef(type) ? type.substring(1) : type;
/// Given a raw response from the service protocol and a [ServiceObjectOwner],
/// recursively walk the response and replace values that are service maps with
/// actual [ServiceObject]s. During the upgrade the owner is given a chance
/// to return a cached / canonicalized object.
void _upgradeCollection(
dynamic collection,
ServiceObjectOwner owner,
) {
if (collection is ServiceMap) {
return;
}
if (collection is Map<String, dynamic>) {
_upgradeMap(collection, owner);
} else if (collection is List) {
_upgradeList(collection, owner);
}
}
void _upgradeMap(Map<String, dynamic> map, ServiceObjectOwner owner) {
map.forEach((String k, Object v) {
if ((v is Map<String, dynamic>) && _isServiceMap(v)) {
map[k] = owner.getFromMap(v);
} else if (v is List) {
_upgradeList(v, owner);
} else if (v is Map<String, dynamic>) {
_upgradeMap(v, owner);
}
});
}
void _upgradeList(List<dynamic> list, ServiceObjectOwner owner) {
for (int i = 0; i < list.length; i += 1) {
final Object v = list[i];
if ((v is Map<String, dynamic>) && _isServiceMap(v)) {
list[i] = owner.getFromMap(v);
} else if (v is List) {
_upgradeList(v, owner);
} else if (v is Map<String, dynamic>) {
_upgradeMap(v, owner);
}
}
}
/// Base class of all objects received over the service protocol.
abstract class ServiceObject {
ServiceObject._empty(this._owner);
/// Factory constructor given a [ServiceObjectOwner] and a service map,
/// upgrade the map into a proper [ServiceObject]. This function always
/// returns a new instance and does not interact with caches.
factory ServiceObject._fromMap(
ServiceObjectOwner owner,
Map<String, dynamic> map,
) {
if (map == null) {
return null;
}
if (!_isServiceMap(map)) {
throw VMServiceObjectLoadError('Expected a service map', map);
}
final String type = _stripRef(map['type'] as String);
ServiceObject serviceObject;
switch (type) {
case 'Event':
serviceObject = ServiceEvent._empty(owner);
break;
case 'Isolate':
serviceObject = Isolate._empty(owner.vm);
break;
}
// If we don't have a model object for this service object type, as a
// fallback return a ServiceMap object.
serviceObject ??= ServiceMap._empty(owner);
// We have now constructed an empty service object, call update to populate it.
serviceObject.updateFromMap(map);
return serviceObject;
}
final ServiceObjectOwner _owner;
ServiceObjectOwner get owner => _owner;
/// The id of this object.
String get id => _id;
String _id;
/// The user-level type of this object.
String get type => _type;
String _type;
/// The vm-level type of this object. Usually the same as [type].
String get vmType => _vmType;
String _vmType;
/// Is it safe to cache this object?
bool _canCache = false;
bool get canCache => _canCache;
/// Has this object been fully loaded?
bool get loaded => _loaded;
bool _loaded = false;
/// Is this object immutable after it is [loaded]?
bool get immutable => false;
String get name => _name;
String _name;
String get vmName => _vmName;
String _vmName;
/// If this is not already loaded, load it. Otherwise reload.
Future<ServiceObject> load() async {
if (loaded) {
return this;
}
return reload();
}
/// Fetch this object from vmService and return the response directly.
Future<Map<String, dynamic>> _fetchDirect() {
final Map<String, dynamic> params = <String, dynamic>{
'objectId': id,
}; };
return _owner.isolate.invokeRpcRaw('getObject', params: params); });
} vmService.registerService('flutterVersion', 'Flutter Tools');
Future<ServiceObject> _inProgressReload; if (compileExpression != null) {
/// Reload the service object (if possible). vmService.registerServiceCallback('compileExpression', (Map<String, dynamic> params) async {
Future<ServiceObject> reload() async { final String isolateId = params['isolateId'] as String;
final bool hasId = (id != null) && (id != ''); if (isolateId is! String || isolateId.isEmpty) {
final bool isVM = this is VM; throw throw vm_service.RPCError(
// We should always reload the VM. "Invalid 'isolateId': $isolateId",
// We can't reload objects without an id. RPCErrorCodes.kInvalidParams,
// We shouldn't reload an immutable and already loaded object. '',
if (!isVM && (!hasId || (immutable && loaded))) { );
return this;
}
if (_inProgressReload == null) {
final Completer<ServiceObject> completer = Completer<ServiceObject>();
_inProgressReload = completer.future;
try {
final Map<String, dynamic> response = await _fetchDirect();
if (_stripRef(response['type'] as String) == 'Sentinel') {
// An object may have been collected.
completer.complete(ServiceObject._fromMap(owner, response));
} else {
updateFromMap(response);
completer.complete(this);
}
// Catches all exceptions to propagate to the completer.
} catch (e, st) { // ignore: avoid_catches_without_on_clauses
completer.completeError(e, st);
} }
_inProgressReload = null; final String expression = params['expression'] as String;
return await completer.future; if (expression is! String || expression.isEmpty) {
} throw throw vm_service.RPCError(
"Invalid 'expression': $expression",
return await _inProgressReload; RPCErrorCodes.kInvalidParams,
} '',
);
/// Update [this] using [map] as a source. [map] can be a service reference.
void updateFromMap(Map<String, dynamic> map) {
// Don't allow the type to change on an object update.
final bool mapIsRef = _hasRef(map['type'] as String);
final String mapType = _stripRef(map['type'] as String);
if ((_type != null) && (_type != mapType)) {
throw VMServiceObjectLoadError('ServiceObject types must not change',
map);
}
_type = mapType;
_vmType = map.containsKey('_vmType') ? _stripRef(map['_vmType'] as String) : _type;
_canCache = map['fixedId'] == true;
if ((_id != null) && (_id != map['id']) && _canCache) {
throw VMServiceObjectLoadError('ServiceObject id changed', map);
}
_id = map['id'] as String;
// Copy name properties.
_name = map['name'] as String;
_vmName = map.containsKey('_vmName') ? map['_vmName'] as String : _name;
// We have now updated all common properties, let the subclasses update
// their specific properties.
_update(map, mapIsRef);
}
/// Implemented by subclasses to populate their model.
void _update(Map<String, dynamic> map, bool mapIsRef);
}
class ServiceEvent extends ServiceObject {
ServiceEvent._empty(ServiceObjectOwner owner) : super._empty(owner);
String _kind;
String get kind => _kind;
DateTime _timestamp;
DateTime get timestamp => _timestamp;
String _extensionKind;
String get extensionKind => _extensionKind;
Map<String, dynamic> _extensionData;
Map<String, dynamic> get extensionData => _extensionData;
List<Map<String, dynamic>> _timelineEvents;
List<Map<String, dynamic>> get timelineEvents => _timelineEvents;
String _message;
String get message => _message;
@override
void _update(Map<String, dynamic> map, bool mapIsRef) {
_loaded = true;
_upgradeCollection(map, owner);
_kind = map['kind'] as String;
assert(map['isolate'] == null || owner == map['isolate']);
_timestamp =
DateTime.fromMillisecondsSinceEpoch(map['timestamp'] as int);
if (map['extensionKind'] != null) {
_extensionKind = map['extensionKind'] as String;
_extensionData = castStringKeyedMap(map['extensionData']);
}
// map['timelineEvents'] is List<dynamic> which can't be assigned to
// List<Map<String, dynamic>> directly. Unfortunately, we previously didn't
// catch this exception because json_rpc_2 is hiding all these exceptions
// on a Stream.
final List<dynamic> dynamicList = map['timelineEvents'] as List<dynamic>;
_timelineEvents = dynamicList?.cast<Map<String, dynamic>>();
final String base64Bytes = map['bytes'] as String;
if (base64Bytes != null) {
_message = utf8.decode(base64.decode(base64Bytes)).trim();
}
}
}
/// A ServiceObjectOwner is either a [VM] or an [Isolate]. Owners can cache
/// and/or canonicalize service objects received over the wire.
abstract class ServiceObjectOwner extends ServiceObject {
ServiceObjectOwner._empty(ServiceObjectOwner owner) : super._empty(owner);
/// Returns the owning VM.
VM get vm => null;
/// Returns the owning isolate (if any).
Isolate get isolate => null;
/// Returns the vmService connection.
VMService get vmService => null;
/// Builds a [ServiceObject] corresponding to the [id] from [map].
/// The result may come from the cache. The result will not necessarily
/// be [loaded].
ServiceObject getFromMap(Map<String, dynamic> map);
}
/// There is only one instance of the VM class. The VM class owns [Isolate]
/// and [FlutterView] objects.
class VM extends ServiceObjectOwner {
VM._empty(this._vmService) : super._empty(null);
/// Connection to the VMService.
final VMService _vmService;
@override
VMService get vmService => _vmService;
@override
VM get vm => this;
@override
Future<Map<String, dynamic>> _fetchDirect() => invokeRpcRaw('getVM');
@override
void _update(Map<String, dynamic> map, bool mapIsRef) {
if (mapIsRef) {
return;
}
// Upgrade the collection. A side effect of this call is that any new
// isolates in the map are created and added to the isolate cache.
_upgradeCollection(map, this);
_loaded = true;
if (map['_heapAllocatedMemoryUsage'] != null) {
_heapAllocatedMemoryUsage = map['_heapAllocatedMemoryUsage'] as int;
}
_maxRSS = map['_maxRSS'] as int;
_embedder = map['_embedder'] as String;
// Remove any isolates which are now dead from the isolate cache.
_removeDeadIsolates((map['isolates'] as List<dynamic>).cast<Isolate>());
}
final Map<String, ServiceObject> _cache = <String,ServiceObject>{};
final Map<String,Isolate> _isolateCache = <String,Isolate>{};
/// The list of live isolates, ordered by isolate start time.
final List<Isolate> isolates = <Isolate>[];
/// The number of bytes allocated (e.g. by malloc) in the native heap.
int _heapAllocatedMemoryUsage;
int get heapAllocatedMemoryUsage => _heapAllocatedMemoryUsage ?? 0;
/// The peak resident set size for the process.
int _maxRSS;
int get maxRSS => _maxRSS ?? 0;
// The embedder's name, Flutter or dart_runner.
String _embedder;
String get embedder => _embedder;
bool get isFlutterEngine => embedder == 'Flutter';
bool get isDartRunner => embedder == 'dart_runner';
int _compareIsolates(Isolate a, Isolate b) {
final DateTime aStart = a.startTime;
final DateTime bStart = b.startTime;
if (aStart == null) {
if (bStart == null) {
return 0;
} else {
return 1;
} }
} final List<String> definitions = List<String>.from(params['definitions'] as List<dynamic>);
if (bStart == null) { final List<String> typeDefinitions = List<String>.from(params['typeDefinitions'] as List<dynamic>);
return -1; final String libraryUri = params['libraryUri'] as String;
} final String klass = params['klass'] as String;
return aStart.compareTo(bStart); final bool isStatic = params['isStatic'] as bool ?? false;
} try {
final String kernelBytesBase64 = await compileExpression(isolateId,
void _buildIsolateList() { expression, definitions, typeDefinitions, libraryUri, klass,
final List<Isolate> isolateList = _isolateCache.values.toList(); isStatic);
isolateList.sort(_compareIsolates); return <String, dynamic>{
isolates.clear(); 'type': 'Success',
isolates.addAll(isolateList); 'result': <String, dynamic>{
} 'result': <String, dynamic>{'kernelBytes': kernelBytesBase64},
},
void _removeDeadIsolates(List<Isolate> newIsolates) { };
// Build a set of new isolates. } on vm_service.RPCError {
final Set<String> newIsolateSet = <String>{}; rethrow;
for (final Isolate iso in newIsolates) { } on Exception catch (e, st) {
newIsolateSet.add(iso.id); throw vm_service.RPCError(
} 'Error during expression compilation: $e\n$st',
RPCErrorCodes.kServerError,
// Remove any old isolates which no longer exist. '',
final List<String> toRemove = <String>[]; );
_isolateCache.forEach((String id, _) {
if (!newIsolateSet.contains(id)) {
toRemove.add(id);
} }
}); });
toRemove.forEach(_isolateCache.remove); vmService.registerService('compileExpression', 'Flutter Tools');
_buildIsolateList();
}
@override
ServiceObject getFromMap(Map<String, dynamic> map) {
if (map == null) {
return null;
}
final String type = _stripRef(map['type'] as String);
if (type == 'VM') {
// Update this VM object.
updateFromMap(map);
return this;
}
final String mapId = map['id'] as String;
switch (type) {
case 'Isolate':
// Check cache.
Isolate isolate = _isolateCache[mapId];
if (isolate == null) {
// Add new isolate to the cache.
isolate = ServiceObject._fromMap(this, map) as Isolate;
_isolateCache[mapId] = isolate;
_buildIsolateList();
// Eagerly load the isolate.
isolate.load().catchError((dynamic e, StackTrace stack) {
globals.printTrace('Eagerly loading an isolate failed: $e\n$stack');
});
} else {
// Existing isolate, update data.
isolate.updateFromMap(map);
}
return isolate;
default:
// If we don't have a model object for this service object type, as a
// fallback return a ServiceMap object.
final ServiceObject serviceObject = ServiceMap._empty(owner);
// We have now constructed an empty service object, call update to populate it.
serviceObject.updateFromMap(map);
return serviceObject;
}
} }
if (device != null) {
// This function does not reload the isolate if it's found in the cache. vmService.registerServiceCallback('flutterMemoryInfo', (Map<String, dynamic> params) async {
Future<Isolate> getIsolate(String isolateId) { try {
if (!loaded) { final MemoryInfo result = await device.queryMemoryInfo();
// Trigger a VM load, then get the isolate. Ignore any errors. return <String, dynamic>{
return load().then<Isolate>((ServiceObject serviceObject) => getIsolate(isolateId)).catchError((dynamic error) => null); 'result': <String, Object>{
} 'type': 'Success',
return Future<Isolate>.value(_isolateCache[isolateId]); ...result.toJson(),
} }
};
/// Invoke the RPC and return the raw response. } on Exception catch (e, st) {
Future<Map<String, dynamic>> invokeRpcRaw( throw vm_service.RPCError(
String method, { 'Error during memory info query $e\n$st',
Map<String, dynamic> params = const <String, dynamic>{}, RPCErrorCodes.kServerError,
bool truncateLogs = true, '',
}) async { );
final vm_service.Response response = await _vmService }
._delegateService.callServiceExtension(method, args: params); });
return response.json; vmService.registerService('flutterMemoryInfo', 'Flutter Tools');
}
/// Invoke the RPC and return a [ServiceObject] response.
Future<T> invokeRpc<T extends ServiceObject>(
String method, {
Map<String, dynamic> params = const <String, dynamic>{},
bool truncateLogs = true,
}) async {
final Map<String, dynamic> response = await invokeRpcRaw(
method,
params: params,
truncateLogs: truncateLogs,
);
final T serviceObject = ServiceObject._fromMap(this, response) as T;
if ((serviceObject != null) && (serviceObject._canCache)) {
final String serviceObjectId = serviceObject.id;
_cache.putIfAbsent(serviceObjectId, () => serviceObject);
}
return serviceObject;
}
/// Create a new development file system on the device.
Future<Map<String, dynamic>> createDevFS(String fsName) {
return invokeRpcRaw('_createDevFS', params: <String, dynamic>{'fsName': fsName});
}
/// Delete an existing file system.
Future<Map<String, dynamic>> deleteDevFS(String fsName) {
return invokeRpcRaw('_deleteDevFS', params: <String, dynamic>{'fsName': fsName});
}
Future<Map<String, dynamic>> clearVMTimeline() {
return invokeRpcRaw('clearVMTimeline');
}
Future<Map<String, dynamic>> setVMTimelineFlags(List<String> recordedStreams) {
assert(recordedStreams != null);
return invokeRpcRaw(
'setVMTimelineFlags',
params: <String, dynamic>{
'recordedStreams': recordedStreams,
},
);
}
Future<Map<String, dynamic>> getVMTimeline() {
return invokeRpcRaw('getVMTimeline');
} }
return vmService;
} }
/// An isolate running inside the VM. Instances of the Isolate class are always /// Connect to a Dart VM Service at [httpUri].
/// canonicalized. ///
class Isolate extends ServiceObjectOwner { /// If the [reloadSources] parameter is not null, the 'reloadSources' service
Isolate._empty(VM owner) : super._empty(owner); /// will be registered. The VM Service Protocol allows clients to register
/// custom services that can be invoked by other clients through the service
@override /// protocol itself.
VM get vm => owner as VM; ///
/// See: https://github.com/dart-lang/sdk/commit/df8bf384eb815cf38450cb50a0f4b62230fba217
@override Future<vm_service.VmService> connectToVmService(
VMService get vmService => vm.vmService; Uri httpUri, {
ReloadSources reloadSources,
@override Restart restart,
Isolate get isolate => this; CompileExpression compileExpression,
ReloadMethod reloadMethod,
DateTime startTime; io.CompressionOptions compression = io.CompressionOptions.compressionDefault,
Device device,
/// The last pause event delivered to the isolate. If the isolate is running, }) async {
/// this will be a resume event. final VMServiceConnector connector = context.get<VMServiceConnector>() ?? _connect;
ServiceEvent pauseEvent; return connector(httpUri,
reloadSources: reloadSources,
final Map<String, ServiceObject> _cache = <String, ServiceObject>{}; restart: restart,
compileExpression: compileExpression,
@override compression: compression,
ServiceObject getFromMap(Map<String, dynamic> map) { device: device,
if (map == null) { reloadMethod: reloadMethod,
return null; );
}
final String mapType = _stripRef(map['type'] as String);
if (mapType == 'Isolate') {
// There are sometimes isolate refs in ServiceEvents.
return vm.getFromMap(map);
}
final String mapId = map['id'] as String;
ServiceObject serviceObject = (mapId != null) ? _cache[mapId] : null;
if (serviceObject != null) {
serviceObject.updateFromMap(map);
return serviceObject;
}
// Build the object from the map directly.
serviceObject = ServiceObject._fromMap(this, map);
if ((serviceObject != null) && serviceObject.canCache) {
_cache[mapId] = serviceObject;
}
return serviceObject;
}
@override
Future<Map<String, dynamic>> _fetchDirect() => invokeRpcRaw('getIsolate');
/// Invoke the RPC and return the raw response.
Future<Map<String, dynamic>> invokeRpcRaw(
String method, {
Map<String, dynamic> params,
}) {
// Inject the 'isolateId' parameter.
if (params == null) {
params = <String, dynamic>{
'isolateId': id,
};
} else {
params['isolateId'] = id;
}
return vm.invokeRpcRaw(method, params: params);
}
@override
void _update(Map<String, dynamic> map, bool mapIsRef) {
if (mapIsRef) {
return;
}
_loaded = true;
final int startTimeMillis = map['startTime'] as int;
startTime = DateTime.fromMillisecondsSinceEpoch(startTimeMillis);
_upgradeCollection(map, this);
pauseEvent = map['pauseEvent'] as ServiceEvent;
}
@override
String toString() => 'Isolate $id';
} }
class ServiceMap extends ServiceObject implements Map<String, dynamic> { Future<vm_service.VmService> _connect(
ServiceMap._empty(ServiceObjectOwner owner) : super._empty(owner); Uri httpUri, {
ReloadSources reloadSources,
final Map<String, dynamic> _map = <String, dynamic>{}; Restart restart,
CompileExpression compileExpression,
@override ReloadMethod reloadMethod,
void _update(Map<String, dynamic> map, bool mapIsRef) { io.CompressionOptions compression = io.CompressionOptions.compressionDefault,
_loaded = !mapIsRef; Device device,
_upgradeCollection(map, owner); }) async {
_map.clear(); final Uri wsUri = httpUri.replace(scheme: 'ws', path: globals.fs.path.join(httpUri.path, 'ws'));
_map.addAll(map); final io.WebSocket channel = await _openChannel(wsUri.toString(), compression: compression);
} // Create an instance of the package:vm_service API in addition to the flutter
// tool's to allow gradual migration.
// Forward Map interface calls. final Completer<void> streamClosedCompleter = Completer<void>();
@override final vm_service.VmService delegateService = vm_service.VmService(
void addAll(Map<String, dynamic> other) => _map.addAll(other); channel,
@override channel.add,
void clear() => _map.clear(); log: null,
@override disposeHandler: () async {
bool containsValue(dynamic v) => _map.containsValue(v); if (!streamClosedCompleter.isCompleted) {
@override streamClosedCompleter.complete();
bool containsKey(Object k) => _map.containsKey(k); }
@override await channel.close();
void forEach(void f(String key, dynamic value)) => _map.forEach(f); },
@override );
dynamic putIfAbsent(String key, dynamic ifAbsent()) => _map.putIfAbsent(key, ifAbsent);
@override final vm_service.VmService service = setUpVmService(
void remove(Object key) => _map.remove(key); reloadSources,
@override restart,
dynamic operator [](Object k) => _map[k]; compileExpression,
@override device,
void operator []=(String k, dynamic v) => _map[k] = v; reloadMethod,
@override delegateService,
bool get isEmpty => _map.isEmpty; );
@override _httpAddressExpando[service] = httpUri;
bool get isNotEmpty => _map.isNotEmpty; _wsAddressExpando[service] = wsUri;
@override
Iterable<String> get keys => _map.keys; // This call is to ensure we are able to establish a connection instead of
@override // keeping on trucking and failing farther down the process.
Iterable<dynamic> get values => _map.values; await delegateService.getVersion();
@override return service;
int get length => _map.length;
@override
String toString() => _map.toString();
@override
void addEntries(Iterable<MapEntry<String, dynamic>> entries) => _map.addEntries(entries);
@override
Map<RK, RV> cast<RK, RV>() => _map.cast<RK, RV>();
@override
void removeWhere(bool test(String key, dynamic value)) => _map.removeWhere(test);
@override
Map<K2, V2> map<K2, V2>(MapEntry<K2, V2> transform(String key, dynamic value)) => _map.map<K2, V2>(transform);
@override
Iterable<MapEntry<String, dynamic>> get entries => _map.entries;
@override
void updateAll(dynamic update(String key, dynamic value)) => _map.updateAll(update);
Map<RK, RV> retype<RK, RV>() => _map.cast<RK, RV>();
@override
dynamic update(String key, dynamic update(dynamic value), { dynamic ifAbsent() }) => _map.update(key, update, ifAbsent: ifAbsent);
} }
/// Peered to an Android/iOS FlutterView widget on a device. /// Peered to an Android/iOS FlutterView widget on a device.
...@@ -1240,6 +439,9 @@ class FlutterView { ...@@ -1240,6 +439,9 @@ class FlutterView {
/// Flutter specific VM Service functionality. /// Flutter specific VM Service functionality.
extension FlutterVmService on vm_service.VmService { extension FlutterVmService on vm_service.VmService {
Uri get wsAddress => _wsAddressExpando[this];
Uri get httpAddress => _httpAddressExpando[this];
/// Set the asset directory for the an attached Flutter view. /// Set the asset directory for the an attached Flutter view.
Future<void> setAssetDirectory({ Future<void> setAssetDirectory({
...@@ -1546,6 +748,39 @@ extension FlutterVmService on vm_service.VmService { ...@@ -1546,6 +748,39 @@ extension FlutterVmService on vm_service.VmService {
return null; return null;
}, test: (dynamic error) => error is vm_service.SentinelException); }, test: (dynamic error) => error is vm_service.SentinelException);
} }
/// Create a new development file system on the device.
Future<vm_service.Response> createDevFS(String fsName) {
return callServiceExtension('_createDevFS', args: <String, dynamic>{'fsName': fsName});
}
/// Delete an existing file system.
Future<vm_service.Response> deleteDevFS(String fsName) {
return callServiceExtension('_deleteDevFS', args: <String, dynamic>{'fsName': fsName});
}
Future<vm_service.Response> screenshot() {
return callServiceExtension(kScreenshotMethod);
}
Future<vm_service.Response> screenshotSkp() {
return callServiceExtension(kScreenshotSkpMethod);
}
/// Set the VM timeline flags
Future<vm_service.Response> setVMTimelineFlags(List<String> recordedStreams) {
assert(recordedStreams != null);
return callServiceExtension(
'setVMTimelineFlags',
args: <String, dynamic>{
'recordedStreams': recordedStreams,
},
);
}
Future<vm_service.Response> getVMTimeline() {
return callServiceExtension('getVMTimeline');
}
} }
/// Whether the event attached to an [Isolate.pauseEvent] should be considered /// Whether the event attached to an [Isolate.pauseEvent] should be considered
......
...@@ -9,18 +9,15 @@ import 'package:meta/meta.dart'; ...@@ -9,18 +9,15 @@ import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart'; import 'package:platform/platform.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'package:quiver/testing/async.dart';
import 'package:vm_service/vm_service.dart' as vm_service; import 'package:vm_service/vm_service.dart' as vm_service;
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.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/base/terminal.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/attach.dart'; import 'package:flutter_tools/src/commands/attach.dart';
import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/mdns_discovery.dart'; import 'package:flutter_tools/src/mdns_discovery.dart';
...@@ -76,16 +73,12 @@ void main() { ...@@ -76,16 +73,12 @@ void main() {
FakeDeviceLogReader mockLogReader; FakeDeviceLogReader mockLogReader;
MockPortForwarder portForwarder; MockPortForwarder portForwarder;
MockAndroidDevice device; MockAndroidDevice device;
MockProcessManager mockProcessManager;
MockHttpClient httpClient; MockHttpClient httpClient;
Completer<void> vmServiceDoneCompleter;
setUp(() { setUp(() {
mockProcessManager = MockProcessManager();
mockLogReader = FakeDeviceLogReader(); mockLogReader = FakeDeviceLogReader();
portForwarder = MockPortForwarder(); portForwarder = MockPortForwarder();
device = MockAndroidDevice(); device = MockAndroidDevice();
vmServiceDoneCompleter = Completer<void>();
when(device.portForwarder) when(device.portForwarder)
.thenReturn(portForwarder); .thenReturn(portForwarder);
when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort'))) when(portForwarder.forward(devicePort, hostPort: anyNamed('hostPort')))
...@@ -144,114 +137,6 @@ void main() { ...@@ -144,114 +137,6 @@ void main() {
Logger: () => logger, Logger: () => logger,
}); });
testUsingContext('finds all observatory ports and forwards them', () async {
testFileSystem.file(testFileSystem.path.join('.packages')).createSync();
testFileSystem.file(testFileSystem.path.join('lib', 'main.dart')).createSync();
testFileSystem
.file(testFileSystem.path.join('build', 'flutter_assets', 'AssetManifest.json'))
..createSync(recursive: true)
..writeAsStringSync('{}');
when(device.name).thenReturn('MockAndroidDevice');
when(device.getLogReader(includePastLogs: anyNamed('includePastLogs')))
.thenReturn(mockLogReader);
final Process dartProcess = MockProcess();
final StreamController<List<int>> compilerStdoutController = StreamController<List<int>>();
when(dartProcess.stdout).thenAnswer((_) => compilerStdoutController.stream);
when(dartProcess.stderr)
.thenAnswer((_) => Stream<List<int>>.fromFuture(Future<List<int>>.value(const <int>[])));
when(dartProcess.stdin).thenAnswer((_) => MockStdIn());
final Completer<int> dartProcessExitCode = Completer<int>();
when(dartProcess.exitCode).thenAnswer((_) => dartProcessExitCode.future);
when(mockProcessManager.start(any)).thenAnswer((_) => Future<Process>.value(dartProcess));
testDeviceManager.addDevice(device);
final List<String> observatoryLogs = <String>[];
await FakeAsync().run((FakeAsync time) {
unawaited(runZoned(() async {
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
// The "Observatory URL on device" message is output by the ProtocolDiscovery when it found the observatory.
if (message.startsWith('[verbose] Observatory URL on device')) {
observatoryLogs.add(message);
}
if (message == '[stdout] Waiting for a connection from Flutter on MockAndroidDevice...') {
observatoryLogs.add(message);
}
if (message == '[stdout] Lost connection to device.') {
observatoryLogs.add(message);
}
if (message.contains('Hot reload.')) {
observatoryLogs.add(message);
}
if (message.contains('Hot restart.')) {
observatoryLogs.add(message);
}
});
final TestHotRunnerFactory testHotRunnerFactory = TestHotRunnerFactory();
final Future<void> task = createTestCommandRunner(
AttachCommand(hotRunnerFactory: testHotRunnerFactory)
).run(<String>['attach']);
// First iteration of the attach loop.
mockLogReader.addLine('Observatory listening on http://127.0.0.1:0001');
mockLogReader.addLine('Observatory listening on http://127.0.0.1:1234');
time.elapse(const Duration(milliseconds: 200));
compilerStdoutController
.add(utf8.encode('result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n'));
time.flushMicrotasks();
// Second iteration of the attach loop.
mockLogReader.addLine('Observatory listening on http://127.0.0.1:0002');
mockLogReader.addLine('Observatory listening on http://127.0.0.1:1235');
time.elapse(const Duration(milliseconds: 200));
compilerStdoutController
.add(utf8.encode('result abc\nline1\nline2\nabc\nabc /path/to/main.dart.dill 0\n'));
time.flushMicrotasks();
dartProcessExitCode.complete(0);
await loggerSubscription.cancel();
await testHotRunnerFactory.exitApp();
await task;
}));
});
expect(observatoryLogs.length, 9);
expect(observatoryLogs[0], '[stdout] Waiting for a connection from Flutter on MockAndroidDevice...');
expect(observatoryLogs[1], '[verbose] Observatory URL on device: http://127.0.0.1:1234');
expect(observatoryLogs[2], '[stdout] Lost connection to device.');
expect(observatoryLogs[3].contains('Hot reload.'), isTrue);
expect(observatoryLogs[4].contains('Hot restart.'), isTrue);
expect(observatoryLogs[5], '[verbose] Observatory URL on device: http://127.0.0.1:1235');
expect(observatoryLogs[6], '[stdout] Lost connection to device.');
expect(observatoryLogs[7].contains('Hot reload.'), isTrue);
expect(observatoryLogs[8].contains('Hot restart.'), isTrue);
verify(portForwarder.forward(1234, hostPort: anyNamed('hostPort'))).called(1);
verify(portForwarder.forward(1235, hostPort: anyNamed('hostPort'))).called(1);
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
HttpClientFactory: () => () => httpClient,
ProcessManager: () => mockProcessManager,
Logger: () => logger,
VMServiceConnector: () => getFakeVmServiceFactory(
vmServiceDoneCompleter: vmServiceDoneCompleter,
),
});
testUsingContext('Fails with tool exit on bad Observatory uri', () async { testUsingContext('Fails with tool exit on bad Observatory uri', () async {
when(device.getLogReader(includePastLogs: anyNamed('includePastLogs'))) when(device.getLogReader(includePastLogs: anyNamed('includePastLogs')))
.thenAnswer((_) { .thenAnswer((_) {
...@@ -797,50 +682,54 @@ VMServiceConnector getFakeVmServiceFactory({ ...@@ -797,50 +682,54 @@ VMServiceConnector getFakeVmServiceFactory({
CompressionOptions compression, CompressionOptions compression,
Device device, Device device,
}) async { }) async {
final VMService vmService = VMServiceMock(); final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
final VM vm = VMMock(); requests: <VmServiceExpectation>[
FakeVmServiceRequest(
when(vmService.vm).thenReturn(vm); id: '1',
when(vmService.isClosed).thenReturn(false); method: kListViewsMethod,
when(vmService.done).thenAnswer((_) { args: null,
return Future<void>.value(null); jsonResponse: <String, Object>{
}); 'views': <Object>[
when(vmService.onDone).thenAnswer((_) { <String, Object>{
return Future<void>.value(null); 'id': '1',
}); 'isolate': fakeUnpausedIsolate.toJson()
when(vmService.getVM()).thenAnswer((_) async { },
return vm_service.VM( ],
pid: 1, },
architectureBits: 64, ),
hostCPU: '', FakeVmServiceRequest(
name: '', id: '2',
isolates: <vm_service.IsolateRef>[], method: 'getVM',
isolateGroups: <vm_service.IsolateGroupRef>[], args: null,
startTime: 0, jsonResponse: vm_service.VM.parse(<String, Object>{})
targetCPU: '', .toJson(),
operatingSystem: '', ),
version: '', FakeVmServiceRequest(
); id: '3',
}); method: '_createDevFS',
when(vmService.getIsolate(any)) args: <String, Object>{
.thenAnswer((Invocation invocation) async { 'fsName': globals.fs.currentDirectory.absolute.path,
return fakeUnpausedIsolate; },
}); jsonResponse: <String, Object>{
when(vmService.callMethod(kListViewsMethod)) 'uri': globals.fs.currentDirectory.absolute.path,
.thenAnswer((_) async { },
return vm_service.Response.parse(<String, Object>{ ),
'views': <Object>[ FakeVmServiceRequest(
<String, Object>{ id: '4',
'id': '1', method: kListViewsMethod,
'isolate': fakeUnpausedIsolate.toJson() args: null,
} jsonResponse: <String, Object>{
] 'views': <Object>[
}); <String, Object>{
}); 'id': '1',
when(vm.createDevFS(any)) 'isolate': fakeUnpausedIsolate.toJson()
.thenAnswer((_) => Future<Map<String, dynamic>>.value(<String, dynamic>{'uri': '/',})); },
],
return vmService; },
),
],
);
return fakeVmServiceHost.vmService;
}; };
} }
...@@ -884,8 +773,6 @@ class TestHotRunnerFactory extends HotRunnerFactory { ...@@ -884,8 +773,6 @@ class TestHotRunnerFactory extends HotRunnerFactory {
} }
} }
class VMMock extends Mock implements VM {}
class VMServiceMock extends Mock implements VMService {}
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
class MockProcess extends Mock implements Process {} class MockProcess extends Mock implements Process {}
class MockHttpClientRequest extends Mock implements HttpClientRequest {} class MockHttpClientRequest extends Mock implements HttpClientRequest {}
......
...@@ -2,49 +2,49 @@ ...@@ -2,49 +2,49 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/test/coverage_collector.dart'; import 'package:flutter_tools/src/test/coverage_collector.dart';
import 'package:flutter_tools/src/vmservice.dart';
import 'package:mockito/mockito.dart';
import 'package:vm_service/vm_service.dart' as vm_service; import 'package:vm_service/vm_service.dart' as vm_service;
import '../src/common.dart'; import '../src/common.dart';
void main() { void main() {
MockVMService mockVMService; testWithoutContext('Coverage collector Can handle coverage SentinelException', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
setUp(() { requests: <VmServiceExpectation>[
mockVMService = MockVMService(); FakeVmServiceRequest(
}); id: '1',
method: 'getVM',
test('Coverage collector Can handle coverage sentinenl data', () async { args: null,
when(mockVMService.getScripts(any)) jsonResponse: (vm_service.VM.parse(<String, Object>{})
.thenThrow(vm_service.SentinelException.parse('getScripts', <String, Object>{})); ..isolates = <vm_service.IsolateRef>[
final Map<String, Object> result = await collect(null, (String predicate) => true, connector: (Uri uri) async { vm_service.IsolateRef.parse(<String, Object>{
return mockVMService; 'id': '1'
}); }),
]
).toJson(),
),
const FakeVmServiceRequest(
id: '2',
method: 'getScripts',
args: <String, Object>{
'isolateId': '1',
},
jsonResponse: <String, Object>{
'type': 'Sentinel'
}
)
],
);
final Map<String, Object> result = await collect(
null,
(String predicate) => true,
connector: (Uri uri) async {
return fakeVmServiceHost.vmService;
},
);
expect(result, <String, Object>{'type': 'CodeCoverage', 'coverage': <Object>[]}); expect(result, <String, Object>{'type': 'CodeCoverage', 'coverage': <Object>[]});
expect(fakeVmServiceHost.hasRemainingExpectations, false);
}); });
} }
class MockVMService extends Mock implements VMService {
@override
final MockVM vm = MockVM();
}
class MockVM extends Mock implements VM {
@override
final List<MockIsolate> isolates = <MockIsolate>[ MockIsolate() ];
}
class MockIsolate extends Mock implements Isolate {}
class MockProcess extends Mock implements Process {
final Completer<int>completer = Completer<int>();
@override
Future<int> get exitCode => completer.future;
}
...@@ -126,13 +126,21 @@ void main() { ...@@ -126,13 +126,21 @@ void main() {
// simulate package // simulate package
await _createPackage(fs, 'somepkg', 'somefile.txt'); await _createPackage(fs, 'somepkg', 'somefile.txt');
final RealMockVMService vmService = RealMockVMService(); final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
final RealMockVM vm = RealMockVM(); requests: <VmServiceExpectation>[
final Map<String, dynamic> response = <String, dynamic>{ 'uri': 'file://abc' }; FakeVmServiceRequest(
when(vm.createDevFS(any)).thenAnswer((Invocation invocation) { id: '1',
return Future<Map<String, dynamic>>.value(response); method: '_createDevFS',
}); args: <String, Object>{
when(vmService.vm).thenReturn(vm); 'fsName': 'test',
},
jsonResponse: <String, Object>{
'uri': Uri.parse('test').toString(),
}
)
],
);
setHttpAddress(Uri.parse('http://localhost'), fakeVmServiceHost.vmService);
reset(httpClient); reset(httpClient);
...@@ -152,7 +160,7 @@ void main() { ...@@ -152,7 +160,7 @@ void main() {
}); });
final DevFS devFS = DevFS( final DevFS devFS = DevFS(
vmService, fakeVmServiceHost.vmService,
'test', 'test',
tempDir, tempDir,
osUtils: osUtils, osUtils: osUtils,
...@@ -183,101 +191,43 @@ void main() { ...@@ -183,101 +191,43 @@ void main() {
}); });
group('devfs remote', () { group('devfs remote', () {
MockVMService vmService;
final MockResidentCompiler residentCompiler = MockResidentCompiler();
DevFS devFS; DevFS devFS;
setUpAll(() async { setUpAll(() async {
tempDir = _newTempDir(fs); tempDir = _newTempDir(fs);
basePath = tempDir.path; basePath = tempDir.path;
vmService = MockVMService();
await vmService.setUp();
}); });
setUp(() { setUp(() {
vmService.resetState(); final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
FakeVmServiceRequest(
id: '1',
method: '_createDevFS',
args: <String, Object>{
'fsName': 'test',
},
jsonResponse: <String, Object>{
'uri': Uri.parse('test').toString(),
}
)
],
);
setHttpAddress(Uri.parse('http://localhost'), fakeVmServiceHost.vmService);
devFS = DevFS( devFS = DevFS(
vmService, fakeVmServiceHost.vmService,
'test', 'test',
tempDir, tempDir,
osUtils: FakeOperatingSystemUtils(), osUtils: FakeOperatingSystemUtils(),
// TODO(jonahwilliams): remove and prevent usage of http writer.
disableUpload: true,
); );
}); });
tearDownAll(() async { tearDownAll(() async {
await vmService.tearDown();
_cleanupTempDirs(); _cleanupTempDirs();
}); });
testUsingContext('create dev file system', () async {
// simulate workspace
final File file = fs.file(fs.path.join(basePath, filePath));
await file.parent.create(recursive: true);
file.writeAsBytesSync(<int>[1, 2, 3]);
// simulate package
await _createPackage(fs, 'somepkg', 'somefile.txt');
await devFS.create();
vmService.expectMessages(<String>['create test']);
expect(devFS.assetPathsToEvict, isEmpty);
final UpdateFSReport report = await devFS.update(
mainUri: Uri.parse('lib/foo.txt'),
generator: residentCompiler,
pathToReload: 'lib/foo.txt.dill',
trackWidgetCreation: false,
invalidatedFiles: <Uri>[],
packageConfig: PackageConfig.empty,
);
vmService.expectMessages(<String>[
'writeFile test lib/foo.txt.dill',
]);
expect(devFS.assetPathsToEvict, isEmpty);
expect(report.syncedBytes, 22);
expect(report.success, true);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
HttpClient: () => () => HttpClient(),
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('delete dev file system', () async {
expect(vmService.messages, isEmpty, reason: 'prior test timeout');
await devFS.destroy();
vmService.expectMessages(<String>['destroy test']);
expect(devFS.assetPathsToEvict, isEmpty);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('cleanup preexisting file system', () async {
// simulate workspace
final File file = fs.file(fs.path.join(basePath, filePath));
await file.parent.create(recursive: true);
file.writeAsBytesSync(<int>[1, 2, 3]);
// simulate package
await _createPackage(fs, 'somepkg', 'somefile.txt');
await devFS.create();
vmService.expectMessages(<String>['create test']);
expect(devFS.assetPathsToEvict, isEmpty);
// Try to create again.
await devFS.create();
vmService.expectMessages(<String>['create test', 'destroy test', 'create test']);
expect(devFS.assetPathsToEvict, isEmpty);
// Really destroy.
await devFS.destroy();
vmService.expectMessages(<String>['destroy test']);
expect(devFS.assetPathsToEvict, isEmpty);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('reports unsuccessful compile when errors are returned', () async { testUsingContext('reports unsuccessful compile when errors are returned', () async {
await devFS.create(); await devFS.create();
final DateTime previousCompile = devFS.lastCompiled; final DateTime previousCompile = devFS.lastCompiled;
...@@ -345,98 +295,6 @@ void main() { ...@@ -345,98 +295,6 @@ void main() {
}); });
} }
class MockVMService extends BasicMock implements VMService {
MockVMService() {
_vm = MockVM(this);
}
Uri _httpAddress;
HttpServer _server;
MockVM _vm;
@override
Uri get httpAddress => _httpAddress;
@override
VM get vm => _vm;
Future<void> setUp() async {
try {
_server = await HttpServer.bind(InternetAddress.loopbackIPv6, 0);
_httpAddress = Uri.parse('http://[::1]:${_server.port}');
} on SocketException {
// Fall back to IPv4 if the host doesn't support binding to IPv6 localhost
_server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
_httpAddress = Uri.parse('http://127.0.0.1:${_server.port}');
}
_server.listen((HttpRequest request) {
final String fsName = request.headers.value('dev_fs_name');
final String devicePath = utf8.decode(base64.decode(request.headers.value('dev_fs_uri_b64')));
messages.add('writeFile $fsName $devicePath');
request.drain<List<int>>().then<void>((List<int> value) {
request.response
..write('Got it')
..close();
});
});
}
Future<void> tearDown() async {
await _server?.close();
}
void resetState() {
_vm = MockVM(this);
messages.clear();
}
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class MockVM implements VM {
MockVM(this._service);
final MockVMService _service;
final Uri _baseUri = Uri.parse('file:///tmp/devfs/test');
bool _devFSExists = false;
static const int kFileSystemAlreadyExists = 1001;
@override
Future<Map<String, dynamic>> createDevFS(String fsName) async {
_service.messages.add('create $fsName');
if (_devFSExists) {
throw vm_service.RPCError('File system already exists', kFileSystemAlreadyExists, '');
}
_devFSExists = true;
return <String, dynamic>{'uri': '$_baseUri'};
}
@override
Future<Map<String, dynamic>> deleteDevFS(String fsName) async {
_service.messages.add('destroy $fsName');
_devFSExists = false;
return <String, dynamic>{'type': 'Success'};
}
@override
Future<Map<String, dynamic>> invokeRpcRaw(
String method, {
Map<String, dynamic> params = const <String, dynamic>{},
Duration timeout,
bool timeoutFatal = true,
bool truncateLogs = true,
}) async {
_service.messages.add('$method $params');
return <String, dynamic>{'success': true};
}
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class RealMockResidentCompiler extends Mock implements ResidentCompiler {} class RealMockResidentCompiler extends Mock implements ResidentCompiler {}
final List<Directory> _tempDirs = <Directory>[]; final List<Directory> _tempDirs = <Directory>[];
...@@ -474,14 +332,6 @@ Future<File> _createPackage(FileSystem fs, String pkgName, String pkgFileName, { ...@@ -474,14 +332,6 @@ Future<File> _createPackage(FileSystem fs, String pkgName, String pkgFileName, {
..writeAsStringSync(sb.toString()); ..writeAsStringSync(sb.toString());
} }
class RealMockVM extends Mock implements VM {
}
class RealMockVMService extends Mock implements VMService {
}
class MyHttpOverrides extends HttpOverrides { class MyHttpOverrides extends HttpOverrides {
MyHttpOverrides(this._httpClient); MyHttpOverrides(this._httpClient);
@override @override
...@@ -497,3 +347,4 @@ class MockHttpClientRequest extends Mock implements HttpClientRequest {} ...@@ -497,3 +347,4 @@ class MockHttpClientRequest extends Mock implements HttpClientRequest {}
class MockHttpHeaders extends Mock implements HttpHeaders {} class MockHttpHeaders extends Mock implements HttpHeaders {}
class MockHttpClientResponse extends Mock implements HttpClientResponse {} class MockHttpClientResponse extends Mock implements HttpClientResponse {}
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {} class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
class MockVMService extends Mock implements vm_service.VmService {}
...@@ -628,21 +628,34 @@ void main() { ...@@ -628,21 +628,34 @@ void main() {
group('FuchsiaIsolateDiscoveryProtocol', () { group('FuchsiaIsolateDiscoveryProtocol', () {
MockPortForwarder portForwarder; MockPortForwarder portForwarder;
MockVMService vmService;
setUp(() { setUp(() {
portForwarder = MockPortForwarder(); portForwarder = MockPortForwarder();
vmService = MockVMService();
}); });
Future<Uri> findUri(List<FlutterView> views, String expectedIsolateName) async { Future<Uri> findUri(List<FlutterView> views, String expectedIsolateName) async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
FakeVmServiceRequest(
id: '1',
method: kListViewsMethod,
args: null,
jsonResponse: <String, Object>{
'views': <Object>[
for (FlutterView view in views)
view.toJson()
],
},
),
],
);
final MockFuchsiaDevice fuchsiaDevice = final MockFuchsiaDevice fuchsiaDevice =
MockFuchsiaDevice('123', portForwarder, false); MockFuchsiaDevice('123', portForwarder, false);
final FuchsiaIsolateDiscoveryProtocol discoveryProtocol = final FuchsiaIsolateDiscoveryProtocol discoveryProtocol =
FuchsiaIsolateDiscoveryProtocol( FuchsiaIsolateDiscoveryProtocol(
fuchsiaDevice, fuchsiaDevice,
expectedIsolateName, expectedIsolateName,
(Uri uri) async => vmService, (Uri uri) async => fakeVmServiceHost.vmService,
true, // only poll once. true, // only poll once.
); );
...@@ -650,17 +663,7 @@ void main() { ...@@ -650,17 +663,7 @@ void main() {
.thenAnswer((Invocation invocation) async => <int>[1]); .thenAnswer((Invocation invocation) async => <int>[1]);
when(portForwarder.forward(1)) when(portForwarder.forward(1))
.thenAnswer((Invocation invocation) async => 2); .thenAnswer((Invocation invocation) async => 2);
when(vmService.getVMOld()) setHttpAddress(Uri.parse('example'), fakeVmServiceHost.vmService);
.thenAnswer((Invocation invocation) => Future<void>.value(null));
when(vmService.httpAddress).thenReturn(Uri.parse('example'));
when(vmService.callMethod(kListViewsMethod)).thenAnswer((Invocation invocation) async {
return vm_service.Response.parse(<String, Object>{
'views': <Object>[
for (FlutterView view in views)
view.toJson()
],
});
});
return await discoveryProtocol.uri; return await discoveryProtocol.uri;
} }
...@@ -1121,8 +1124,6 @@ class MockFuchsiaDevice extends Mock implements FuchsiaDevice { ...@@ -1121,8 +1124,6 @@ class MockFuchsiaDevice extends Mock implements FuchsiaDevice {
class MockPortForwarder extends Mock implements DevicePortForwarder {} class MockPortForwarder extends Mock implements DevicePortForwarder {}
class MockVMService extends Mock implements VMService {}
class FuchsiaDeviceWithFakeDiscovery extends FuchsiaDevice { class FuchsiaDeviceWithFakeDiscovery extends FuchsiaDevice {
FuchsiaDeviceWithFakeDiscovery(String id, {String name}) : super(id, name: name); FuchsiaDeviceWithFakeDiscovery(String id, {String name}) : super(id, name: name);
......
...@@ -11,7 +11,6 @@ import 'package:flutter_tools/src/convert.dart'; ...@@ -11,7 +11,6 @@ import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/vmservice.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:vm_service/vm_service.dart'; import 'package:vm_service/vm_service.dart';
...@@ -177,4 +176,4 @@ Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt ...@@ -177,4 +176,4 @@ Runner(libsystem_asl.dylib)[297] <Notice>: libMobileGestalt
} }
class MockArtifacts extends Mock implements Artifacts {} class MockArtifacts extends Mock implements Artifacts {}
class MockVmService extends Mock implements VMService, VmService {} class MockVmService extends Mock implements VmService {}
...@@ -146,29 +146,21 @@ void main() { ...@@ -146,29 +146,21 @@ void main() {
when(mockFlutterDevice.vmService).thenAnswer((Invocation invocation) { when(mockFlutterDevice.vmService).thenAnswer((Invocation invocation) {
return fakeVmServiceHost.vmService; return fakeVmServiceHost.vmService;
}); });
when(mockFlutterDevice.flutterDeprecatedVmService).thenAnswer((Invocation invocation) {
return mockVMService;
});
when(mockFlutterDevice.refreshViews()).thenAnswer((Invocation invocation) async { }); when(mockFlutterDevice.refreshViews()).thenAnswer((Invocation invocation) async { });
when(mockFlutterDevice.getVMs()).thenAnswer((Invocation invocation) async { }); when(mockFlutterDevice.reloadSources(any, pause: anyNamed('pause'))).thenAnswer((Invocation invocation) async {
when(mockFlutterDevice.reloadSources(any, pause: anyNamed('pause'))).thenReturn(<Future<vm_service.ReloadReport>>[ return <Future<vm_service.ReloadReport>>[
Future<vm_service.ReloadReport>.value(vm_service.ReloadReport.parse(<String, dynamic>{ Future<vm_service.ReloadReport>.value(vm_service.ReloadReport.parse(<String, dynamic>{
'type': 'ReloadReport', 'type': 'ReloadReport',
'success': true, 'success': true,
'details': <String, dynamic>{ 'details': <String, dynamic>{
'loadedLibraryCount': 1, 'loadedLibraryCount': 1,
'finalLibraryCount': 1, 'finalLibraryCount': 1,
'receivedLibraryCount': 1, 'receivedLibraryCount': 1,
'receivedClassesCount': 1, 'receivedClassesCount': 1,
'receivedProceduresCount': 1, 'receivedProceduresCount': 1,
}, },
})), })),
]); ];
// VMService mocks.
when(mockVMService.wsAddress).thenReturn(testUri);
when(mockVMService.done).thenAnswer((Invocation invocation) {
final Completer<void> result = Completer<void>.sync();
return result.future;
}); });
}); });
...@@ -955,6 +947,7 @@ void main() { ...@@ -955,6 +947,7 @@ void main() {
test('HotRunner writes vm service file when providing debugging option', () => testbed.run(() async { test('HotRunner writes vm service file when providing debugging option', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]); fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
setWsAddress(testUri, fakeVmServiceHost.vmService);
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
residentRunner = HotRunner( residentRunner = HotRunner(
<FlutterDevice>[ <FlutterDevice>[
...@@ -1029,6 +1022,7 @@ void main() { ...@@ -1029,6 +1022,7 @@ void main() {
test('ColdRunner writes vm service file when providing debugging option', () => testbed.run(() async { test('ColdRunner writes vm service file when providing debugging option', () => testbed.run(() async {
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]); fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true);
setWsAddress(testUri, fakeVmServiceHost.vmService);
residentRunner = ColdRunner( residentRunner = ColdRunner(
<FlutterDevice>[ <FlutterDevice>[
mockFlutterDevice, mockFlutterDevice,
...@@ -1111,7 +1105,7 @@ void main() { ...@@ -1111,7 +1105,7 @@ void main() {
} }
class MockFlutterDevice extends Mock implements FlutterDevice {} class MockFlutterDevice extends Mock implements FlutterDevice {}
class MockVMService extends Mock implements VMService {} class MockVMService extends Mock implements vm_service.VmService {}
class MockDevFS extends Mock implements DevFS {} class MockDevFS extends Mock implements DevFS {}
class MockDevice extends Mock implements Device {} class MockDevice extends Mock implements Device {}
class MockDeviceLogReader extends Mock implements DeviceLogReader {} class MockDeviceLogReader extends Mock implements DeviceLogReader {}
......
...@@ -91,20 +91,17 @@ final Map<String, Object> listViews = <String, dynamic>{ ...@@ -91,20 +91,17 @@ final Map<String, Object> listViews = <String, dynamic>{
typedef ServiceCallback = Future<Map<String, dynamic>> Function(Map<String, Object>); typedef ServiceCallback = Future<Map<String, dynamic>> Function(Map<String, Object>);
void main() { void main() {
testUsingContext('VmService registers reloadSources', () { testUsingContext('VmService registers reloadSources', () async {
Future<void> reloadSources(String isolateId, { bool pause, bool force}) async {} Future<void> reloadSources(String isolateId, { bool pause, bool force}) async {}
final MockVMService mockVMService = MockVMService(); final MockVMService mockVMService = MockVMService();
VMService( setUpVmService(
null,
null,
reloadSources, reloadSources,
null, null,
null, null,
null, null,
null, null,
mockVMService, mockVMService,
Completer<void>(),
const Stream<dynamic>.empty(),
); );
verify(mockVMService.registerService('reloadSources', 'Flutter Tools')).called(1); verify(mockVMService.registerService('reloadSources', 'Flutter Tools')).called(1);
...@@ -112,20 +109,17 @@ void main() { ...@@ -112,20 +109,17 @@ void main() {
Logger: () => BufferLogger.test() Logger: () => BufferLogger.test()
}); });
testUsingContext('VmService registers reloadMethod', () { testUsingContext('VmService registers reloadMethod', () async {
Future<void> reloadMethod({ String classId, String libraryId,}) async {} Future<void> reloadMethod({ String classId, String libraryId,}) async {}
final MockVMService mockVMService = MockVMService(); final MockVMService mockVMService = MockVMService();
VMService( setUpVmService(
null,
null,
null, null,
null, null,
null, null,
null, null,
reloadMethod, reloadMethod,
mockVMService, mockVMService,
Completer<void>(),
const Stream<dynamic>.empty(),
); );
verify(mockVMService.registerService('reloadMethod', 'Flutter Tools')).called(1); verify(mockVMService.registerService('reloadMethod', 'Flutter Tools')).called(1);
...@@ -133,20 +127,17 @@ void main() { ...@@ -133,20 +127,17 @@ void main() {
Logger: () => BufferLogger.test() Logger: () => BufferLogger.test()
}); });
testUsingContext('VmService registers flutterMemoryInfo service', () { testUsingContext('VmService registers flutterMemoryInfo service', () async {
final MockDevice mockDevice = MockDevice(); final MockDevice mockDevice = MockDevice();
final MockVMService mockVMService = MockVMService(); final MockVMService mockVMService = MockVMService();
VMService( setUpVmService(
null,
null,
null, null,
null, null,
null, null,
mockDevice, mockDevice,
null, null,
mockVMService, mockVMService,
Completer<void>(),
const Stream<dynamic>.empty(),
); );
verify(mockVMService.registerService('flutterMemoryInfo', 'Flutter Tools')).called(1); verify(mockVMService.registerService('flutterMemoryInfo', 'Flutter Tools')).called(1);
...@@ -156,17 +147,13 @@ void main() { ...@@ -156,17 +147,13 @@ void main() {
testUsingContext('VMService returns correct FlutterVersion', () async { testUsingContext('VMService returns correct FlutterVersion', () async {
final MockVMService mockVMService = MockVMService(); final MockVMService mockVMService = MockVMService();
VMService( setUpVmService(
null,
null,
null, null,
null, null,
null, null,
null, null,
null, null,
mockVMService, mockVMService,
Completer<void>(),
const Stream<dynamic>.empty(),
); );
verify(mockVMService.registerService('flutterVersion', 'Flutter Tools')).called(1); verify(mockVMService.registerService('flutterVersion', 'Flutter Tools')).called(1);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment