Unverified Commit 4851bd51 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[devicelab] remove vm_service_client from devicelab (#78559)

parent 2dbe1ec8
...@@ -11,7 +11,8 @@ import 'package:flutter_devicelab/framework/framework.dart'; ...@@ -11,7 +11,8 @@ import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/framework/task_result.dart'; import 'package:flutter_devicelab/framework/task_result.dart';
import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/framework/utils.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'package:vm_service_client/vm_service_client.dart'; import 'package:vm_service/vm_service.dart';
import 'package:vm_service/vm_service_io.dart';
void main() { void main() {
task(() async { task(() async {
...@@ -54,52 +55,58 @@ void main() { ...@@ -54,52 +55,58 @@ void main() {
if (!ok) if (!ok)
throw 'Failed to run test app.'; throw 'Failed to run test app.';
final VMServiceClient client = VMServiceClient.connect('ws://localhost:$vmServicePort/ws'); final VmService client = await vmServiceConnectUri('ws://localhost:$vmServicePort/ws');
final VM vm = await client.getVM(); final VM vm = await client.getVM();
final VMIsolateRef isolate = vm.isolates.first; final IsolateRef isolate = vm.isolates.first;
final StreamController<VMExtensionEvent> frameEventsController = StreamController<VMExtensionEvent>(); final StreamController<Event> frameEventsController = StreamController<Event>();
final StreamController<VMExtensionEvent> navigationEventsController = StreamController<VMExtensionEvent>(); final StreamController<Event> navigationEventsController = StreamController<Event>();
isolate.onExtensionEvent.listen((VMExtensionEvent event) { try {
if (event.kind == 'Flutter.Frame') { await client.streamListen(EventKind.kExtension);
} catch (err) {
// Do nothing on errors.
}
client.onExtensionEvent.listen((Event event) {
if (event.extensionKind == 'Flutter.Frame') {
frameEventsController.add(event); frameEventsController.add(event);
} else if (event.kind == 'Flutter.Navigation') { } else if (event.extensionKind == 'Flutter.Navigation') {
navigationEventsController.add(event); navigationEventsController.add(event);
} }
}); });
final Stream<VMExtensionEvent> frameEvents = frameEventsController.stream; final Stream<Event> frameEvents = frameEventsController.stream;
final Stream<VMExtensionEvent> navigationEvents = navigationEventsController.stream; final Stream<Event> navigationEvents = navigationEventsController.stream;
print('reassembling app...'); print('reassembling app...');
final Future<VMExtensionEvent> frameFuture = frameEvents.first; final Future<Event> frameFuture = frameEvents.first;
await isolate.invokeExtension('ext.flutter.reassemble'); await client.callServiceExtension('ext.flutter.reassemble', isolateId: isolate.id);
// ensure we get an event // ensure we get an event
final VMExtensionEvent event = await frameFuture; final Event event = await frameFuture;
print('${event.kind}: ${event.data}'); print('${event.kind}: ${event.data}');
// validate the fields // validate the fields
// {number: 8, startTime: 0, elapsed: 1437, build: 600, raster: 800} // {number: 8, startTime: 0, elapsed: 1437, build: 600, raster: 800}
expect(event.data['number'] is int); print(event.extensionData.data);
expect((event.data['number'] as int) >= 0); expect(event.extensionData.data['number'] is int);
expect(event.data['startTime'] is int); expect((event.extensionData.data['number'] as int) >= 0);
expect((event.data['startTime'] as int) >= 0); expect(event.extensionData.data['startTime'] is int);
expect(event.data['elapsed'] is int); expect((event.extensionData.data['startTime'] as int) >= 0);
expect((event.data['elapsed'] as int) >= 0); expect(event.extensionData.data['elapsed'] is int);
expect(event.data['build'] is int); expect((event.extensionData.data['elapsed'] as int) >= 0);
expect((event.data['build'] as int) >= 0); expect(event.extensionData.data['build'] is int);
expect(event.data['raster'] is int); expect((event.extensionData.data['build'] as int) >= 0);
expect((event.data['raster'] as int) >= 0); expect(event.extensionData.data['raster'] is int);
expect((event.extensionData.data['raster'] as int) >= 0);
final Future<VMExtensionEvent> navigationFuture = navigationEvents.first; final Future<Event> navigationFuture = navigationEvents.first;
// This tap triggers a navigation event. // This tap triggers a navigation event.
device.tap(100, 200); device.tap(100, 200);
final VMExtensionEvent navigationEvent = await navigationFuture; final Event navigationEvent = await navigationFuture;
// validate the fields // validate the fields
expect(navigationEvent.data['route'] is Map<dynamic, dynamic>); expect(navigationEvent.extensionData.data['route'] is Map<dynamic, dynamic>);
final Map<dynamic, dynamic> route = navigationEvent.data['route'] as Map<dynamic, dynamic>; final Map<dynamic, dynamic> route = navigationEvent.extensionData.data['route'] as Map<dynamic, dynamic>;
expect(route['description'] is String); expect(route['description'] is String);
expect(route['settings'] is Map<dynamic, dynamic>); expect(route['settings'] is Map<dynamic, dynamic>);
final Map<dynamic, dynamic> settings = route['settings'] as Map<dynamic, dynamic>; final Map<dynamic, dynamic> settings = route['settings'] as Map<dynamic, dynamic>;
......
...@@ -7,7 +7,7 @@ import 'dart:convert'; ...@@ -7,7 +7,7 @@ import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:vm_service_client/vm_service_client.dart'; import 'package:vm_service/vm_service.dart';
import 'adb.dart'; import 'adb.dart';
import 'cocoon.dart'; import 'cocoon.dart';
...@@ -131,8 +131,12 @@ Future<TaskResult> runTask( ...@@ -131,8 +131,12 @@ Future<TaskResult> runTask(
}); });
try { try {
final VMIsolateRef isolate = await _connectToRunnerIsolate(await uri.future); final ConnectionResult result = await _connectToRunnerIsolate(await uri.future);
final Map<String, dynamic> taskResultJson = await isolate.invokeExtension('ext.cocoonRunTask', isolateParams) as Map<String, dynamic>; final Map<String, dynamic> taskResultJson = (await result.vmService.callServiceExtension(
'ext.cocoonRunTask',
args: isolateParams,
isolateId: result.isolate.id,
)).json;
final TaskResult taskResult = TaskResult.fromJson(taskResultJson); final TaskResult taskResult = TaskResult.fromJson(taskResultJson);
await runner.exitCode; await runner.exitCode;
return taskResult; return taskResult;
...@@ -144,14 +148,13 @@ Future<TaskResult> runTask( ...@@ -144,14 +148,13 @@ Future<TaskResult> runTask(
} }
} }
Future<VMIsolateRef> _connectToRunnerIsolate(Uri vmServiceUri) async { Future<ConnectionResult> _connectToRunnerIsolate(Uri vmServiceUri) async {
final List<String> pathSegments = <String>[ final List<String> pathSegments = <String>[
// Add authentication code. // Add authentication code.
if (vmServiceUri.pathSegments.isNotEmpty) vmServiceUri.pathSegments[0], if (vmServiceUri.pathSegments.isNotEmpty) vmServiceUri.pathSegments[0],
'ws', 'ws',
]; ];
final String url = vmServiceUri.replace(scheme: 'ws', pathSegments: final String url = vmServiceUri.replace(scheme: 'ws', pathSegments: pathSegments).toString();
pathSegments).toString();
final Stopwatch stopwatch = Stopwatch()..start(); final Stopwatch stopwatch = Stopwatch()..start();
while (true) { while (true) {
...@@ -160,13 +163,17 @@ Future<VMIsolateRef> _connectToRunnerIsolate(Uri vmServiceUri) async { ...@@ -160,13 +163,17 @@ Future<VMIsolateRef> _connectToRunnerIsolate(Uri vmServiceUri) async {
await (await WebSocket.connect(url)).close(); await (await WebSocket.connect(url)).close();
// Look up the isolate. // Look up the isolate.
final VMServiceClient client = VMServiceClient.connect(url); final VmService client = await vmServiceConnectUri(url);
final VM vm = await client.getVM(); VM vm = await client.getVM();
final VMIsolateRef isolate = vm.isolates.single; while (vm.isolates.isEmpty) {
final String response = await isolate.invokeExtension('ext.cocoonRunnerReady') as String; await Future<void>.delayed(const Duration(seconds: 1));
if (response != 'ready') vm = await client.getVM();
}
final IsolateRef isolate = vm.isolates.first;
final Response response = await client.callServiceExtension('ext.cocoonRunnerReady', isolateId: isolate.id);
if (response.json['response'] != 'ready')
throw 'not ready yet'; throw 'not ready yet';
return isolate; return ConnectionResult(client, isolate);
} catch (error) { } catch (error) {
if (stopwatch.elapsed > const Duration(seconds: 10)) if (stopwatch.elapsed > const Duration(seconds: 10))
print('VM service still not ready after ${stopwatch.elapsed}: $error\nContinuing to retry...'); print('VM service still not ready after ${stopwatch.elapsed}: $error\nContinuing to retry...');
...@@ -174,3 +181,38 @@ Future<VMIsolateRef> _connectToRunnerIsolate(Uri vmServiceUri) async { ...@@ -174,3 +181,38 @@ Future<VMIsolateRef> _connectToRunnerIsolate(Uri vmServiceUri) async {
} }
} }
} }
class ConnectionResult {
ConnectionResult(this.vmService, this.isolate);
final VmService vmService;
final IsolateRef isolate;
}
/// The cocoon client sends an invalid VM service response, we need to intercept it.
Future<VmService> vmServiceConnectUri(String wsUri, {Log log}) async {
final WebSocket socket = await WebSocket.connect(wsUri);
final StreamController<dynamic> controller = StreamController<dynamic>();
final Completer<dynamic> streamClosedCompleter = Completer<dynamic>();
socket.listen(
(dynamic data) {
final Map<String, dynamic> rawData = json.decode(data as String) as Map<String, dynamic> ;
if (rawData['result'] == 'ready') {
rawData['result'] = <String, dynamic>{'response': 'ready'};
controller.add(json.encode(rawData));
} else {
controller.add(data);
}
},
onError: (dynamic err, StackTrace stackTrace) => controller.addError(err, stackTrace),
onDone: () => streamClosedCompleter.complete(),
);
return VmService(
controller.stream,
(String message) => socket.add(message),
log: log,
disposeHandler: () => socket.close(),
streamClosed: streamClosedCompleter.future,
);
}
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