Listen for uncaught exceptions during loading of a web test suite in Chrome (flutter/engine#55166)

Without this the test runner will hang if the compiled test is invalid and unable to execute.
This commit is contained in:
Jason Simmons 2024-09-27 10:18:18 -07:00 committed by GitHub
parent fdbad460a5
commit 96d24ff033
3 changed files with 56 additions and 4 deletions

View File

@ -75,6 +75,12 @@ abstract class Browser {
/// with an error.
Future<void> get onExit;
/// A future that completes if the browser is notified about an uncaught
/// exception.
///
/// Returns `null` if the browser does not support this.
Future<String>? get onUncaughtException => null;
/// Closes the browser
///
/// Returns the same [Future] as [onExit], except that it won't emit

View File

@ -20,6 +20,8 @@ import 'common.dart';
import 'environment.dart';
import 'package_lock.dart';
const String kBlankPageUrl = 'about:blank';
/// Provides an environment for desktop Chrome.
class ChromeEnvironment implements BrowserEnvironment {
ChromeEnvironment({
@ -82,6 +84,7 @@ class Chrome extends Browser {
required bool useDwarf,
}) {
final Completer<Uri> remoteDebuggerCompleter = Completer<Uri>.sync();
final Completer<String> exceptionCompleter = Completer<String>();
return Chrome._(BrowserProcess(() async {
// A good source of various Chrome CLI options:
// https://peter.sh/experiments/chromium-command-line-switches/
@ -98,7 +101,7 @@ class Chrome extends Browser {
final String dir = await generateUserDirectory(installation, useDwarf);
final List<String> args = <String>[
'--user-data-dir=$dir',
url.toString(),
kBlankPageUrl,
if (!debug)
'--headless',
if (isChromeNoSandbox)
@ -139,6 +142,8 @@ class Chrome extends Browser {
final Process process =
await _spawnChromiumProcess(installation.executable, args);
await setupChromiumTab(url, exceptionCompleter);
remoteDebuggerCompleter.complete(
getRemoteDebuggerUrl(Uri.parse('http://localhost:$kDevtoolsPort')));
@ -146,10 +151,10 @@ class Chrome extends Browser {
.then((_) => Directory(dir).deleteSync(recursive: true)));
return process;
}), remoteDebuggerCompleter.future);
}), remoteDebuggerCompleter.future, exceptionCompleter.future);
}
Chrome._(this._process, this.remoteDebuggerUrl);
Chrome._(this._process, this.remoteDebuggerUrl, this._onUncaughtException);
static Future<String> generateUserDirectory(
BrowserInstallation installation,
@ -211,12 +216,17 @@ class Chrome extends Browser {
final BrowserProcess _process;
final Future<String> _onUncaughtException;
@override
final Future<Uri> remoteDebuggerUrl;
@override
Future<void> get onExit => _process.onExit;
@override
Future<String>? get onUncaughtException => _onUncaughtException;
@override
Future<void> close() => _process.close();
@ -378,3 +388,28 @@ Future<Uri> getRemoteDebuggerUrl(Uri base) async {
return base;
}
}
Future<void> setupChromiumTab(
Uri url, Completer<String> exceptionCompleter) async {
final wip.ChromeConnection chromeConnection =
wip.ChromeConnection('localhost', kDevtoolsPort);
final wip.ChromeTab? chromeTab = await chromeConnection.getTab(
(wip.ChromeTab chromeTab) => chromeTab.url == kBlankPageUrl);
final wip.WipConnection wipConnection = await chromeTab!.connect();
await wipConnection.runtime.enable();
wipConnection.runtime.onExceptionThrown.listen(
(wip.ExceptionThrownEvent event) {
if (!exceptionCompleter.isCompleted) {
final String text = event.exceptionDetails.text;
final String? description = event.exceptionDetails.exception?.description;
exceptionCompleter.complete('$text: $description');
}
}
);
await wipConnection.page.enable();
await wipConnection.page.navigate(url.toString());
}

View File

@ -1059,7 +1059,18 @@ class BrowserManager {
}
_controllers.add(controller!);
return await controller!.suite;
final List<Future<RunnerSuite>> futures = <Future<RunnerSuite>>[
controller!.suite
];
if (_browser.onUncaughtException != null) {
futures.add(_browser.onUncaughtException!.then<RunnerSuite>(
(String error) =>
throw Exception('Exception while loading suite: $error')));
}
final RunnerSuite suite = await Future.any(futures);
return suite;
} catch (_) {
closeIframe();
rethrow;