// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This is a minimal dependency heart beat test for Observatory. import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'launcher.dart'; import 'service_client.dart'; class Expect { static void equals(dynamic actual, dynamic expected) { if (actual != expected) { throw 'Expected $actual == $expected'; } } static void contains(String needle, String haystack) { if (!haystack.contains(needle)) { throw 'Expected $haystack to contain $needle'; } } static void isTrue(bool tf) { if (tf != true) { throw 'Expected $tf to be true'; } } static void isFalse(bool tf) { if (tf != false) { throw 'Expected $tf to be false'; } } static void notExecuted() { throw 'Should not have hit'; } static void isNotNull(dynamic a) { if (a == null) { throw 'Expected $a to not be null'; } } } Future readResponse(HttpClientResponse response) { final Completer completer = Completer(); final StringBuffer contents = 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 { uri = uri.replace(path: 'getVM'); final HttpClient client = HttpClient(); final HttpClientRequest request = await client.getUrl(uri); final HttpClientResponse response = await request.close(); Expect.equals(response.statusCode, 200); 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 { uri = uri.replace(scheme: 'ws', path: 'ws'); final WebSocket webSocketClient = await WebSocket.connect(uri.toString()); final ServiceClient serviceClient = ServiceClient(webSocketClient); final Map response = await serviceClient.invokeRPC('getVM'); Expect.equals(response['type'], 'VM'); try { await serviceClient.invokeRPC('BART_SIMPSON'); Expect.notExecuted(); } catch (e) { // Method not found. Expect.equals(e['code'], -32601); } } // Test accessing an Observatory UI asset. Future testHttpAssetRequest(Uri uri) async { uri = uri.replace(path: 'third_party/trace_viewer_full.html'); final HttpClient client = HttpClient(); final HttpClientRequest request = await client.getUrl(uri); final HttpClientResponse response = await request.close(); Expect.equals(response.statusCode, 200); await response.drain>(); client.close(); } Future testStartPaused(Uri uri) async { uri = uri.replace(scheme: 'ws', path: 'ws'); final WebSocket webSocketClient = await WebSocket.connect(uri.toString()); final Completer isolateStartedId = Completer(); final Completer isolatePausedId = Completer(); final Completer isolateResumeId = Completer(); final ServiceClient serviceClient = ServiceClient(webSocketClient, isolateStartedId: isolateStartedId, isolatePausedId: isolatePausedId, isolateResumeId: isolateResumeId); await serviceClient.invokeRPC('streamListen', { 'streamId': 'Isolate'}); await serviceClient.invokeRPC('streamListen', { 'streamId': 'Debug'}); final Map response = await serviceClient.invokeRPC('getVM'); Expect.equals(response['type'], 'VM'); String isolateId; if (response['isolates'].length > 0) { isolateId = response['isolates'][0]['id']; } else { // Wait until isolate starts. isolateId = await isolateStartedId.future; } // Grab the isolate. 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 isolatePausedId.future; isolate = await serviceClient.invokeRPC('getIsolate', { 'isolateId': isolateId, }); } // 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. 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 TestFunction = Future Function(Uri uri); final List basicTests = [ testHttpProtocolRequest, testWebSocketProtocolRequest, testHttpAssetRequest ]; final List startPausedTests = [ // TODO(engine): Investigate difference in lifecycle events. // testStartPaused, ]; Future runTests(ShellLauncher launcher, List tests) async { final ShellProcess process = await launcher.launch(); final Uri uri = await process.waitForObservatory(); try { for (int i = 0; i < tests.length; i++) { print('Executing test ${i+1}/${tests.length}'); await tests[i](uri); } } catch (e) { print('Observatory test failure: $e'); exitCode = -1; } await process.kill(); return exitCode == 0; } Future main(List args) async { if (args.length < 2) { print('Usage: dart ${Platform.script} ' ' ...'); return; } final String shellExecutablePath = args[0]; final String mainDartPath = args[1]; final List extraArgs = args.length <= 2 ? [] : args.sublist(2); final ShellLauncher launcher = ShellLauncher(shellExecutablePath, mainDartPath, false, extraArgs); final ShellLauncher startPausedlauncher = ShellLauncher(shellExecutablePath, mainDartPath, true, extraArgs); await runTests(launcher, basicTests); await runTests(startPausedlauncher, startPausedTests); }