Unverified Commit 3f9ede13 authored by Devon Carew's avatar Devon Carew Committed by GitHub

fix the reload and restart service extension methods (#56240)

parent 6d1966ef
...@@ -43,7 +43,7 @@ abstract class RPCErrorCodes { ...@@ -43,7 +43,7 @@ abstract class RPCErrorCodes {
/// Internal JSON-RPC error. /// Internal JSON-RPC error.
static const int kInternalError = -32603; static const int kInternalError = -32603;
/// Application specific error codes.s /// Application specific error codes.
static const int kServerError = -32000; static const int kServerError = -32000;
} }
...@@ -155,32 +155,19 @@ vm_service.VmService setUpVmService( ...@@ -155,32 +155,19 @@ vm_service.VmService setUpVmService(
) { ) {
if (reloadSources != null) { if (reloadSources != null) {
vmService.registerServiceCallback('reloadSources', (Map<String, dynamic> params) async { vmService.registerServiceCallback('reloadSources', (Map<String, dynamic> params) async {
final String isolateId = params['isolateId'].value as String; final String isolateId = _validateRpcStringParam('reloadSources', params, 'isolateId');
final bool force = params['force'] as bool ?? false; final bool force = _validateRpcBoolParam('reloadSources', params, 'force');
final bool pause = params['pause'] as bool ?? false; final bool pause = _validateRpcBoolParam('reloadSources', params, 'pause');
if (isolateId.isEmpty) { await reloadSources(isolateId, force: force, pause: pause);
throw vm_service.RPCError(
"Invalid 'isolateId': $isolateId", return <String, dynamic>{
RPCErrorCodes.kInvalidParams, 'result': <String, Object>{
'', 'type': 'Success',
); }
} };
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,
'',
);
}
}); });
vmService.registerService('reloadSources', 'Flutter Tools'); vmService.registerService('reloadSources', 'Flutter Tools');
} }
if (reloadMethod != null) { if (reloadMethod != null) {
...@@ -194,56 +181,30 @@ vm_service.VmService setUpVmService( ...@@ -194,56 +181,30 @@ vm_service.VmService setUpVmService(
// If the build method of a StatefulWidget is updated, then this is the name // If the build method of a StatefulWidget is updated, then this is the name
// of the Widget class that created the State object. // of the Widget class that created the State object.
vmService.registerServiceCallback('reloadMethod', (Map<String, dynamic> params) async { vmService.registerServiceCallback('reloadMethod', (Map<String, dynamic> params) async {
final String libraryId = params['library'] as String; final String libraryId = _validateRpcStringParam('reloadMethod', params, 'library');
final String classId = params['class'] as String; final String classId = _validateRpcStringParam('reloadMethod', params, 'class');
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'); globals.printTrace('reloadMethod not yet supported, falling back to hot reload');
try { await reloadMethod(libraryId: libraryId, classId: classId);
await reloadMethod( return <String, dynamic>{
libraryId: libraryId, 'result': <String, Object>{
classId: classId, 'type': 'Success',
); }
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, '');
}
}); });
vmService.registerService('reloadMethod', 'Flutter Tools'); vmService.registerService('reloadMethod', 'Flutter Tools');
} }
if (restart != null) { if (restart != null) {
vmService.registerServiceCallback('hotRestart', (Map<String, dynamic> params) async { vmService.registerServiceCallback('hotRestart', (Map<String, dynamic> params) async {
final bool pause = params['pause'] as bool ?? false; final bool pause = _validateRpcBoolParam('compileExpression', params, 'pause');
try { await restart(pause: pause);
await restart(pause: pause); return <String, dynamic>{
return <String, String>{'type': 'Success'}; 'result': <String, Object>{
} on vm_service.RPCError { 'type': 'Success',
rethrow; }
} on Exception catch (e, st) { };
throw vm_service.RPCError(
'Error during Hot Restart: $e\n$st',
RPCErrorCodes.kServerError,
'',
);
}
}); });
vmService.registerService('hotRestart', 'Flutter Tools'); vmService.registerService('hotRestart', 'Flutter Tools');
} }
...@@ -264,66 +225,35 @@ vm_service.VmService setUpVmService( ...@@ -264,66 +225,35 @@ vm_service.VmService setUpVmService(
if (compileExpression != null) { if (compileExpression != null) {
vmService.registerServiceCallback('compileExpression', (Map<String, dynamic> params) async { vmService.registerServiceCallback('compileExpression', (Map<String, dynamic> params) async {
final String isolateId = params['isolateId'] as String; final String isolateId = _validateRpcStringParam('compileExpression', params, 'isolateId');
if (isolateId is! String || isolateId.isEmpty) { final String expression = _validateRpcStringParam('compileExpression', params, 'expression');
throw throw vm_service.RPCError(
"Invalid 'isolateId': $isolateId",
RPCErrorCodes.kInvalidParams,
'',
);
}
final String expression = params['expression'] as String;
if (expression is! String || expression.isEmpty) {
throw throw vm_service.RPCError(
"Invalid 'expression': $expression",
RPCErrorCodes.kInvalidParams,
'',
);
}
final List<String> definitions = List<String>.from(params['definitions'] as List<dynamic>); final List<String> definitions = List<String>.from(params['definitions'] as List<dynamic>);
final List<String> typeDefinitions = List<String>.from(params['typeDefinitions'] as List<dynamic>); final List<String> typeDefinitions = List<String>.from(params['typeDefinitions'] as List<dynamic>);
final String libraryUri = params['libraryUri'] as String; final String libraryUri = params['libraryUri'] as String;
final String klass = params['klass'] as String; final String klass = params['klass'] as String;
final bool isStatic = params['isStatic'] as bool ?? false; final bool isStatic = _validateRpcBoolParam('compileExpression', params, 'isStatic');
try {
final String kernelBytesBase64 = await compileExpression(isolateId, final String kernelBytesBase64 = await compileExpression(isolateId,
expression, definitions, typeDefinitions, libraryUri, klass, expression, definitions, typeDefinitions, libraryUri, klass,
isStatic); isStatic);
return <String, dynamic>{ return <String, dynamic>{
'type': 'Success', 'type': 'Success',
'result': <String, dynamic>{ 'result': <String, dynamic>{
'result': <String, dynamic>{'kernelBytes': kernelBytesBase64}, '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,
'',
);
}
}); });
vmService.registerService('compileExpression', 'Flutter Tools'); vmService.registerService('compileExpression', 'Flutter Tools');
} }
if (device != null) { if (device != null) {
vmService.registerServiceCallback('flutterMemoryInfo', (Map<String, dynamic> params) async { vmService.registerServiceCallback('flutterMemoryInfo', (Map<String, dynamic> params) async {
try { final MemoryInfo result = await device.queryMemoryInfo();
final MemoryInfo result = await device.queryMemoryInfo(); return <String, dynamic>{
return <String, dynamic>{ 'result': <String, Object>{
'result': <String, Object>{ 'type': 'Success',
'type': 'Success', ...result.toJson(),
...result.toJson(), }
} };
};
} on Exception catch (e, st) {
throw vm_service.RPCError(
'Error during memory info query $e\n$st',
RPCErrorCodes.kServerError,
'',
);
}
}); });
vmService.registerService('flutterMemoryInfo', 'Flutter Tools'); vmService.registerService('flutterMemoryInfo', 'Flutter Tools');
} }
...@@ -401,6 +331,30 @@ Future<vm_service.VmService> _connect( ...@@ -401,6 +331,30 @@ Future<vm_service.VmService> _connect(
return service; return service;
} }
String _validateRpcStringParam(String methodName, Map<String, dynamic> params, String paramName) {
final dynamic value = params[paramName];
if (value is! String || (value as String).isEmpty) {
throw vm_service.RPCError(
methodName,
RPCErrorCodes.kInvalidParams,
"Invalid '$paramName': $value",
);
}
return value as String;
}
bool _validateRpcBoolParam(String methodName, Map<String, dynamic> params, String paramName) {
final dynamic value = params[paramName];
if (value != null && value is! bool) {
throw vm_service.RPCError(
methodName,
RPCErrorCodes.kInvalidParams,
"Invalid '$paramName': $value",
);
}
return (value as bool) ?? false;
}
/// Peered to an Android/iOS FlutterView widget on a device. /// Peered to an Android/iOS FlutterView widget on a device.
class FlutterView { class FlutterView {
FlutterView({ FlutterView({
......
...@@ -2,8 +2,11 @@ ...@@ -2,8 +2,11 @@
// 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:io'; // ignore: dart_io_import
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:matcher/matcher.dart';
import 'package:vm_service/vm_service.dart'; import 'package:vm_service/vm_service.dart';
import 'package:vm_service/vm_service_io.dart'; import 'package:vm_service/vm_service_io.dart';
...@@ -13,31 +16,72 @@ import 'test_driver.dart'; ...@@ -13,31 +16,72 @@ import 'test_driver.dart';
import 'test_utils.dart'; import 'test_utils.dart';
void main() { void main() {
Directory tempDir; group('Flutter Tool VMService method', () {
FlutterRunTestDriver flutter; Directory tempDir;
FlutterRunTestDriver flutter;
VmService vmService;
setUpAll(() async {
tempDir = createResolvedTempDirectorySync('vmservice_integration_test.');
final BasicProject _project = BasicProject();
await _project.setUpIn(tempDir);
flutter = FlutterRunTestDriver(tempDir);
await flutter.run(withDebugger: true);
final int port = flutter.vmServicePort;
vmService = await vmServiceConnectUri('ws://localhost:$port/ws');
});
tearDownAll(() async {
await flutter?.stop();
tryToDelete(tempDir);
});
test('flutterVersion can be called', () async {
final Response response =
await vmService.callServiceExtension('s0.flutterVersion');
expect(response.type, 'Success');
expect(response.json, containsPair('frameworkRevisionShort', isNotNull));
expect(response.json, containsPair('engineRevisionShort', isNotNull));
});
test('flutterMemoryInfo can be called', () async {
final Response response =
await vmService.callServiceExtension('s0.flutterMemoryInfo');
expect(response.type, 'Success');
});
test('reloadSources can be called', () async {
final VM vm = await vmService.getVM();
final IsolateRef isolateRef = vm.isolates.first;
test('Flutter Tool VMService methods can be called', () async { final Response response = await vmService.callMethod('s0.reloadSources',
tempDir = createResolvedTempDirectorySync('vmservice_integration_test.'); isolateId: isolateRef.id);
expect(response.type, 'Success');
});
final BasicProject _project = BasicProject(); test('reloadSources fails on bad params', () async {
await _project.setUpIn(tempDir); final Future<Response> response =
vmService.callMethod('s0.reloadSources', isolateId: '');
expect(response, throwsA(const TypeMatcher<RPCError>()));
});
flutter = FlutterRunTestDriver(tempDir); test('hotRestart can be called', () async {
await flutter.run(withDebugger: true); final VM vm = await vmService.getVM();
final int port = flutter.vmServicePort; final IsolateRef isolateRef = vm.isolates.first;
final VmService vmService = await vmServiceConnectUri('ws://localhost:$port/ws');
final Response versionResponse = await vmService.callMethod('s0.flutterVersion'); final Response response =
expect(versionResponse.type, 'Success'); await vmService.callMethod('s0.hotRestart', isolateId: isolateRef.id);
expect(versionResponse.json, containsPair('frameworkRevisionShort', isNotNull)); expect(response.type, 'Success');
expect(versionResponse.json, containsPair('engineRevisionShort', isNotNull)); });
final Response memoryInfoResponse = await vmService.callMethod('s0.flutterMemoryInfo'); test('hotRestart fails on bad params', () async {
expect(memoryInfoResponse.type, 'Success'); final Future<Response> response = vmService.callMethod('s0.hotRestart',
}); args: <String, dynamic>{'pause': 'not_a_bool'});
expect(response, throwsA(const TypeMatcher<RPCError>()));
});
tearDown(() { // TODO(devoncarew): These tests fail on cirrus-ci windows.
tryToDelete(tempDir); }, skip: Platform.isWindows);
flutter?.stop();
});
} }
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