Skip to content

Commit

Permalink
Fix shell launcher test (dart2 compliant and stop polling) (flutter#5009
Browse files Browse the repository at this point in the history
)

* Make test Dart2 compliant

* Use service and debug events instead of polling to wait for isolate to start, run and resume.

* Refactor into _onServiceEvent. Wait for 'paused' event instead of 'isolate runnable'.
  • Loading branch information
aam authored Apr 17, 2018
1 parent 75851f0 commit 98f6c2d
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 106 deletions.
2 changes: 1 addition & 1 deletion shell/testing/observatory/empty_main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

main() {
void main() {
}
19 changes: 9 additions & 10 deletions shell/testing/observatory/launcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,23 @@ import 'dart:convert';
import 'dart:io';

class ShellProcess {
final Completer _observatoryUriCompleter = new Completer();
final Completer<Uri> _observatoryUriCompleter = new Completer<Uri>();
final Process _process;

ShellProcess(this._process) {
assert(_process != null);
ShellProcess(this._process) : assert(_process != null) {
// Scan stdout and scrape the Observatory Uri.
_process.stdout.transform(UTF8.decoder)
.transform(new LineSplitter()).listen((line) {
_process.stdout.transform(utf8.decoder)
.transform(const LineSplitter()).listen((String line) {
const String observatoryUriPrefix = 'Observatory listening on ';
if (line.startsWith(observatoryUriPrefix)) {
print(line);
Uri uri = Uri.parse(line.substring(observatoryUriPrefix.length));
final Uri uri = Uri.parse(line.substring(observatoryUriPrefix.length));
_observatoryUriCompleter.complete(uri);
}
});
}

Future kill() async {
Future<bool> kill() async {
if (_process == null) {
return false;
}
Expand All @@ -39,7 +38,7 @@ class ShellProcess {
}

class ShellLauncher {
final List<String> args = [
final List<String> args = <String>[
'--observatory-port=0',
'--non-interactive',
'--run-forever',
Expand All @@ -60,13 +59,13 @@ class ShellLauncher {

Future<ShellProcess> launch() async {
try {
List<String> shellArguments = [];
final List<String> shellArguments = <String>[];
if (startPaused) {
shellArguments.add('--start-paused');
}
shellArguments.addAll(args);
print('Launching $shellExecutablePath $shellArguments');
var process = await Process.start(shellExecutablePath, shellArguments);
final Process process = await Process.start(shellExecutablePath, shellArguments);
return new ShellProcess(process);
} catch (e) {
print('Error launching shell: $e');
Expand Down
90 changes: 65 additions & 25 deletions shell/testing/observatory/service_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,60 +4,100 @@

library observatory_sky_shell_service_client;


import 'dart:async';
import 'dart:convert';
import 'dart:io';


class ServiceClient {
ServiceClient(this.client) {
Completer<dynamic> isolateStartedId;
Completer<dynamic> isolatePausedId;
Completer<dynamic> isolateResumeId;

ServiceClient(this.client, {this.isolateStartedId, this.isolatePausedId,
this.isolateResumeId}) {
client.listen(_onData,
onError: _onError,
cancelOnError: true);
}

Future<Map> invokeRPC(String method, [Map params]) async {
var key = _createKey();
var request = JSON.encode({
Future<Map<String, dynamic>> invokeRPC(String method, [Map<String, dynamic> params]) async {
final String key = _createKey();
final String request = json.encode(<String, dynamic>{
'jsonrpc': '2.0',
'method': method,
'params': params == null ? {} : params,
'params': params == null ? <String, dynamic>{} : params,
'id': key,
});
client.add(request);
var completer = new Completer();
_outstanding_requests[key] = completer;
final Completer<Map<String, dynamic>> completer = new Completer<Map<String, dynamic>>();
_outstandingRequests[key] = completer;
print('-> $key ($method)');
return completer.future;
}

String _createKey() {
var key = '$_id';
final String key = '$_id';
_id++;
return key;
}

void _onData(String message) {
var response = JSON.decode(message);
var key = response['id'];
print('<- $key');
var completer = _outstanding_requests.remove(key);
assert(completer != null);
var result = response['result'];
var error = response['error'];
if (error != null) {
assert(result == null);
completer.completeError(error);
void _onData(dynamic message) {
final Map<String, dynamic> response = json.decode(message);
final dynamic key = response['id'];
if (key != null) {
print('<- $key');
final dynamic completer = _outstandingRequests.remove(key);
assert(completer != null);
final dynamic result = response['result'];
final dynamic error = response['error'];
if (error != null) {
assert(result == null);
completer.completeError(error);
} else {
assert(result != null);
completer.complete(result);
}
} else {
assert(result != null);
completer.complete(result);
if (response['method'] == 'streamNotify') {
_onServiceEvent(response['params']);
}
}
}

void _onServiceEvent(Map<String, dynamic> params) {
if (params == null) {
return;
}
final Map<String, dynamic> event = params['event'];
if (event == null || event['type'] != 'Event') {
return;
}
final dynamic isolateId = event['isolate']['id'];
switch (params['streamId']) {
case 'Isolate':
if (event['kind'] == 'IsolateStarted') {
isolateStartedId?.complete(isolateId);
}
break;
case 'Debug':
switch (event['kind']) {
case 'Resume':
isolateResumeId?.complete(isolateId);
break;
case 'PauseStart':
isolatePausedId?.complete(isolateId);
break;
}
break;
}
}

void _onError(error) {
void _onError(dynamic error) {
print('WebSocket error: $error');
}

final WebSocket client;
final Map<String, Completer> _outstanding_requests = <String, Completer>{};
var _id = 1;
final Map<String, Completer<dynamic>> _outstandingRequests = <String, Completer<dynamic>>{};
int _id = 1;
}
Loading

0 comments on commit 98f6c2d

Please sign in to comment.