OLD | NEW |
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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.runner.browser.browser; | 5 library test.runner.browser.browser; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:io'; | 8 import 'dart:io'; |
9 | 9 |
10 import 'package:stack_trace/stack_trace.dart'; | 10 import 'package:stack_trace/stack_trace.dart'; |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
50 Browser(Future<Process> startBrowser()) { | 50 Browser(Future<Process> startBrowser()) { |
51 // Don't return a Future here because there's no need for the caller to wait | 51 // Don't return a Future here because there's no need for the caller to wait |
52 // for the process to actually start. They should just wait for the HTTP | 52 // for the process to actually start. They should just wait for the HTTP |
53 // request instead. | 53 // request instead. |
54 runZoned(() async { | 54 runZoned(() async { |
55 var process = await startBrowser(); | 55 var process = await startBrowser(); |
56 _processCompleter.complete(process); | 56 _processCompleter.complete(process); |
57 | 57 |
58 var exitCode = await process.exitCode; | 58 var exitCode = await process.exitCode; |
59 | 59 |
| 60 // This hack dodges an otherwise intractable race condition. When the user |
| 61 // presses Control-C, the signal is sent to the browser and the test |
| 62 // runner at the same time. It's possible for the browser to exit before |
| 63 // the [Browser.close] is called, which would trigger the error below. |
| 64 // |
| 65 // A negative exit code signals that the process exited due to a signal. |
| 66 // However, it's possible that this signal didn't come from the user's |
| 67 // Control-C, in which case we do want to throw the error. The only way to |
| 68 // resolve the ambiguity is to wait a brief amount of time and see if this |
| 69 // browser is actually closed. |
| 70 if (!_closed && exitCode < 0) { |
| 71 await new Future.delayed(new Duration(milliseconds: 200)); |
| 72 } |
| 73 |
60 if (!_closed && exitCode != 0) { | 74 if (!_closed && exitCode != 0) { |
61 throw new ApplicationException( | 75 throw new ApplicationException( |
62 "$name failed with exit code $exitCode."); | 76 "$name failed with exit code $exitCode."); |
63 } | 77 } |
64 | 78 |
65 _onExitCompleter.complete(); | 79 _onExitCompleter.complete(); |
66 }, onError: (error, stackTrace) { | 80 }, onError: (error, stackTrace) { |
67 // Ignore any errors after the browser has been closed. | 81 // Ignore any errors after the browser has been closed. |
68 if (_closed) return; | 82 if (_closed) return; |
69 | 83 |
(...skipping 13 matching lines...) Expand all Loading... |
83 /// Returns the same [Future] as [onExit], except that it won't emit | 97 /// Returns the same [Future] as [onExit], except that it won't emit |
84 /// exceptions. | 98 /// exceptions. |
85 Future close() { | 99 Future close() { |
86 _closed = true; | 100 _closed = true; |
87 _process.then((process) => process.kill()); | 101 _process.then((process) => process.kill()); |
88 | 102 |
89 // Swallow exceptions. The user should explicitly use [onExit] for these. | 103 // Swallow exceptions. The user should explicitly use [onExit] for these. |
90 return onExit.catchError((_) {}); | 104 return onExit.catchError((_) {}); |
91 } | 105 } |
92 } | 106 } |
OLD | NEW |