| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 library observatory_tester; | |
| 6 | |
| 7 // Minimal dependency Observatory heartbeat test. | |
| 8 | |
| 9 import 'dart:async'; | |
| 10 import 'dart:convert'; | |
| 11 import 'dart:io'; | |
| 12 | |
| 13 class Expect { | |
| 14 static equals(a, b) { | |
| 15 if (a != b) { | |
| 16 throw 'Expected $a == $b'; | |
| 17 } | |
| 18 } | |
| 19 | |
| 20 static isMap(a) { | |
| 21 if (a is! Map) { | |
| 22 throw 'Expected $a to be a Map'; | |
| 23 } | |
| 24 } | |
| 25 | |
| 26 static notExecuted() { | |
| 27 throw 'Should not have hit'; | |
| 28 } | |
| 29 | |
| 30 static isNotNull(a) { | |
| 31 if (a == null) { | |
| 32 throw 'Expected $a to not be null.'; | |
| 33 } | |
| 34 } | |
| 35 } | |
| 36 | |
| 37 class Launch { | |
| 38 Launch(this.executable, this.arguments, this.process, this.port) { | |
| 39 _killRequested = false; | |
| 40 this.process.exitCode.then(_checkKill); | |
| 41 } | |
| 42 | |
| 43 void kill() { | |
| 44 _killRequested = true; | |
| 45 process.kill(); | |
| 46 } | |
| 47 | |
| 48 void _checkKill(int exitCode) { | |
| 49 if (!_killRequested) { | |
| 50 throw 'Unexpected exit of testee. (exitCode = $exitCode)'; | |
| 51 } | |
| 52 } | |
| 53 | |
| 54 final String executable; | |
| 55 final String arguments; | |
| 56 final Process process; | |
| 57 final int port; | |
| 58 bool _killRequested; | |
| 59 } | |
| 60 | |
| 61 class Launcher { | |
| 62 /// Launch [executable] with [arguments]. Returns a future to a [Launch] | |
| 63 /// which includes the process and port where Observatory is running. | |
| 64 static Future<Launch> launch(String executable, | |
| 65 List<String> arguments) async { | |
| 66 var process = await Process.start(executable, arguments); | |
| 67 | |
| 68 // Completer completes once 'Observatory listening on' message has been | |
| 69 // scraped and we know the port number. | |
| 70 var completer = new Completer(); | |
| 71 | |
| 72 process.stdout.transform(UTF8.decoder) | |
| 73 .transform(new LineSplitter()).listen((line) { | |
| 74 if (line.startsWith('Observatory listening on http://')) { | |
| 75 RegExp portExp = new RegExp(r"\d+.\d+.\d+.\d+:(\d+)"); | |
| 76 var port = portExp.firstMatch(line).group(1); | |
| 77 var portNumber = int.parse(port); | |
| 78 completer.complete(portNumber); | |
| 79 } else { | |
| 80 print(line); | |
| 81 } | |
| 82 }); | |
| 83 | |
| 84 process.stderr.transform(UTF8.decoder) | |
| 85 .transform(new LineSplitter()).listen((line) { | |
| 86 print(line); | |
| 87 }); | |
| 88 | |
| 89 var port = await completer.future; | |
| 90 return new Launch(executable, arguments, process, port); | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 class ServiceHelper { | |
| 95 ServiceHelper(this.client) { | |
| 96 client.listen(_onData, | |
| 97 onError: _onError, | |
| 98 cancelOnError: true); | |
| 99 } | |
| 100 | |
| 101 Future<Map> invokeRPC(String method, [Map params]) async { | |
| 102 var key = _createKey(); | |
| 103 var request = JSON.encode({ | |
| 104 'jsonrpc': '2.0', | |
| 105 'method': method, | |
| 106 'params': params == null ? {} : params, | |
| 107 'id': key, | |
| 108 }); | |
| 109 client.add(request); | |
| 110 var completer = new Completer(); | |
| 111 _outstanding_requests[key] = completer; | |
| 112 print('-> $key ($method)'); | |
| 113 return completer.future; | |
| 114 } | |
| 115 | |
| 116 String _createKey() { | |
| 117 var key = '$_id'; | |
| 118 _id++; | |
| 119 return key; | |
| 120 } | |
| 121 | |
| 122 void _onData(String message) { | |
| 123 var response = JSON.decode(message); | |
| 124 var key = response['id']; | |
| 125 print('<- $key'); | |
| 126 var completer = _outstanding_requests.remove(key); | |
| 127 assert(completer != null); | |
| 128 var result = response['result']; | |
| 129 var error = response['error']; | |
| 130 if (error != null) { | |
| 131 assert(result == null); | |
| 132 completer.completeError(error); | |
| 133 } else { | |
| 134 assert(result != null); | |
| 135 completer.complete(result); | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 void _onError(error) { | |
| 140 print('WebSocket error: $error'); | |
| 141 } | |
| 142 | |
| 143 final WebSocket client; | |
| 144 final Map<String, Completer> _outstanding_requests = <String, Completer>{}; | |
| 145 var _id = 1; | |
| 146 } | |
| 147 | |
| 148 main(List<String> args) async { | |
| 149 var executable = args[0]; | |
| 150 var arguments = args.sublist(1); | |
| 151 | |
| 152 print('Launching $executable with $arguments'); | |
| 153 | |
| 154 var launch = await Launcher.launch(executable, arguments); | |
| 155 | |
| 156 print('Observatory is on port ${launch.port}'); | |
| 157 var serviceUrl = 'ws://127.0.0.1:${launch.port}/ws'; | |
| 158 | |
| 159 var client = await WebSocket.connect(serviceUrl); | |
| 160 print('Connected to $serviceUrl'); | |
| 161 | |
| 162 var helper = new ServiceHelper(client); | |
| 163 | |
| 164 // Invoke getVM RPC. Verify a valid repsonse. | |
| 165 var vm = await helper.invokeRPC('getVM'); | |
| 166 Expect.equals(vm['type'], 'VM'); | |
| 167 | |
| 168 // Invoke a bogus RPC. Expect an error. | |
| 169 bool errorCaught = false; | |
| 170 try { | |
| 171 var bad = await helper.invokeRPC('BARTSIMPSON'); | |
| 172 Expect.notExecuted(); | |
| 173 } catch (e) { | |
| 174 errorCaught = true; | |
| 175 // Map. | |
| 176 Expect.isMap(e); | |
| 177 // Has an error code. | |
| 178 Expect.isNotNull(e['code']); | |
| 179 } | |
| 180 Expect.equals(errorCaught, true); | |
| 181 | |
| 182 await client.close(); | |
| 183 print('Closed connection'); | |
| 184 | |
| 185 print('Finished.'); | |
| 186 launch.kill(); | |
| 187 } | |
| OLD | NEW |