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 |