Unverified Commit 2d5ed986 authored by Danny Tuppeny's avatar Danny Tuppeny Committed by GitHub

Update integration tests to use vm_service_lib (#23937)

* Move integration tests to use vm_service_lib
* Turn off debug logging that was accidentally committed
* Run update-packages --force to fix checksum for new dev dependency
* Trim trailing whitespace
parent d04fa1a6
...@@ -77,6 +77,7 @@ dev_dependencies: ...@@ -77,6 +77,7 @@ dev_dependencies:
collection: 1.14.11 collection: 1.14.11
mockito: 4.0.0 mockito: 4.0.0
file_testing: 2.0.3 file_testing: 2.0.3
vm_service_lib: 0.3.10
http_multi_server: 2.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 2.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
js: 0.6.1+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.6.1+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade"
...@@ -93,4 +94,4 @@ dartdoc: ...@@ -93,4 +94,4 @@ dartdoc:
# Exclude this package from the hosted API docs. # Exclude this package from the hosted API docs.
nodoc: true nodoc: true
# PUBSPEC CHECKSUM: e719 # PUBSPEC CHECKSUM: 0163
...@@ -7,7 +7,7 @@ import 'dart:async'; ...@@ -7,7 +7,7 @@ import 'dart:async';
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:vm_service_client/vm_service_client.dart'; import 'package:vm_service_lib/vm_service_lib.dart';
import '../src/common.dart'; import '../src/common.dart';
import 'test_data/basic_project.dart'; import 'test_data/basic_project.dart';
...@@ -31,45 +31,45 @@ void main() { ...@@ -31,45 +31,45 @@ void main() {
tryToDelete(tempDir); tryToDelete(tempDir);
}); });
Future<VMIsolate> breakInBuildMethod(FlutterTestDriver flutter) async { Future<Isolate> breakInBuildMethod(FlutterTestDriver flutter) async {
return _flutter.breakAt( return _flutter.breakAt(
_project.buildMethodBreakpointUri, _project.buildMethodBreakpointUri,
_project.buildMethodBreakpointLine); _project.buildMethodBreakpointLine);
} }
Future<VMIsolate> breakInTopLevelFunction(FlutterTestDriver flutter) async { Future<Isolate> breakInTopLevelFunction(FlutterTestDriver flutter) async {
return _flutter.breakAt( return _flutter.breakAt(
_project.topLevelFunctionBreakpointUri, _project.topLevelFunctionBreakpointUri,
_project.topLevelFunctionBreakpointLine); _project.topLevelFunctionBreakpointLine);
} }
Future<void> evaluateTrivialExpressions() async { Future<void> evaluateTrivialExpressions() async {
VMInstanceRef res; InstanceRef res;
res = await _flutter.evaluateExpression('"test"'); res = await _flutter.evaluateInFrame('"test"');
expect(res is VMStringInstanceRef && res.value == 'test', isTrue); expect(res.kind == InstanceKind.kString && res.valueAsString == 'test', isTrue);
res = await _flutter.evaluateExpression('1'); res = await _flutter.evaluateInFrame('1');
expect(res is VMIntInstanceRef && res.value == 1, isTrue); expect(res.kind == InstanceKind.kInt && res.valueAsString == 1.toString(), isTrue);
res = await _flutter.evaluateExpression('true'); res = await _flutter.evaluateInFrame('true');
expect(res is VMBoolInstanceRef && res.value == true, isTrue); expect(res.kind == InstanceKind.kBool && res.valueAsString == true.toString(), isTrue);
} }
Future<void> evaluateComplexExpressions() async { Future<void> evaluateComplexExpressions() async {
final VMInstanceRef res = await _flutter.evaluateExpression('new DateTime.now().year'); final InstanceRef res = await _flutter.evaluateInFrame('new DateTime.now().year');
expect(res is VMIntInstanceRef && res.value == DateTime.now().year, isTrue); expect(res.kind == InstanceKind.kInt && res.valueAsString == DateTime.now().year.toString(), isTrue);
} }
Future<void> evaluateComplexReturningExpressions() async { Future<void> evaluateComplexReturningExpressions() async {
final DateTime now = DateTime.now(); final DateTime now = DateTime.now();
final VMInstanceRef resp = await _flutter.evaluateExpression('new DateTime.now()'); final InstanceRef resp = await _flutter.evaluateInFrame('new DateTime.now()');
expect(resp.klass.name, equals('DateTime')); expect(resp.classRef.name, equals('DateTime'));
// Ensure we got a reasonable approximation. The more accurate we try to // Ensure we got a reasonable approximation. The more accurate we try to
// make this, the more likely it'll fail due to differences in the time // make this, the more likely it'll fail due to differences in the time
// in the remote VM and the local VM at the time the code runs. // in the remote VM and the local VM at the time the code runs.
final VMStringInstanceRef res = await resp.evaluate(r'"$year-$month-$day"'); final InstanceRef res = await _flutter.evaluate(resp.id, r'"$year-$month-$day"');
expect(res.value, expect(res.valueAsString,
equals('${now.year}-${now.month}-${now.day}')); equals('${now.year}-${now.month}-${now.day}'));
} }
......
...@@ -6,7 +6,7 @@ import 'dart:async'; ...@@ -6,7 +6,7 @@ import 'dart:async';
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:vm_service_client/vm_service_client.dart'; import 'package:vm_service_lib/vm_service_lib.dart';
import '../src/common.dart'; import '../src/common.dart';
import 'test_data/hot_reload_project.dart'; import 'test_data/hot_reload_project.dart';
...@@ -55,10 +55,10 @@ void main() { ...@@ -55,10 +55,10 @@ void main() {
test('reload hits breakpoints after reload', () async { test('reload hits breakpoints after reload', () async {
await _flutter.run(withDebugger: true); await _flutter.run(withDebugger: true);
final VMIsolate isolate = await _flutter.breakAt( final Isolate isolate = await _flutter.breakAt(
_project.breakpointUri, _project.breakpointUri,
_project.breakpointLine); _project.breakpointLine);
expect(isolate.pauseEvent, isInstanceOf<VMPauseBreakpointEvent>()); expect(isolate.pauseEvent.kind, equals(EventKind.kPauseBreakpoint));
}); });
}, timeout: const Timeout.factor(6)); }, timeout: const Timeout.factor(6));
} }
...@@ -9,15 +9,13 @@ import 'package:file/file.dart'; ...@@ -9,15 +9,13 @@ 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:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'package:source_span/source_span.dart'; import 'package:vm_service_lib/vm_service_lib.dart';
import 'package:stream_channel/stream_channel.dart'; import 'package:vm_service_lib/vm_service_lib_io.dart';
import 'package:vm_service_client/vm_service_client.dart';
import 'package:web_socket_channel/io.dart';
import '../src/common.dart'; import '../src/common.dart';
// Set this to true for debugging to get JSON written to stdout. // Set this to true for debugging to get JSON written to stdout.
const bool _printJsonAndStderr = false; const bool _printDebugOutputToStdOut = false;
const Duration defaultTimeout = Duration(seconds: 40); const Duration defaultTimeout = Duration(seconds: 40);
const Duration appStartTimeout = Duration(seconds: 120); const Duration appStartTimeout = Duration(seconds: 120);
const Duration quitTimeout = Duration(seconds: 10); const Duration quitTimeout = Duration(seconds: 10);
...@@ -37,13 +35,12 @@ class FlutterTestDriver { ...@@ -37,13 +35,12 @@ class FlutterTestDriver {
String _lastResponse; String _lastResponse;
String _currentRunningAppId; String _currentRunningAppId;
Uri _vmServiceWsUri; Uri _vmServiceWsUri;
int _vmServicePort;
bool _hasExited = false; bool _hasExited = false;
VMServiceClient vmService; VmService _vmService;
String get lastErrorInfo => _errorBuffer.toString(); String get lastErrorInfo => _errorBuffer.toString();
Stream<String> get stdout => _stdout.stream; Stream<String> get stdout => _stdout.stream;
int get vmServicePort => _vmServicePort; int get vmServicePort => _vmServiceWsUri.port;
bool get hasExited => _hasExited; bool get hasExited => _hasExited;
String _debugPrint(String msg) { String _debugPrint(String msg) {
...@@ -51,7 +48,7 @@ class FlutterTestDriver { ...@@ -51,7 +48,7 @@ class FlutterTestDriver {
final String truncatedMsg = final String truncatedMsg =
msg.length > maxLength ? msg.substring(0, maxLength) + '...' : msg; msg.length > maxLength ? msg.substring(0, maxLength) + '...' : msg;
_allMessages.add(truncatedMsg); _allMessages.add(truncatedMsg);
if (_printJsonAndStderr) { if (_printDebugOutputToStdOut) {
print('$_logPrefix$truncatedMsg'); print('$_logPrefix$truncatedMsg');
} }
return msg; return msg;
...@@ -141,22 +138,21 @@ class FlutterTestDriver { ...@@ -141,22 +138,21 @@ class FlutterTestDriver {
timeout: appStartTimeout); timeout: appStartTimeout);
final String wsUriString = debugPort['params']['wsUri']; final String wsUriString = debugPort['params']['wsUri'];
_vmServiceWsUri = Uri.parse(wsUriString); _vmServiceWsUri = Uri.parse(wsUriString);
_vmServicePort = debugPort['params']['port']; _vmService =
// Proxy the stream/sink for the VM Client so we can debugPrint it. await vmServiceConnectUri(_vmServiceWsUri.toString());
final StreamChannel<String> channel = IOWebSocketChannel.connect(_vmServiceWsUri) _vmService.onSend.listen((String s) => _debugPrint('==> $s'));
.cast<String>() _vmService.onReceive.listen((String s) => _debugPrint('<== $s'));
.changeStream((Stream<String> stream) => stream.map<String>(_debugPrint)) await Future.wait(<Future<Success>>[
.changeSink((StreamSink<String> sink) => _vmService.streamListen('Isolate'),
StreamController<String>() _vmService.streamListen('Debug'),
..stream.listen((String s) => sink.add(_debugPrint(s)))); ]);
vmService = VMServiceClient(channel);
// Because we start paused, resume so the app is in a "running" state as // Because we start paused, resume so the app is in a "running" state as
// expected by tests. Tests will reload/restart as required if they need // expected by tests. Tests will reload/restart as required if they need
// to hit breakpoints, etc. // to hit breakpoints, etc.
await waitForPause(); await waitForPause();
if (pauseOnExceptions) { if (pauseOnExceptions) {
await (await getFlutterIsolate()).setExceptionPauseMode(VMExceptionPauseMode.unhandled); await _vmService.setExceptionPauseMode(await _getFlutterIsolateId(), ExceptionPauseMode.kUnhandled);
} }
await resume(wait: false); await resume(wait: false);
} }
...@@ -175,7 +171,7 @@ class FlutterTestDriver { ...@@ -175,7 +171,7 @@ class FlutterTestDriver {
final dynamic hotReloadResp = await _sendRequest( final dynamic hotReloadResp = await _sendRequest(
'app.restart', 'app.restart',
<String, dynamic>{'appId': _currentRunningAppId, 'fullRestart': fullRestart, 'pause': pause} <String, dynamic>{'appId': _currentRunningAppId, 'fullRestart': fullRestart, 'pause': pause},
); );
if (hotReloadResp == null || hotReloadResp['code'] != 0) if (hotReloadResp == null || hotReloadResp['code'] != 0)
...@@ -183,11 +179,9 @@ class FlutterTestDriver { ...@@ -183,11 +179,9 @@ class FlutterTestDriver {
} }
Future<int> detach() async { Future<int> detach() async {
if (vmService != null) { if (_vmService != null) {
_debugPrint('Closing VM service'); _debugPrint('Closing VM service');
await vmService.close() _vmService.dispose();
.timeout(quitTimeout,
onTimeout: () { _debugPrint('VM Service did not quit within $quitTimeout'); });
} }
if (_currentRunningAppId != null) { if (_currentRunningAppId != null) {
_debugPrint('Detaching from app'); _debugPrint('Detaching from app');
...@@ -195,11 +189,11 @@ class FlutterTestDriver { ...@@ -195,11 +189,11 @@ class FlutterTestDriver {
_proc.exitCode, _proc.exitCode,
_sendRequest( _sendRequest(
'app.detach', 'app.detach',
<String, dynamic>{'appId': _currentRunningAppId} <String, dynamic>{'appId': _currentRunningAppId},
), ),
]).timeout( ]).timeout(
quitTimeout, quitTimeout,
onTimeout: () { _debugPrint('app.detach did not return within $quitTimeout'); } onTimeout: () { _debugPrint('app.detach did not return within $quitTimeout'); },
); );
_currentRunningAppId = null; _currentRunningAppId = null;
} }
...@@ -208,11 +202,9 @@ class FlutterTestDriver { ...@@ -208,11 +202,9 @@ class FlutterTestDriver {
} }
Future<int> stop() async { Future<int> stop() async {
if (vmService != null) { if (_vmService != null) {
_debugPrint('Closing VM service'); _debugPrint('Closing VM service');
await vmService.close() _vmService.dispose();
.timeout(quitTimeout,
onTimeout: () { _debugPrint('VM Service did not quit within $quitTimeout'); });
} }
if (_currentRunningAppId != null) { if (_currentRunningAppId != null) {
_debugPrint('Stopping app'); _debugPrint('Stopping app');
...@@ -220,11 +212,11 @@ class FlutterTestDriver { ...@@ -220,11 +212,11 @@ class FlutterTestDriver {
_proc.exitCode, _proc.exitCode,
_sendRequest( _sendRequest(
'app.stop', 'app.stop',
<String, dynamic>{'appId': _currentRunningAppId} <String, dynamic>{'appId': _currentRunningAppId},
), ),
]).timeout( ]).timeout(
quitTimeout, quitTimeout,
onTimeout: () { _debugPrint('app.stop did not return within $quitTimeout'); } onTimeout: () { _debugPrint('app.stop did not return within $quitTimeout'); },
); );
_currentRunningAppId = null; _currentRunningAppId = null;
} }
...@@ -248,43 +240,73 @@ class FlutterTestDriver { ...@@ -248,43 +240,73 @@ class FlutterTestDriver {
return _proc.exitCode; return _proc.exitCode;
} }
Future<VMIsolate> getFlutterIsolate() async { String _flutterIsolateId;
Future<String> _getFlutterIsolateId() async {
// Currently these tests only have a single isolate. If this // Currently these tests only have a single isolate. If this
// ceases to be the case, this code will need changing. // ceases to be the case, this code will need changing.
final VM vm = await vmService.getVM(); if (_flutterIsolateId == null) {
return await vm.isolates.single.load(); final VM vm = await _vmService.getVM();
_flutterIsolateId = vm.isolates.first.id;
}
return _flutterIsolateId;
}
Future<Isolate> _getFlutterIsolate() async {
final Isolate isolate = await _vmService.getIsolate(await _getFlutterIsolateId());
return isolate;
} }
Future<void> addBreakpoint(Uri uri, int line) async { Future<void> addBreakpoint(Uri uri, int line) async {
final VMIsolate isolate = await getFlutterIsolate();
_debugPrint('Sending breakpoint for $uri:$line'); _debugPrint('Sending breakpoint for $uri:$line');
await isolate.addBreakpoint(uri, line); await _vmService.addBreakpointWithScriptUri(
await _getFlutterIsolateId(), uri.toString(), line);
} }
Future<VMIsolate> waitForPause() async { Future<Isolate> waitForPause() async {
final VM vm = await vmService.getVM();
final VMIsolate isolate = await vm.isolates.first.load();
_debugPrint('Waiting for isolate to pause'); _debugPrint('Waiting for isolate to pause');
await _timeoutWithMessages<dynamic>(isolate.waitUntilPaused, final String flutterIsolate = await _getFlutterIsolateId();
Future<Isolate> waitForPause() async {
final Completer<Event> pauseEvent = Completer<Event>();
// Start listening for pause events.
final StreamSubscription<Event> pauseSub = _vmService.onDebugEvent
.where((Event event) =>
event.isolate.id == flutterIsolate &&
event.kind.startsWith('Pause'))
.listen(pauseEvent.complete);
// But also check if the isolate was already paused (only after we've set
// up the sub) to avoid races. If it was paused, we don't need to wait
// for the event.
final Isolate isolate = await _vmService.getIsolate(flutterIsolate);
if (!isolate.pauseEvent.kind.startsWith('Pause')) {
await pauseEvent.future;
}
// Cancel the sub on either of the above.
await pauseSub.cancel();
return _getFlutterIsolate();
}
return _timeoutWithMessages<Isolate>(waitForPause,
message: 'Isolate did not pause'); message: 'Isolate did not pause');
return isolate.load();
} }
Future<VMIsolate> resume({ bool wait = true }) => _resume(wait: wait); Future<Isolate> resume({bool wait = true}) => _resume(wait: wait);
Future<VMIsolate> stepOver({ bool wait = true }) => _resume(step: VMStep.over, wait: wait); Future<Isolate> stepOver({bool wait = true}) => _resume(step: StepOption.kOver, wait: wait);
Future<VMIsolate> stepInto({ bool wait = true }) => _resume(step: VMStep.into, wait: wait); Future<Isolate> stepInto({bool wait = true}) => _resume(step: StepOption.kInto, wait: wait);
Future<VMIsolate> stepOut({ bool wait = true }) => _resume(step: VMStep.out, wait: wait); Future<Isolate> stepOut({bool wait = true}) => _resume(step: StepOption.kOut, wait: wait);
Future<VMIsolate> _resume({VMStep step, bool wait = true}) async { Future<Isolate> _resume({String step, bool wait = true}) async {
final VM vm = await vmService.getVM();
final VMIsolate isolate = await vm.isolates.first.load();
_debugPrint('Sending resume ($step)'); _debugPrint('Sending resume ($step)');
await _timeoutWithMessages<dynamic>(() => isolate.resume(step: step), await _timeoutWithMessages<dynamic>(() async => _vmService.resume(await _getFlutterIsolateId(), step: step),
message: 'Isolate did not respond to resume ($step)'); message: 'Isolate did not respond to resume ($step)');
return wait ? waitForPause() : null; return wait ? waitForPause() : null;
} }
Future<VMIsolate> breakAt(Uri uri, int line, { bool restart = false }) async { Future<Isolate> breakAt(Uri uri, int line, {bool restart = false}) async {
if (restart) { if (restart) {
// For a hot restart, we need to send the breakpoints after the restart // For a hot restart, we need to send the breakpoints after the restart
// so we need to pause during the restart to avoid races. // so we need to pause during the restart to avoid races.
...@@ -298,26 +320,47 @@ class FlutterTestDriver { ...@@ -298,26 +320,47 @@ class FlutterTestDriver {
} }
} }
Future<VMInstanceRef> evaluateExpression(String expression) async { Future<InstanceRef> evaluateInFrame(String expression) async {
final VMFrame topFrame = await getTopStackFrame(); return _timeoutWithMessages<InstanceRef>(
return _timeoutWithMessages<VMInstanceRef>(() => topFrame.evaluate(expression), () async => await _vmService.evaluateInFrame(await _getFlutterIsolateId(), 0, expression),
message: 'Timed out evaluating expression ($expression)'); message: 'Timed out evaluating expression ($expression)');
} }
Future<VMFrame> getTopStackFrame() async { Future<InstanceRef> evaluate(String targetId, String expression) async {
final VM vm = await vmService.getVM(); return _timeoutWithMessages<InstanceRef>(
final VMIsolate isolate = await vm.isolates.first.load(); () async => await _vmService.evaluate(await _getFlutterIsolateId(), targetId, expression),
final VMStack stack = await isolate.getStack(); message: 'Timed out evaluating expression ($expression for $targetId)');
}
Future<Frame> getTopStackFrame() async {
final String flutterIsolateId = await _getFlutterIsolateId();
final Stack stack = await _vmService.getStack(flutterIsolateId);
if (stack.frames.isEmpty) { if (stack.frames.isEmpty) {
throw Exception('Stack is empty'); throw Exception('Stack is empty');
} }
return stack.frames.first; return stack.frames.first;
} }
Future<FileLocation> getSourceLocation() async { Future<SourcePosition> getSourceLocation() async {
final VMFrame frame = await getTopStackFrame(); final String flutterIsolateId = await _getFlutterIsolateId();
final VMScript script = await frame.location.script.load(); final Frame frame = await getTopStackFrame();
return script.sourceLocation(frame.location.token); final Script script = await _vmService.getObject(flutterIsolateId, frame.location.script.id);
return _lookupTokenPos(script.tokenPosTable, frame.location.tokenPos);
}
SourcePosition _lookupTokenPos(List<List<int>> table, int tokenPos) {
for (List<int> row in table) {
final int lineNumber = row[0];
int index = 1;
for (index = 1; index < row.length - 1; index += 2) {
if (row[index] == tokenPos) {
return SourcePosition(lineNumber, row[index + 1]);
}
}
}
return null;
} }
Future<Map<String, dynamic>> _waitFor({ Future<Map<String, dynamic>> _waitFor({
...@@ -428,3 +471,10 @@ class FlutterTestDriver { ...@@ -428,3 +471,10 @@ class FlutterTestDriver {
Stream<String> _transformToLines(Stream<List<int>> byteStream) { Stream<String> _transformToLines(Stream<List<int>> byteStream) {
return byteStream.transform<String>(utf8.decoder).transform<String>(const LineSplitter()); return byteStream.transform<String>(utf8.decoder).transform<String>(const LineSplitter());
} }
class SourcePosition {
SourcePosition(this.line, this.column);
final int line;
final int column;
}
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