From af59a935ccb2d0115bd0dea1fb11b2306803e935 Mon Sep 17 00:00:00 2001 From: Alexander Aprelev Date: Fri, 13 Apr 2018 23:23:07 -0700 Subject: [PATCH 1/3] Make test Dart2 compliant --- shell/testing/observatory/empty_main.dart | 2 +- shell/testing/observatory/launcher.dart | 19 ++-- shell/testing/observatory/service_client.dart | 35 ++++---- shell/testing/observatory/test.dart | 86 +++++++++---------- 4 files changed, 71 insertions(+), 71 deletions(-) diff --git a/shell/testing/observatory/empty_main.dart b/shell/testing/observatory/empty_main.dart index bf3b7d4496935..0d653a819f728 100644 --- a/shell/testing/observatory/empty_main.dart +++ b/shell/testing/observatory/empty_main.dart @@ -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() { } diff --git a/shell/testing/observatory/launcher.dart b/shell/testing/observatory/launcher.dart index e3f6671cac33c..9a88555199538 100644 --- a/shell/testing/observatory/launcher.dart +++ b/shell/testing/observatory/launcher.dart @@ -9,24 +9,23 @@ import 'dart:convert'; import 'dart:io'; class ShellProcess { - final Completer _observatoryUriCompleter = new Completer(); + final Completer _observatoryUriCompleter = new Completer(); 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 kill() async { if (_process == null) { return false; } @@ -39,7 +38,7 @@ class ShellProcess { } class ShellLauncher { - final List args = [ + final List args = [ '--observatory-port=0', '--non-interactive', '--run-forever', @@ -60,13 +59,13 @@ class ShellLauncher { Future launch() async { try { - List shellArguments = []; + final List shellArguments = []; 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'); diff --git a/shell/testing/observatory/service_client.dart b/shell/testing/observatory/service_client.dart index 00b62e8df9162..45bf0488b4e45 100644 --- a/shell/testing/observatory/service_client.dart +++ b/shell/testing/observatory/service_client.dart @@ -4,9 +4,10 @@ library observatory_sky_shell_service_client; - import 'dart:async'; import 'dart:convert'; +import 'dart:io'; + class ServiceClient { ServiceClient(this.client) { @@ -15,35 +16,35 @@ class ServiceClient { cancelOnError: true); } - Future invokeRPC(String method, [Map params]) async { - var key = _createKey(); - var request = JSON.encode({ + Future> invokeRPC(String method, [Map params]) async { + final String key = _createKey(); + final String request = json.encode({ 'jsonrpc': '2.0', 'method': method, - 'params': params == null ? {} : params, + 'params': params == null ? {} : params, 'id': key, }); client.add(request); - var completer = new Completer(); - _outstanding_requests[key] = completer; + final Completer> completer = new Completer>(); + _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']; + void _onData(dynamic message) { + final Map response = json.decode(message); + final dynamic key = response['id']; print('<- $key'); - var completer = _outstanding_requests.remove(key); + final dynamic completer = _outstandingRequests.remove(key); assert(completer != null); - var result = response['result']; - var error = response['error']; + final dynamic result = response['result']; + final dynamic error = response['error']; if (error != null) { assert(result == null); completer.completeError(error); @@ -53,11 +54,11 @@ class ServiceClient { } } - void _onError(error) { + void _onError(dynamic error) { print('WebSocket error: $error'); } final WebSocket client; - final Map _outstanding_requests = {}; - var _id = 1; + final Map> _outstandingRequests = >{}; + int _id = 1; } diff --git a/shell/testing/observatory/test.dart b/shell/testing/observatory/test.dart index 5786153ed8951..843bd1c5917db 100644 --- a/shell/testing/observatory/test.dart +++ b/shell/testing/observatory/test.dart @@ -12,35 +12,35 @@ import 'launcher.dart'; import 'service_client.dart'; class Expect { - static equals(dynamic actual, dynamic expected) { + static void equals(dynamic actual, dynamic expected) { if (actual != expected) { throw 'Expected $actual == $expected'; } } - static contains(String needle, String haystack) { + static void contains(String needle, String haystack) { if (!haystack.contains(needle)) { throw 'Expected $haystack to contain $needle'; } } - static isTrue(bool tf) { + static void isTrue(bool tf) { if (tf != true) { - throw 'Expected $a to be true'; + throw 'Expected $tf to be true'; } } - static isFalse(bool tf) { + static void isFalse(bool tf) { if (tf != false) { - throw 'Expected $a to be false'; + throw 'Expected $tf to be false'; } } - static notExecuted() { + static void notExecuted() { throw 'Should not have hit'; } - static isNotNull(dynamic a) { + static void isNotNull(dynamic a) { if (a == null) { throw 'Expected $a to not be null'; } @@ -48,32 +48,32 @@ class Expect { } Future readResponse(HttpClientResponse response) { - var completer = new Completer(); - var contents = new StringBuffer(); - response.transform(UTF8.decoder).listen((String data) { + final Completer completer = new Completer(); + final StringBuffer contents = new StringBuffer(); + response.transform(utf8.decoder).listen((String data) { contents.write(data); }, onDone: () => completer.complete(contents.toString())); return completer.future; } // Test accessing the service protocol over http. -Future testHttpProtocolRequest(Uri uri) async { +Future testHttpProtocolRequest(Uri uri) async { uri = uri.replace(path: 'getVM'); - HttpClient client = new HttpClient(); - HttpClientRequest request = await client.getUrl(uri); - HttpClientResponse response = await request.close(); + final HttpClient client = new HttpClient(); + final HttpClientRequest request = await client.getUrl(uri); + final HttpClientResponse response = await request.close(); Expect.equals(response.statusCode, 200); - Map responseAsMap = JSON.decode(await readResponse(response)); - Expect.equals(responseAsMap['jsonrpc'], "2.0"); + final Map responseAsMap = json.decode(await readResponse(response)); + Expect.equals(responseAsMap['jsonrpc'], '2.0'); client.close(); } // Test accessing the service protocol over ws. -Future testWebSocketProtocolRequest(Uri uri) async { +Future testWebSocketProtocolRequest(Uri uri) async { uri = uri.replace(scheme: 'ws', path: 'ws'); - WebSocket webSocketClient = await WebSocket.connect(uri.toString()); - ServiceClient serviceClient = new ServiceClient(webSocketClient); - Map response = await serviceClient.invokeRPC('getVM'); + final WebSocket webSocketClient = await WebSocket.connect(uri.toString()); + final ServiceClient serviceClient = new ServiceClient(webSocketClient); + final Map response = await serviceClient.invokeRPC('getVM'); Expect.equals(response['type'], 'VM'); try { await serviceClient.invokeRPC('BART_SIMPSON'); @@ -85,25 +85,25 @@ Future testWebSocketProtocolRequest(Uri uri) async { } // Test accessing an Observatory UI asset. -Future testHttpAssetRequest(Uri uri) async { +Future testHttpAssetRequest(Uri uri) async { uri = uri.replace(path: 'third_party/trace_viewer_full.html'); - HttpClient client = new HttpClient(); - HttpClientRequest request = await client.getUrl(uri); - HttpClientResponse response = await request.close(); + final HttpClient client = new HttpClient(); + final HttpClientRequest request = await client.getUrl(uri); + final HttpClientResponse response = await request.close(); Expect.equals(response.statusCode, 200); - await response.drain(); + await response.drain>(); client.close(); } -Future testStartPaused(Uri uri) async { +Future testStartPaused(Uri uri) async { uri = uri.replace(scheme: 'ws', path: 'ws'); - WebSocket webSocketClient = await WebSocket.connect(uri.toString()); - ServiceClient serviceClient = new ServiceClient(webSocketClient); + final WebSocket webSocketClient = await WebSocket.connect(uri.toString()); + final ServiceClient serviceClient = new ServiceClient(webSocketClient); // Wait until we have the isolateId. String isolateId; while (isolateId == null) { - Map response = await serviceClient.invokeRPC('getVM'); + final Map response = await serviceClient.invokeRPC('getVM'); Expect.equals(response['type'], 'VM'); if (response['isolates'].length > 0) { isolateId = response['isolates'][0]['id']; @@ -111,9 +111,9 @@ Future testStartPaused(Uri uri) async { } // Grab the isolate. - Map isolate; + Map isolate; while(true) { - isolate = await serviceClient.invokeRPC('getIsolate', { + isolate = await serviceClient.invokeRPC('getIsolate', { 'isolateId': isolateId, }); Expect.equals(isolate['type'], 'Isolate'); @@ -128,13 +128,13 @@ Future testStartPaused(Uri uri) async { Expect.equals(isolate['pauseEvent']['kind'], 'PauseStart'); // Resume the isolate. - await serviceClient.invokeRPC('resume', { + await serviceClient.invokeRPC('resume', { 'isolateId': isolateId, }); // Wait until the isolate has resumed. while (true) { - Map response = await serviceClient.invokeRPC('getIsolate', { + final Map response = await serviceClient.invokeRPC('getIsolate', { 'isolateId': isolateId, }); Expect.equals(response['type'], 'Isolate'); @@ -145,23 +145,23 @@ Future testStartPaused(Uri uri) async { } } -typedef Future TestFunction(Uri uri); +typedef Future TestFunction(Uri uri); -final List basicTests = [ +final List basicTests = [ testHttpProtocolRequest, testWebSocketProtocolRequest, testHttpAssetRequest ]; -final List startPausedTests = [ +final List startPausedTests = [ testStartPaused, ]; -bool runTests(ShellLauncher launcher, List tests) async { - ShellProcess process = await launcher.launch(); - Uri uri = await process.waitForObservatory(); +Future runTests(ShellLauncher launcher, List tests) async { + final ShellProcess process = await launcher.launch(); + final Uri uri = await process.waitForObservatory(); try { - for (var i = 0; i < tests.length; i++) { + for (int i = 0; i < tests.length; i++) { print('Executing test ${i+1}/${tests.length}'); await tests[i](uri); } @@ -173,7 +173,7 @@ bool runTests(ShellLauncher launcher, List tests) async { return exitCode == 0; } -main(List args) async { +Future main(List args) async { if (args.length < 2) { print('Usage: dart ${Platform.script} ' ' ...'); @@ -181,7 +181,7 @@ main(List args) async { } final String shellExecutablePath = args[0]; final String mainDartPath = args[1]; - final List extraArgs = args.length <= 2 ? [] : args.sublist(2); + final List extraArgs = args.length <= 2 ? [] : args.sublist(2); final ShellLauncher launcher = new ShellLauncher(shellExecutablePath, From 3f878a5b8eaa708ec03bec9fde3308788310ed6e Mon Sep 17 00:00:00 2001 From: Alexander Aprelev Date: Fri, 13 Apr 2018 23:32:17 -0700 Subject: [PATCH 2/3] Use service and debug events instead of polling to wait for isolate to start, run and resume. --- shell/testing/observatory/service_client.dart | 60 ++++++++++++++---- shell/testing/observatory/test.dart | 62 ++++++++++--------- 2 files changed, 81 insertions(+), 41 deletions(-) diff --git a/shell/testing/observatory/service_client.dart b/shell/testing/observatory/service_client.dart index 45bf0488b4e45..0334377d1ec0b 100644 --- a/shell/testing/observatory/service_client.dart +++ b/shell/testing/observatory/service_client.dart @@ -10,7 +10,12 @@ import 'dart:io'; class ServiceClient { - ServiceClient(this.client) { + Completer isolateStartedId; + Completer isolateRunnableId; + Completer isolateResumeId; + + ServiceClient(this.client, {this.isolateStartedId, this.isolateRunnableId, + this.isolateResumeId}) { client.listen(_onData, onError: _onError, cancelOnError: true); @@ -40,17 +45,50 @@ class ServiceClient { void _onData(dynamic message) { final Map response = json.decode(message); final dynamic key = response['id']; - 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); + 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); + final String method = response['method']; + if (method != 'streamNotify') { + return; + } + final Map params = response['params']; + if (params == null) { + return; + } + final Map event = params['event']; + if (event == null || event['type'] != 'Event') { + return; + } + final dynamic isolateId = event['isolate']['id']; + switch (params['streamId']) { + case 'Isolate': + switch (event['kind']) { + case 'IsolateStarted': + isolateStartedId?.complete(isolateId); + break; + case 'IsolateRunnable': + isolateRunnableId?.complete(isolateId); + break; + } + break; + case 'Debug': + if (event['kind'] == 'Resume') { + isolateResumeId?.complete(isolateId); + } + break; + } } } diff --git a/shell/testing/observatory/test.dart b/shell/testing/observatory/test.dart index 843bd1c5917db..b449878441260 100644 --- a/shell/testing/observatory/test.dart +++ b/shell/testing/observatory/test.dart @@ -98,51 +98,53 @@ Future testHttpAssetRequest(Uri uri) async { Future testStartPaused(Uri uri) async { uri = uri.replace(scheme: 'ws', path: 'ws'); final WebSocket webSocketClient = await WebSocket.connect(uri.toString()); - final ServiceClient serviceClient = new ServiceClient(webSocketClient); + final Completer isolateStartedId = new Completer(); + final Completer isolateRunnableId = new Completer(); + final Completer isolateResumeId = new Completer(); + final ServiceClient serviceClient = new ServiceClient(webSocketClient, + isolateStartedId: isolateStartedId, + isolateRunnableId: isolateRunnableId, + isolateResumeId: isolateResumeId); + await serviceClient.invokeRPC('streamListen', { 'streamId': 'Isolate'}); + await serviceClient.invokeRPC('streamListen', { 'streamId': 'Debug'}); - // Wait until we have the isolateId. + final Map response = await serviceClient.invokeRPC('getVM'); + Expect.equals(response['type'], 'VM'); String isolateId; - while (isolateId == null) { - final Map response = await serviceClient.invokeRPC('getVM'); - Expect.equals(response['type'], 'VM'); - if (response['isolates'].length > 0) { - isolateId = response['isolates'][0]['id']; - } + if (response['isolates'].length > 0) { + isolateId = response['isolates'][0]['id']; + } else { + // Wait until isolate starts. + isolateId = await isolateStartedId.future; } // Grab the isolate. - Map isolate; - while(true) { + Map isolate = await serviceClient.invokeRPC('getIsolate', { + 'isolateId': isolateId, + }); + Expect.equals(isolate['type'], 'Isolate'); + Expect.isNotNull(isolate['pauseEvent']); + // If it is not runnable, wait until it becomes runnable. + if (isolate['pauseEvent']['kind'] == 'None') { + await isolateRunnableId.future; isolate = await serviceClient.invokeRPC('getIsolate', { 'isolateId': isolateId, }); - Expect.equals(isolate['type'], 'Isolate'); - // Verify that it is paused at start. - Expect.isNotNull(isolate['pauseEvent']); - if (isolate['pauseEvent']['kind'] != 'None') { - break; - } - } - - Expect.isNotNull(isolate); + } + // Verify that it is paused at start. Expect.equals(isolate['pauseEvent']['kind'], 'PauseStart'); // Resume the isolate. await serviceClient.invokeRPC('resume', { 'isolateId': isolateId, }); - // Wait until the isolate has resumed. - while (true) { - final Map response = await serviceClient.invokeRPC('getIsolate', { - 'isolateId': isolateId, - }); - Expect.equals(response['type'], 'Isolate'); - Expect.isNotNull(response['pauseEvent']); - if (response['pauseEvent']['kind'] == 'Resume') { - break; - } - } + await isolateResumeId.future; + final Map resumedResponse = await serviceClient.invokeRPC( + 'getIsolate', {'isolateId': isolateId}); + Expect.equals(resumedResponse['type'], 'Isolate'); + Expect.isNotNull(resumedResponse['pauseEvent']); + Expect.equals(resumedResponse['pauseEvent']['kind'], 'Resume'); } typedef Future TestFunction(Uri uri); From e5e974df9debd8fc4dc285a7ab8a5991e738fd40 Mon Sep 17 00:00:00 2001 From: Alexander Aprelev Date: Mon, 16 Apr 2018 13:52:34 -0700 Subject: [PATCH 3/3] Refactor into _onServiceEvent. Wait for 'paused' event instead of 'isolate runnable'. --- shell/testing/observatory/service_client.dart | 61 ++++++++++--------- shell/testing/observatory/test.dart | 6 +- 2 files changed, 34 insertions(+), 33 deletions(-) diff --git a/shell/testing/observatory/service_client.dart b/shell/testing/observatory/service_client.dart index 0334377d1ec0b..1b107bb76a1a7 100644 --- a/shell/testing/observatory/service_client.dart +++ b/shell/testing/observatory/service_client.dart @@ -11,10 +11,10 @@ import 'dart:io'; class ServiceClient { Completer isolateStartedId; - Completer isolateRunnableId; + Completer isolatePausedId; Completer isolateResumeId; - ServiceClient(this.client, {this.isolateStartedId, this.isolateRunnableId, + ServiceClient(this.client, {this.isolateStartedId, this.isolatePausedId, this.isolateResumeId}) { client.listen(_onData, onError: _onError, @@ -59,36 +59,37 @@ class ServiceClient { completer.complete(result); } } else { - final String method = response['method']; - if (method != 'streamNotify') { - return; + if (response['method'] == 'streamNotify') { + _onServiceEvent(response['params']); } - final Map params = response['params']; - if (params == null) { - return; - } - final Map event = params['event']; - if (event == null || event['type'] != 'Event') { - return; - } - final dynamic isolateId = event['isolate']['id']; - switch (params['streamId']) { - case 'Isolate': - switch (event['kind']) { - case 'IsolateStarted': - isolateStartedId?.complete(isolateId); - break; - case 'IsolateRunnable': - isolateRunnableId?.complete(isolateId); - break; - } - break; - case 'Debug': - if (event['kind'] == 'Resume') { + } + } + + void _onServiceEvent(Map params) { + if (params == null) { + return; + } + final Map 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; - } + break; + case 'PauseStart': + isolatePausedId?.complete(isolateId); + break; + } + break; } } diff --git a/shell/testing/observatory/test.dart b/shell/testing/observatory/test.dart index b449878441260..9d7d7f769e7fd 100644 --- a/shell/testing/observatory/test.dart +++ b/shell/testing/observatory/test.dart @@ -99,11 +99,11 @@ Future testStartPaused(Uri uri) async { uri = uri.replace(scheme: 'ws', path: 'ws'); final WebSocket webSocketClient = await WebSocket.connect(uri.toString()); final Completer isolateStartedId = new Completer(); - final Completer isolateRunnableId = new Completer(); + final Completer isolatePausedId = new Completer(); final Completer isolateResumeId = new Completer(); final ServiceClient serviceClient = new ServiceClient(webSocketClient, isolateStartedId: isolateStartedId, - isolateRunnableId: isolateRunnableId, + isolatePausedId: isolatePausedId, isolateResumeId: isolateResumeId); await serviceClient.invokeRPC('streamListen', { 'streamId': 'Isolate'}); await serviceClient.invokeRPC('streamListen', { 'streamId': 'Debug'}); @@ -126,7 +126,7 @@ Future testStartPaused(Uri uri) async { Expect.isNotNull(isolate['pauseEvent']); // If it is not runnable, wait until it becomes runnable. if (isolate['pauseEvent']['kind'] == 'None') { - await isolateRunnableId.future; + await isolatePausedId.future; isolate = await serviceClient.invokeRPC('getIsolate', { 'isolateId': isolateId, });