| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library test_helper; | 5 library test_helper; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:convert'; | 8 import 'dart:convert'; |
| 9 import 'dart:io'; | 9 import 'dart:io'; |
| 10 import 'package:observatory/service_io.dart'; | 10 import 'package:observatory/service_io.dart'; |
| 11 import 'package:unittest/unittest.dart'; | 11 import 'package:unittest/unittest.dart'; |
| 12 | 12 |
| 13 bool _isWebSocketDisconnect(e) { | 13 bool _isWebSocketDisconnect(e) { |
| 14 return e is NetworkRpcException; | 14 return e is NetworkRpcException; |
| 15 } | 15 } |
| 16 | 16 |
| 17 // This invocation should set up the state being tested. | 17 // This invocation should set up the state being tested. |
| 18 const String _TESTEE_MODE_FLAG = "--testee-mode"; | 18 const String _TESTEE_MODE_FLAG = "--testee-mode"; |
| 19 | 19 |
| 20 class _TestLauncher { | 20 class _TestLauncher { |
| 21 Process process; | 21 Process process; |
| 22 final List<String> args; | 22 final List<String> args; |
| 23 bool killedByTester = false; | 23 bool killedByTester = false; |
| 24 | 24 |
| 25 _TestLauncher() : args = ['--enable-vm-service:0', | 25 _TestLauncher() : args = ['--enable-vm-service:0', |
| 26 Platform.script.toFilePath(), | 26 Platform.script.toFilePath(), |
| 27 _TESTEE_MODE_FLAG] {} | 27 _TESTEE_MODE_FLAG] {} |
| 28 | 28 |
| 29 Future<int> launch(bool pause_on_exit) { | 29 Future<int> launch(bool pause_on_start, bool pause_on_exit) { |
| 30 String dartExecutable = Platform.executable; | 30 String dartExecutable = Platform.executable; |
| 31 var fullArgs = []; | 31 var fullArgs = []; |
| 32 if (pause_on_start == true) { |
| 33 fullArgs.add('--pause-isolates-on-start'); |
| 34 } |
| 32 if (pause_on_exit == true) { | 35 if (pause_on_exit == true) { |
| 33 fullArgs.add('--pause-isolates-on-exit'); | 36 fullArgs.add('--pause-isolates-on-exit'); |
| 34 } | 37 } |
| 35 fullArgs.addAll(Platform.executableArguments); | 38 fullArgs.addAll(Platform.executableArguments); |
| 36 fullArgs.addAll(args); | 39 fullArgs.addAll(args); |
| 37 print('** Launching $dartExecutable ${fullArgs.join(' ')}'); | 40 print('** Launching $dartExecutable ${fullArgs.join(' ')}'); |
| 38 return Process.start(dartExecutable, fullArgs).then((p) { | 41 return Process.start(dartExecutable, fullArgs).then((p) { |
| 39 | 42 |
| 40 Completer completer = new Completer(); | 43 Completer completer = new Completer(); |
| 41 process = p; | 44 process = p; |
| 42 var portNumber; | 45 var portNumber; |
| 43 var blank; | 46 var blank; |
| 44 var first = true; | 47 var first = true; |
| 45 process.stdout.transform(UTF8.decoder) | 48 process.stdout.transform(UTF8.decoder) |
| 46 .transform(new LineSplitter()).listen((line) { | 49 .transform(new LineSplitter()).listen((line) { |
| 47 if (line.startsWith('Observatory listening on http://')) { | 50 if (line.startsWith('Observatory listening on http://')) { |
| 48 RegExp portExp = new RegExp(r"\d+.\d+.\d+.\d+:(\d+)"); | 51 RegExp portExp = new RegExp(r"\d+.\d+.\d+.\d+:(\d+)"); |
| 49 var port = portExp.firstMatch(line).group(1); | 52 var port = portExp.firstMatch(line).group(1); |
| 50 portNumber = int.parse(port); | 53 portNumber = int.parse(port); |
| 51 } | 54 } |
| 52 if (line == '') { | 55 if (pause_on_start || line == '') { |
| 53 // Received blank line. | 56 // Received blank line. |
| 54 blank = true; | 57 blank = true; |
| 55 } | 58 } |
| 56 if (portNumber != null && blank == true && first == true) { | 59 if (portNumber != null && blank == true && first == true) { |
| 57 completer.complete(portNumber); | 60 completer.complete(portNumber); |
| 58 // Stop repeat completions. | 61 // Stop repeat completions. |
| 59 first = false; | 62 first = false; |
| 60 print('** Signaled to run test queries on $portNumber'); | 63 print('** Signaled to run test queries on $portNumber'); |
| 61 } | 64 } |
| 62 print(line); | 65 print(line); |
| (...skipping 28 matching lines...) Expand all Loading... |
| 91 String serviceHttpAddress; | 94 String serviceHttpAddress; |
| 92 | 95 |
| 93 /// Runs [tests] in sequence, each of which should take an [Isolate] and | 96 /// Runs [tests] in sequence, each of which should take an [Isolate] and |
| 94 /// return a [Future]. Code for setting up state can run before and/or | 97 /// return a [Future]. Code for setting up state can run before and/or |
| 95 /// concurrently with the tests. Uses [mainArgs] to determine whether | 98 /// concurrently with the tests. Uses [mainArgs] to determine whether |
| 96 /// to run tests or testee in this invokation of the script. | 99 /// to run tests or testee in this invokation of the script. |
| 97 void runIsolateTests(List<String> mainArgs, | 100 void runIsolateTests(List<String> mainArgs, |
| 98 List<IsolateTest> tests, | 101 List<IsolateTest> tests, |
| 99 {void testeeBefore(), | 102 {void testeeBefore(), |
| 100 void testeeConcurrent(), | 103 void testeeConcurrent(), |
| 101 bool pause_on_exit}) { | 104 bool pause_on_start: false, |
| 105 bool pause_on_exit: false}) { |
| 106 assert(!pause_on_start || testeeBefore == null); |
| 102 if (mainArgs.contains(_TESTEE_MODE_FLAG)) { | 107 if (mainArgs.contains(_TESTEE_MODE_FLAG)) { |
| 103 if (testeeBefore != null) { | 108 if (!pause_on_start) { |
| 104 testeeBefore(); | 109 if (testeeBefore != null) { |
| 110 testeeBefore(); |
| 111 } |
| 112 print(''); // Print blank line to signal that we are ready. |
| 105 } | 113 } |
| 106 print(''); // Print blank line to signal that we are ready. | |
| 107 if (testeeConcurrent != null) { | 114 if (testeeConcurrent != null) { |
| 108 testeeConcurrent(); | 115 testeeConcurrent(); |
| 109 } | 116 } |
| 110 // Wait around for the process to be killed. | 117 if (!pause_on_exit) { |
| 111 stdin.first.then((_) => exit(0)); | 118 // Wait around for the process to be killed. |
| 119 stdin.first.then((_) => exit(0)); |
| 120 } |
| 112 } else { | 121 } else { |
| 113 var process = new _TestLauncher(); | 122 var process = new _TestLauncher(); |
| 114 process.launch(pause_on_exit).then((port) { | 123 process.launch(pause_on_start, pause_on_exit).then((port) { |
| 115 if (mainArgs.contains("--gdb")) { | 124 if (mainArgs.contains("--gdb")) { |
| 116 port = 8181; | 125 port = 8181; |
| 117 } | 126 } |
| 118 String addr = 'ws://localhost:$port/ws'; | 127 String addr = 'ws://localhost:$port/ws'; |
| 119 serviceHttpAddress = 'http://localhost:$port'; | 128 serviceHttpAddress = 'http://localhost:$port'; |
| 120 var testIndex = 1; | 129 var testIndex = 1; |
| 121 var totalTests = tests.length; | 130 var totalTests = tests.length; |
| 122 var name = Platform.script.pathSegments.last; | 131 var name = Platform.script.pathSegments.last; |
| 123 runZoned(() { | 132 runZoned(() { |
| 124 new WebSocketVM(new WebSocketVMTarget(addr)).load() | 133 new WebSocketVM(new WebSocketVMTarget(addr)).load() |
| 125 .then((VM vm) => vm.isolates.first.load()) | 134 .then((VM vm) => vm.isolates.first.load()) |
| 126 .then((Isolate isolate) => Future.forEach(tests, (test) { | 135 .then((Isolate isolate) => Future.forEach(tests, (test) { |
| 127 print('Running $name [$testIndex/$totalTests]'); | 136 print('Running $name [$testIndex/$totalTests]'); |
| 128 testIndex++; | 137 testIndex++; |
| 129 return test(isolate); | 138 return test(isolate); |
| 130 })).then((_) => process.requestExit()); | 139 })).then((_) => process.requestExit()); |
| 131 }, onError: (e, st) { | 140 }, onError: (e, st) { |
| 132 process.requestExit(); | 141 process.requestExit(); |
| 133 if (!_isWebSocketDisconnect(e)) { | 142 if (!_isWebSocketDisconnect(e)) { |
| 134 print('Unexpected exception in service tests: $e $st'); | 143 print('Unexpected exception in service tests: $e $st'); |
| 135 throw e; | 144 throw e; |
| 136 } | 145 } |
| 137 }); | 146 }); |
| 138 }); | 147 }); |
| 139 } | 148 } |
| 140 } | 149 } |
| 141 | 150 |
| 142 | 151 |
| 143 // Cancel the subscription and complete the completer when finished processing | |
| 144 // events. | |
| 145 typedef void ServiceEventHandler(ServiceEvent event, | |
| 146 StreamSubscription subscription, | |
| 147 Completer completer); | |
| 148 | |
| 149 Future processServiceEvents(VM vm, | |
| 150 String streamId, | |
| 151 ServiceEventHandler handler) { | |
| 152 Completer completer = new Completer(); | |
| 153 vm.getEventStream(streamId).then((stream) { | |
| 154 var subscription; | |
| 155 subscription = stream.listen((ServiceEvent event) { | |
| 156 handler(event, subscription, completer); | |
| 157 }); | |
| 158 }); | |
| 159 return completer.future; | |
| 160 } | |
| 161 | |
| 162 | |
| 163 Future<Isolate> hasStoppedAtBreakpoint(Isolate isolate) { | 152 Future<Isolate> hasStoppedAtBreakpoint(Isolate isolate) { |
| 164 // Set up a listener to wait for breakpoint events. | 153 // Set up a listener to wait for breakpoint events. |
| 165 Completer completer = new Completer(); | 154 Completer completer = new Completer(); |
| 166 isolate.vm.getEventStream(VM.kDebugStream).then((stream) { | 155 isolate.vm.getEventStream(VM.kDebugStream).then((stream) { |
| 167 var subscription; | 156 var subscription; |
| 168 subscription = stream.listen((ServiceEvent event) { | 157 subscription = stream.listen((ServiceEvent event) { |
| 169 print("Event: $event"); | 158 print("Event: $event"); |
| 170 if (event.kind == ServiceEvent.kPauseBreakpoint) { | 159 if (event.kind == ServiceEvent.kPauseBreakpoint) { |
| 171 print('Breakpoint reached'); | 160 print('Breakpoint reached'); |
| 172 subscription.cancel(); | 161 subscription.cancel(); |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 279 | 268 |
| 280 | 269 |
| 281 /// Runs [tests] in sequence, each of which should take an [Isolate] and | 270 /// Runs [tests] in sequence, each of which should take an [Isolate] and |
| 282 /// return a [Future]. Code for setting up state can run before and/or | 271 /// return a [Future]. Code for setting up state can run before and/or |
| 283 /// concurrently with the tests. Uses [mainArgs] to determine whether | 272 /// concurrently with the tests. Uses [mainArgs] to determine whether |
| 284 /// to run tests or testee in this invokation of the script. | 273 /// to run tests or testee in this invokation of the script. |
| 285 Future runVMTests(List<String> mainArgs, | 274 Future runVMTests(List<String> mainArgs, |
| 286 List<VMTest> tests, | 275 List<VMTest> tests, |
| 287 {Future testeeBefore(), | 276 {Future testeeBefore(), |
| 288 Future testeeConcurrent(), | 277 Future testeeConcurrent(), |
| 289 bool pause_on_exit}) async { | 278 bool pause_on_start: false, |
| 279 bool pause_on_exit: false}) async { |
| 290 if (mainArgs.contains(_TESTEE_MODE_FLAG)) { | 280 if (mainArgs.contains(_TESTEE_MODE_FLAG)) { |
| 291 if (testeeBefore != null) { | 281 if (!pause_on_start) { |
| 292 await testeeBefore(); | 282 if (testeeBefore != null) { |
| 283 await testeeBefore(); |
| 284 } |
| 285 print(''); // Print blank line to signal that we are ready. |
| 293 } | 286 } |
| 294 print(''); // Print blank line to signal that we are ready. | |
| 295 if (testeeConcurrent != null) { | 287 if (testeeConcurrent != null) { |
| 296 await testeeConcurrent(); | 288 await testeeConcurrent(); |
| 297 } | 289 } |
| 298 // Wait around for the process to be killed. | 290 if (!pause_on_exit) { |
| 299 stdin.first.then((_) => exit(0)); | 291 // Wait around for the process to be killed. |
| 292 stdin.first.then((_) => exit(0)); |
| 293 } |
| 300 } else { | 294 } else { |
| 301 var process = new _TestLauncher(); | 295 var process = new _TestLauncher(); |
| 302 process.launch(pause_on_exit).then((port) async { | 296 process.launch(pause_on_start, pause_on_exit).then((port) async { |
| 303 if (mainArgs.contains("--gdb")) { | 297 if (mainArgs.contains("--gdb")) { |
| 304 port = 8181; | 298 port = 8181; |
| 305 } | 299 } |
| 306 String addr = 'ws://localhost:$port/ws'; | 300 String addr = 'ws://localhost:$port/ws'; |
| 307 serviceHttpAddress = 'http://localhost:$port'; | 301 serviceHttpAddress = 'http://localhost:$port'; |
| 308 var testIndex = 1; | 302 var testIndex = 1; |
| 309 var totalTests = tests.length; | 303 var totalTests = tests.length; |
| 310 var name = Platform.script.pathSegments.last; | 304 var name = Platform.script.pathSegments.last; |
| 311 runZoned(() { | 305 runZoned(() { |
| 312 new WebSocketVM(new WebSocketVMTarget(addr)).load() | 306 new WebSocketVM(new WebSocketVMTarget(addr)).load() |
| 313 .then((VM vm) => Future.forEach(tests, (test) { | 307 .then((VM vm) => Future.forEach(tests, (test) { |
| 314 print('Running $name [$testIndex/$totalTests]'); | 308 print('Running $name [$testIndex/$totalTests]'); |
| 315 testIndex++; | 309 testIndex++; |
| 316 return test(vm); | 310 return test(vm); |
| 317 })).then((_) => process.requestExit()); | 311 })).then((_) => process.requestExit()); |
| 318 }, onError: (e, st) { | 312 }, onError: (e, st) { |
| 319 process.requestExit(); | 313 process.requestExit(); |
| 320 if (!_isWebSocketDisconnect(e)) { | 314 if (!_isWebSocketDisconnect(e)) { |
| 321 print('Unexpected exception in service tests: $e $st'); | 315 print('Unexpected exception in service tests: $e $st'); |
| 322 throw e; | 316 throw e; |
| 323 } | 317 } |
| 324 }); | 318 }); |
| 325 }); | 319 }); |
| 326 } | 320 } |
| 327 } | 321 } |
| OLD | NEW |