Unverified Commit 44d9af80 authored by Anna Gringauze's avatar Anna Gringauze Committed by GitHub

Flutter_tools for web: report error messages with stacks on exit (#87386)

* Flutter_tools for web platform: report error messages with stacks on toolExit

Print error messages and stacks in verbose mode before calling
`throwToolExit` on communication errors to chrome and dwds.

This will help us disagnose CI flakes:

Helps: https://github.com/flutter/flutter/issues/84012
Closes: https://github.com/flutter/flutter/issues/87149

* Added tests

* Extend timeout for testing failure to connect to chrome

* Update rest of chrome tests to await until matching is finished
parent d1eff7f4
...@@ -314,17 +314,21 @@ class ResidentWebRunner extends ResidentRunner { ...@@ -314,17 +314,21 @@ class ResidentWebRunner extends ResidentRunner {
enableDevTools: enableDevTools, enableDevTools: enableDevTools,
); );
}); });
} on WebSocketException { } on WebSocketException catch (error, stackTrace) {
appFailedToStart(); appFailedToStart();
_logger.printError('$error', stackTrace: stackTrace);
throwToolExit(kExitMessage); throwToolExit(kExitMessage);
} on ChromeDebugException { } on ChromeDebugException catch (error, stackTrace) {
appFailedToStart(); appFailedToStart();
_logger.printError('$error', stackTrace: stackTrace);
throwToolExit(kExitMessage); throwToolExit(kExitMessage);
} on AppConnectionException { } on AppConnectionException catch (error, stackTrace) {
appFailedToStart(); appFailedToStart();
_logger.printError('$error', stackTrace: stackTrace);
throwToolExit(kExitMessage); throwToolExit(kExitMessage);
} on SocketException { } on SocketException catch (error, stackTrace) {
appFailedToStart(); appFailedToStart();
_logger.printError('$error', stackTrace: stackTrace);
throwToolExit(kExitMessage); throwToolExit(kExitMessage);
} on Exception { } on Exception {
appFailedToStart(); appFailedToStart();
......
...@@ -396,10 +396,11 @@ class ChromiumLauncher { ...@@ -396,10 +396,11 @@ class ChromiumLauncher {
if (!skipCheck) { if (!skipCheck) {
try { try {
await chrome.chromeConnection.getTabs(); await chrome.chromeConnection.getTabs();
} on Exception catch (e) { } on Exception catch (error, stackTrace) {
_logger.printError('$error', stackTrace: stackTrace);
await chrome.close(); await chrome.close();
throwToolExit( throwToolExit(
'Unable to connect to Chrome debug port: ${chrome.debugPort}\n $e'); 'Unable to connect to Chrome debug port: ${chrome.debugPort}\n $error');
} }
} }
currentCompleter.complete(chrome); currentCompleter.complete(chrome);
......
...@@ -942,12 +942,14 @@ void main() { ...@@ -942,12 +942,14 @@ void main() {
}); });
testUsingContext('Successfully turns WebSocketException into ToolExit', () async { testUsingContext('Successfully turns WebSocketException into ToolExit', () async {
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice); final BufferLogger logger = BufferLogger.test();
final ResidentRunner residentWebRunner = setUpResidentRunner(flutterDevice, logger: logger);
fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]); fakeVmServiceHost = FakeVmServiceHost(requests: <VmServiceExpectation>[]);
_setupMocks(); _setupMocks();
webDevFS.exception = const WebSocketException(); webDevFS.exception = const WebSocketException();
await expectLater(residentWebRunner.run, throwsToolExit()); await expectLater(residentWebRunner.run, throwsToolExit());
expect(logger.errorText, contains('WebSocketException'));
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
......
...@@ -62,13 +62,12 @@ void main() { ...@@ -62,13 +62,12 @@ void main() {
}); });
testWithoutContext('can launch chrome and connect to the devtools', () async { testWithoutContext('can launch chrome and connect to the devtools', () async {
expect( await expectReturnsNormallyLater(
() async => _testLaunchChrome( _testLaunchChrome(
'/.tmp_rand0/flutter_tools_chrome_device.rand0', '/.tmp_rand0/flutter_tools_chrome_device.rand0',
processManager, processManager,
chromeLauncher, chromeLauncher,
), )
returnsNormally,
); );
}); });
...@@ -79,13 +78,13 @@ void main() { ...@@ -79,13 +78,13 @@ void main() {
chromeLauncher, chromeLauncher,
); );
expect( await expectToolExitLater(
() async => _testLaunchChrome( _testLaunchChrome(
'/.tmp_rand0/flutter_tools_chrome_device.rand1', '/.tmp_rand0/flutter_tools_chrome_device.rand1',
processManager, processManager,
chromeLauncher, chromeLauncher,
), ),
throwsToolExit(message: 'Only one instance of chrome can be started'), contains('Only one instance of chrome can be started'),
); );
}); });
...@@ -97,13 +96,12 @@ void main() { ...@@ -97,13 +96,12 @@ void main() {
); );
await chrome.close(); await chrome.close();
expect( await expectReturnsNormallyLater(
() async => _testLaunchChrome( _testLaunchChrome(
'/.tmp_rand0/flutter_tools_chrome_device.rand1', '/.tmp_rand0/flutter_tools_chrome_device.rand1',
processManager, processManager,
chromeLauncher, chromeLauncher,
), )
returnsNormally,
); );
}); });
...@@ -219,12 +217,11 @@ void main() { ...@@ -219,12 +217,11 @@ void main() {
) )
]); ]);
expect( await expectReturnsNormallyLater(
() async => chromiumLauncher.launch( chromiumLauncher.launch(
'example_url', 'example_url',
skipCheck: true, skipCheck: true,
), )
returnsNormally,
); );
}); });
...@@ -259,12 +256,11 @@ void main() { ...@@ -259,12 +256,11 @@ void main() {
) )
]); ]);
expect( await expectReturnsNormallyLater(
() async => chromiumLauncher.launch( chromiumLauncher.launch(
'example_url', 'example_url',
skipCheck: true, skipCheck: true,
), )
returnsNormally,
); );
}); });
...@@ -301,12 +297,11 @@ void main() { ...@@ -301,12 +297,11 @@ void main() {
), ),
]); ]);
expect( await expectReturnsNormallyLater(
() async => chromiumLauncher.launch( chromiumLauncher.launch(
'example_url', 'example_url',
skipCheck: true, skipCheck: true,
), )
returnsNormally,
); );
}); });
...@@ -322,13 +317,12 @@ void main() { ...@@ -322,13 +317,12 @@ void main() {
stderr: kDevtoolsStderr, stderr: kDevtoolsStderr,
)); ));
expect( await expectReturnsNormallyLater(
() async => chromeLauncher.launch( chromeLauncher.launch(
'example_url', 'example_url',
skipCheck: true, skipCheck: true,
debugPort: 10000, debugPort: 10000,
), )
returnsNormally,
); );
}); });
...@@ -348,13 +342,12 @@ void main() { ...@@ -348,13 +342,12 @@ void main() {
stderr: kDevtoolsStderr, stderr: kDevtoolsStderr,
)); ));
expect( await expectReturnsNormallyLater(
() async => chromeLauncher.launch( chromeLauncher.launch(
'example_url', 'example_url',
skipCheck: true, skipCheck: true,
headless: true, headless: true,
), )
returnsNormally,
); );
}); });
...@@ -451,13 +444,12 @@ void main() { ...@@ -451,13 +444,12 @@ void main() {
stderr: kDevtoolsStderr, stderr: kDevtoolsStderr,
)); ));
expect( await expectReturnsNormallyLater(
() async => chromeLauncher.launch( chromeLauncher.launch(
'example_url', 'example_url',
skipCheck: true, skipCheck: true,
headless: true, headless: true,
), )
returnsNormally,
); );
}); });
...@@ -488,13 +480,12 @@ void main() { ...@@ -488,13 +480,12 @@ void main() {
stderr: kDevtoolsStderr, stderr: kDevtoolsStderr,
)); ));
expect( await expectReturnsNormallyLater(
() async => chromeLauncher.launch( chromeLauncher.launch(
'example_url', 'example_url',
skipCheck: true, skipCheck: true,
headless: true, headless: true,
), )
returnsNormally,
); );
}); });
...@@ -516,15 +507,47 @@ void main() { ...@@ -516,15 +507,47 @@ void main() {
)); ));
} }
expect( await expectToolExitLater(
() async => chromeLauncher.launch( chromeLauncher.launch(
'example_url', 'example_url',
skipCheck: true, skipCheck: true,
headless: true, headless: true,
), ),
throwsToolExit(message: 'Failed to launch browser.'), contains('Failed to launch browser.'),
); );
}); });
testWithoutContext('Logs an error and exits if connection check fails.', () async {
final BufferLogger logger = BufferLogger.test();
final ChromiumLauncher chromiumLauncher = ChromiumLauncher(
fileSystem: fileSystem,
platform: platform,
processManager: processManager,
operatingSystemUtils: operatingSystemUtils,
browserFinder: findChromeExecutable,
logger: logger,
);
processManager.addCommand(const FakeCommand(
command: <String>[
'example_chrome',
'--user-data-dir=/.tmp_rand0/flutter_tools_chrome_device.rand0',
'--remote-debugging-port=12345',
...kChromeArgs,
'example_url',
],
stderr: kDevtoolsStderr,
));
await expectToolExitLater(
chromiumLauncher.launch(
'example_url',
skipCheck: false,
headless: false,
),
contains('Unable to connect to Chrome debug port:'),
);
expect(logger.errorText, contains('SocketException'));
}, timeout: const Timeout.factor(2));
} }
Future<Chromium> _testLaunchChrome(String userDataDir, FakeProcessManager processManager, ChromiumLauncher chromeLauncher) { Future<Chromium> _testLaunchChrome(String userDataDir, FakeProcessManager processManager, ChromiumLauncher chromeLauncher) {
......
...@@ -124,6 +124,15 @@ Future<void> expectToolExitLater(Future<dynamic> future, Matcher messageMatcher) ...@@ -124,6 +124,15 @@ Future<void> expectToolExitLater(Future<dynamic> future, Matcher messageMatcher)
} }
} }
Future<void> expectReturnsNormallyLater(Future<dynamic> future) async {
try {
await future;
// Catch all exceptions to give a better test failure message.
} catch (e, trace) { // ignore: avoid_catches_without_on_clauses
fail('Expected to run with no exceptions, got $e\n$trace');
}
}
Matcher containsIgnoringWhitespace(String toSearch) { Matcher containsIgnoringWhitespace(String toSearch) {
return predicate( return predicate(
(String source) { (String source) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment