diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bfa5202cfa64cd41dbf78725e3919603b521290..1cd35ffd5d5ddcc162c150e1d144e5473051a84d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ * Fix running VM tests against `pub serve`. +* More gracefully handle browser errors. + * Properly load Dartium from the Dart Editor when possible. ### 0.12.0-beta.8 diff --git a/lib/src/runner/browser/chrome.dart b/lib/src/runner/browser/chrome.dart index fc4e232920e60dbc875b5f29550c94aab2d4f0c5..bca71a48994976cf5dc3e268b9a8d2e95a08f318 100644 --- a/lib/src/runner/browser/chrome.dart +++ b/lib/src/runner/browser/chrome.dart @@ -5,11 +5,15 @@ library test.runner.browser.chrome; import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as p; +import 'package:stack_trace/stack_trace.dart'; import '../../util/io.dart'; +import '../../utils.dart'; +import '../application_exception.dart'; import 'browser.dart'; // TODO(nweiz): move this into its own package? @@ -25,12 +29,6 @@ class Chrome implements Browser { /// The underlying process. Process _process; - /// The temporary directory used as the browser's user data dir. - /// - /// A new data dir is created for each run to ensure that they're - /// well-isolated. - String _dir; - Future get onExit => _onExitCompleter.future; final _onExitCompleter = new Completer(); @@ -52,9 +50,8 @@ class Chrome implements Browser { // for the process to actually start. They should just wait for the HTTP // request instead. withTempDir((dir) { - _dir = dir; return Process.start(executable, [ - "--user-data-dir=$_dir", + "--user-data-dir=$dir", url.toString(), "--disable-extensions", "--disable-popup-blocking", @@ -72,9 +69,19 @@ class Chrome implements Browser { return _process.exitCode; }); }).then((exitCode) { - if (exitCode != 0) throw "Chrome failed with exit code $exitCode."; - }).then(_onExitCompleter.complete) - .catchError(_onExitCompleter.completeError); + if (exitCode == 0) return null; + + return UTF8.decodeStream(_process.stderr).then((error) { + throw new ApplicationException( + "Chrome failed with exit code $exitCode:\n$error"); + }); + }).then(_onExitCompleter.complete).catchError((error, stackTrace) { + if (stackTrace == null) stackTrace = new Trace.current(); + _onExitCompleter.completeError( + new ApplicationException( + "Failed to start Chrome: ${getErrorMessage(error)}."), + stackTrace); + }); } Future close() { diff --git a/lib/src/runner/browser/content_shell.dart b/lib/src/runner/browser/content_shell.dart index 2df48c32f4556fc00e457b6eb2158be793f3ea7e..6849e391a8ea83923021718c8d039e76703b04d6 100644 --- a/lib/src/runner/browser/content_shell.dart +++ b/lib/src/runner/browser/content_shell.dart @@ -8,6 +8,9 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'package:stack_trace/stack_trace.dart'; + +import '../../utils.dart'; import '../application_exception.dart'; import 'browser.dart'; @@ -71,9 +74,20 @@ class ContentShell implements Browser { "latest/dartium/"); } - if (exitCode != 0) throw "Content shell failed with exit code $exitCode."; - }).then(_onExitCompleter.complete) - .catchError(_onExitCompleter.completeError); + if (exitCode == 0) return null; + + + return UTF8.decodeStream(_process.stderr).then((error) { + throw new ApplicationException( + "Content shell failed with exit code $exitCode:\n$error"); + }); + }).then(_onExitCompleter.complete).catchError((error, stackTrace) { + if (stackTrace == null) stackTrace = new Trace.current(); + _onExitCompleter.completeError( + new ApplicationException( + "Failed to start content shell: ${getErrorMessage(error)}."), + stackTrace); + }); } Future close() { diff --git a/lib/src/runner/browser/dartium.dart b/lib/src/runner/browser/dartium.dart index f8d81513e00bad2cd386b5ad018676fbc8d323b8..627b17d32db2c3ca0188210b2744a356a32a049a 100644 --- a/lib/src/runner/browser/dartium.dart +++ b/lib/src/runner/browser/dartium.dart @@ -5,11 +5,15 @@ library test.runner.browser.dartium; import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as p; +import 'package:stack_trace/stack_trace.dart'; import '../../util/io.dart'; +import '../../utils.dart'; +import '../application_exception.dart'; import 'browser.dart'; /// A class for running an instance of Dartium. @@ -23,12 +27,6 @@ class Dartium implements Browser { /// The underlying process. Process _process; - /// The temporary directory used as the browser's user data dir. - /// - /// A new data dir is created for each run to ensure that they're - /// well-isolated. - String _dir; - Future get onExit => _onExitCompleter.future; final _onExitCompleter = new Completer(); @@ -50,9 +48,8 @@ class Dartium implements Browser { // for the process to actually start. They should just wait for the HTTP // request instead. withTempDir((dir) { - _dir = dir; return Process.start(executable, [ - "--user-data-dir=$_dir", + "--user-data-dir=$dir", url.toString(), "--disable-extensions", "--disable-popup-blocking", @@ -70,9 +67,19 @@ class Dartium implements Browser { return _process.exitCode; }); }).then((exitCode) { - if (exitCode != 0) throw "Dartium failed with exit code $exitCode."; - }).then(_onExitCompleter.complete) - .catchError(_onExitCompleter.completeError); + if (exitCode == 0) return null; + + return UTF8.decodeStream(_process.stderr).then((error) { + throw new ApplicationException( + "Dartium failed with exit code $exitCode:\n$error"); + }); + }).then(_onExitCompleter.complete).catchError((error, stackTrace) { + if (stackTrace == null) stackTrace = new Trace.current(); + _onExitCompleter.completeError( + new ApplicationException( + "Failed to start Dartium: ${getErrorMessage(error)}."), + stackTrace); + }); } Future close() { diff --git a/lib/src/runner/browser/firefox.dart b/lib/src/runner/browser/firefox.dart index 215f3a85885d77cbe0cf621dc670c6b051d21c98..36eed533adbe14817651f3381a90cfbe6ddbb00c 100644 --- a/lib/src/runner/browser/firefox.dart +++ b/lib/src/runner/browser/firefox.dart @@ -5,11 +5,15 @@ library test.runner.browser.firefox; import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as p; +import 'package:stack_trace/stack_trace.dart'; import '../../util/io.dart'; +import '../../utils.dart'; +import '../application_exception.dart'; import 'browser.dart'; final _preferences = ''' @@ -29,12 +33,6 @@ class Firefox implements Browser { /// The underlying process. Process _process; - /// The temporary directory used as the browser's user data dir. - /// - /// A new data dir is created for each run to ensure that they're - /// well-isolated. - String _dir; - Future get onExit => _onExitCompleter.future; final _onExitCompleter = new Completer(); @@ -56,13 +54,11 @@ class Firefox implements Browser { // for the process to actually start. They should just wait for the HTTP // request instead. withTempDir((dir) { - _dir = dir; - new File(p.join(dir, 'prefs.js')).writeAsStringSync(_preferences); return Process.start(executable, [ "--profile", - "$_dir", + "$dir", url.toString(), "--no-remote" ], environment: { @@ -76,9 +72,19 @@ class Firefox implements Browser { return _process.exitCode; }); }).then((exitCode) { - if (exitCode != 0) throw "Firefox failed with exit code $exitCode."; - }).then(_onExitCompleter.complete) - .catchError(_onExitCompleter.completeError); + if (exitCode == 0) return null; + + return UTF8.decodeStream(_process.stderr).then((error) { + throw new ApplicationException( + "Firefox failed with exit code $exitCode:\n$error"); + }); + }).then(_onExitCompleter.complete).catchError((error, stackTrace) { + if (stackTrace == null) stackTrace = new Trace.current(); + _onExitCompleter.completeError( + new ApplicationException( + "Failed to start Firefox: ${getErrorMessage(error)}."), + stackTrace); + }); } Future close() { diff --git a/lib/src/runner/browser/phantom_js.dart b/lib/src/runner/browser/phantom_js.dart index 0eadafb4512d767c8558ae00c2a476508469f4da..c13ee63fb6c24f53f5056d635ddb3e98f504d660 100644 --- a/lib/src/runner/browser/phantom_js.dart +++ b/lib/src/runner/browser/phantom_js.dart @@ -5,12 +5,15 @@ library test.runner.browser.phantom_js; import 'dart:async'; +import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as p; +import 'package:stack_trace/stack_trace.dart'; import '../../util/exit_codes.dart' as exit_codes; import '../../util/io.dart'; +import '../../utils.dart'; import '../application_exception.dart'; import 'browser.dart'; @@ -82,9 +85,20 @@ class PhantomJS implements Browser { throw new ApplicationException( "Only PhantomJS version 2.0.0 or greater is supported."); } - if (exitCode != 0) throw "PhantomJS failed with exit code $exitCode."; - }).then(_onExitCompleter.complete) - .catchError(_onExitCompleter.completeError); + + if (exitCode == 0) return null; + + return UTF8.decodeStream(_process.stderr).then((error) { + throw new ApplicationException( + "PhantomJS failed with exit code $exitCode:\n$error"); + }); + }).then(_onExitCompleter.complete).catchError((error, stackTrace) { + if (stackTrace == null) stackTrace = new Trace.current(); + _onExitCompleter.completeError( + new ApplicationException( + "Failed to start PhantomJS: ${getErrorMessage(error)}."), + stackTrace); + }); } Future close() { diff --git a/lib/src/runner/browser/safari.dart b/lib/src/runner/browser/safari.dart index 4ad28da4a41180fb1df9a2cbf837930e18fa4728..af697850b282d954d87c23a39554a494d9a3d664 100644 --- a/lib/src/runner/browser/safari.dart +++ b/lib/src/runner/browser/safari.dart @@ -9,8 +9,11 @@ import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as p; +import 'package:stack_trace/stack_trace.dart'; import '../../util/io.dart'; +import '../../utils.dart'; +import '../application_exception.dart'; import 'browser.dart'; /// A class for running an instance of Safari. @@ -59,9 +62,19 @@ class Safari implements Browser { return _process.exitCode; }); }).then((exitCode) { - if (exitCode != 0) throw "Safari failed with exit code $exitCode."; - }).then(_onExitCompleter.complete) - .catchError(_onExitCompleter.completeError); + if (exitCode == 0) return null; + + return UTF8.decodeStream(_process.stderr).then((error) { + throw new ApplicationException( + "Safari failed with exit code $exitCode:\n$error"); + }); + }).then(_onExitCompleter.complete).catchError((error, stackTrace) { + if (stackTrace == null) stackTrace = new Trace.current(); + _onExitCompleter.completeError( + new ApplicationException( + "Failed to start Safari: ${getErrorMessage(error)}."), + stackTrace); + }); } Future close() { diff --git a/lib/src/runner/browser/server.dart b/lib/src/runner/browser/server.dart index fb0e55abddf6f16b6d0569b6715785ac54854bd8..17862ebcb7fa4b844b4d49782f8f2a60442c7361 100644 --- a/lib/src/runner/browser/server.dart +++ b/lib/src/runner/browser/server.dart @@ -266,7 +266,7 @@ void main() { // TODO(nweiz): Don't start the browser until all the suites are compiled. return _browserManagerFor(browser).then((browserManager) { - if (_closed) return null; + if (_closed || browserManager == null) return null; return browserManager.loadSuite(path, suiteUrl, metadata); }).then((suite) { if (_closed) return null;