From ad540e4f3f2665e5fe2b73ce1f04db3d666643db Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Wed, 23 Jun 2021 19:26:02 -0700 Subject: [PATCH] [flutter_tools] retry chrome launch up to 3 times (#85162) --- .../flutter_tools/lib/src/web/chrome.dart | 25 ++++--- .../test/general.shard/web/chrome_test.dart | 65 +++++++++++++++---- 2 files changed, 69 insertions(+), 21 deletions(-) diff --git a/packages/flutter_tools/lib/src/web/chrome.dart b/packages/flutter_tools/lib/src/web/chrome.dart index a3a252e67f..70181b07f8 100644 --- a/packages/flutter_tools/lib/src/web/chrome.dart +++ b/packages/flutter_tools/lib/src/web/chrome.dart @@ -249,7 +249,9 @@ class ChromiumLauncher { // Keep attempting to launch the browser until one of: // - Chrome launched successfully, in which case we just return from the loop. - // - The tool detected an unretriable Chrome error, in which case we throw ToolExit. + // - The tool reached the maximum retry count, in which case we throw ToolExit. + const int kMaxRetries = 3; + int retry = 0; while (true) { final Process process = await _processManager.start(args); @@ -263,6 +265,7 @@ class ChromiumLauncher { // Wait until the DevTools are listening before trying to connect. This is // only required for flutter_test --platform=chrome and not flutter run. bool hitGlibcBug = false; + bool shouldRetry = false; await process.stderr .transform(utf8.decoder) .transform(const LineSplitter()) @@ -270,6 +273,7 @@ class ChromiumLauncher { _logger.printTrace('[CHROME]:$line'); if (line.contains(_kGlibcError)) { hitGlibcBug = true; + shouldRetry = true; } return line; }) @@ -282,17 +286,22 @@ class ChromiumLauncher { // Return value unused. return ''; } - _logger.printTrace('Failed to launch browser. Command used to launch it: ${args.join(' ')}'); - throw ToolExit( - 'Failed to launch browser. Make sure you are using an up-to-date ' - 'Chrome or Edge. Otherwise, consider using -d web-server instead ' - 'and filing an issue at https://github.com/flutter/flutter/issues.', - ); + if (retry >= kMaxRetries) { + _logger.printTrace('Failed to launch browser after $kMaxRetries tries. Command used to launch it: ${args.join(' ')}'); + throw ToolExit( + 'Failed to launch browser. Make sure you are using an up-to-date ' + 'Chrome or Edge. Otherwise, consider using -d web-server instead ' + 'and filing an issue at https://github.com/flutter/flutter/issues.', + ); + } + shouldRetry = true; + return ''; }); - if (!hitGlibcBug) { + if (!hitGlibcBug && !shouldRetry) { return process; } + retry += 1; // A precaution that avoids accumulating browser processes, in case the // glibc bug doesn't cause the browser to quit and we keep looping and diff --git a/packages/flutter_tools/test/general.shard/web/chrome_test.dart b/packages/flutter_tools/test/general.shard/web/chrome_test.dart index b52f87e8e5..fa1af031fe 100644 --- a/packages/flutter_tools/test/general.shard/web/chrome_test.dart +++ b/packages/flutter_tools/test/general.shard/web/chrome_test.dart @@ -461,22 +461,61 @@ void main() { ); }); - testWithoutContext('gives up retrying when a non-glibc error happens', () async { + testWithoutContext('can retry launch when chrome fails to start', () async { + const List args = [ + 'example_chrome', + '--user-data-dir=/.tmp_rand0/flutter_tools_chrome_device.rand0', + '--remote-debugging-port=12345', + ...kChromeArgs, + '--headless', + '--disable-gpu', + '--no-sandbox', + '--window-size=2400,1800', + 'example_url', + ]; + + // Pretend to random error 3 times. + for (int i = 0; i < 3; i++) { + processManager.addCommand(const FakeCommand( + command: args, + stderr: 'BLAH BLAH', + )); + } + + // Succeed on the 4th try. processManager.addCommand(const FakeCommand( - command: [ - 'example_chrome', - '--user-data-dir=/.tmp_rand0/flutter_tools_chrome_device.rand0', - '--remote-debugging-port=12345', - ...kChromeArgs, - '--headless', - '--disable-gpu', - '--no-sandbox', - '--window-size=2400,1800', - 'example_url', - ], - stderr: 'nothing in the std error indicating glibc error', + command: args, + stderr: kDevtoolsStderr, )); + expect( + () async => chromeLauncher.launch( + 'example_url', + skipCheck: true, + headless: true, + ), + returnsNormally, + ); + }); + + testWithoutContext('gives up retrying when an error happens more than 3 times', () async { + for (int i = 0; i < 4; i++) { + processManager.addCommand(const FakeCommand( + command: [ + 'example_chrome', + '--user-data-dir=/.tmp_rand0/flutter_tools_chrome_device.rand0', + '--remote-debugging-port=12345', + ...kChromeArgs, + '--headless', + '--disable-gpu', + '--no-sandbox', + '--window-size=2400,1800', + 'example_url', + ], + stderr: 'nothing in the std error indicating glibc error', + )); + } + expect( () async => chromeLauncher.launch( 'example_url',