Fix stack trace parsing on non-debug builds; add e2e tests (#50652) * Fix stack trace parsing on non-debug builds; add e2e tests
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index f40fc27..15fa50a 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -338,6 +338,9 @@ DebuggingOptions _createDebuggingOptions() { final BuildInfo buildInfo = getBuildInfo(); + final int browserDebugPort = featureFlags.isWebEnabled && argResults.wasParsed('web-browser-debug-port') + ? int.parse(stringArg('web-browser-debug-port')) + : null; if (buildInfo.mode.isRelease) { return DebuggingOptions.disabled( buildInfo, @@ -345,6 +348,8 @@ hostname: featureFlags.isWebEnabled ? stringArg('web-hostname') : '', port: featureFlags.isWebEnabled ? stringArg('web-port') : '', webEnableExposeUrl: featureFlags.isWebEnabled && boolArg('web-allow-expose-url'), + webRunHeadless: featureFlags.isWebEnabled && boolArg('web-run-headless'), + webBrowserDebugPort: browserDebugPort, ); } else { return DebuggingOptions.enabled( @@ -367,6 +372,8 @@ hostname: featureFlags.isWebEnabled ? stringArg('web-hostname') : '', port: featureFlags.isWebEnabled ? stringArg('web-port') : '', webEnableExposeUrl: featureFlags.isWebEnabled && boolArg('web-allow-expose-url'), + webRunHeadless: featureFlags.isWebEnabled && boolArg('web-run-headless'), + webBrowserDebugPort: browserDebugPort, vmserviceOutFile: stringArg('vmservice-out-file'), // Allow forcing fast-start to off to prevent doing more work on devices that // don't support it.
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index c2f33fc..b3dd7d1 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart
@@ -536,6 +536,8 @@ this.hostname, this.port, this.webEnableExposeUrl, + this.webRunHeadless = false, + this.webBrowserDebugPort, this.vmserviceOutFile, this.fastStart = false, }) : debuggingEnabled = true; @@ -545,6 +547,8 @@ this.port, this.hostname, this.webEnableExposeUrl, + this.webRunHeadless = false, + this.webBrowserDebugPort, this.cacheSkSL = false, }) : debuggingEnabled = false, useTestFonts = false, @@ -585,6 +589,17 @@ final String port; final String hostname; final bool webEnableExposeUrl; + + /// Whether to run the browser in headless mode. + /// + /// Some CI environments do not provide a display and fail to launch the + /// browser with full graphics stack. Some browsers provide a special + /// "headless" mode that runs the browser with no graphics. + final bool webRunHeadless; + + /// The port the browser should use for its debugging protocol. + final int webBrowserDebugPort; + /// A file where the vmservice URL should be written after the application is started. final String vmserviceOutFile; final bool fastStart;
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 397e438..a17cb74 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -173,6 +173,19 @@ 'when running on remote machines.', hide: hide, ); + argParser.addFlag('web-run-headless', + defaultsTo: false, + help: 'Launches the browser in headless mode. Currently only Chrome ' + 'supports this option.', + hide: true, + ); + argParser.addOption('web-browser-debug-port', + help: 'The debug port the browser should use. If not specified, a ' + 'random port is selected. Currently only Chrome supports this option. ' + 'It serves the Chrome DevTools Protocol ' + '(https://chromedevtools.github.io/devtools-protocol/).', + hide: true, + ); } void usesTargetOption() {
diff --git a/packages/flutter_tools/lib/src/web/chrome.dart b/packages/flutter_tools/lib/src/web/chrome.dart index 99953cf..f1e67c6 100644 --- a/packages/flutter_tools/lib/src/web/chrome.dart +++ b/packages/flutter_tools/lib/src/web/chrome.dart
@@ -97,8 +97,11 @@ /// `headless` defaults to false, and controls whether we open a headless or /// a `headfull` browser. /// + /// `debugPort` is Chrome's debugging protocol port. If null, a random free + /// port is picked automatically. + /// /// `skipCheck` does not attempt to make a devtools connection before returning. - Future<Chrome> launch(String url, { bool headless = false, bool skipCheck = false, Directory dataDir }) async { + Future<Chrome> launch(String url, { bool headless = false, int debugPort, bool skipCheck = false, Directory dataDir }) async { // This is a JSON file which contains configuration from the // browser session, such as window position. It is located // under the Chrome data-dir folder. @@ -117,7 +120,7 @@ } } - final int port = await globals.os.findFreePort(); + final int port = debugPort ?? await globals.os.findFreePort(); final List<String> args = <String>[ chromeExecutable, // Using a tmp directory ensures that a new instance of chrome launches
diff --git a/packages/flutter_tools/lib/src/web/web_device.dart b/packages/flutter_tools/lib/src/web/web_device.dart index 518d72b..cf48542 100644 --- a/packages/flutter_tools/lib/src/web/web_device.dart +++ b/packages/flutter_tools/lib/src/web/web_device.dart
@@ -134,10 +134,14 @@ // See [ResidentWebRunner.run] in flutter_tools/lib/src/resident_web_runner.dart // for the web initialization and server logic. final String url = platformArgs['uri'] as String; - _chrome = await chromeLauncher.launch(url, + _chrome = await chromeLauncher.launch( + url, dataDir: globals.fs.currentDirectory .childDirectory('.dart_tool') - .childDirectory('chrome-device')); + .childDirectory('chrome-device'), + headless: debuggingOptions.webRunHeadless, + debugPort: debuggingOptions.webBrowserDebugPort, + ); globals.logger.sendEvent('app.webLaunchUrl', <String, dynamic>{'url': url, 'launched': true});
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 83247b9..5bcdc94 100644 --- a/packages/flutter_tools/test/general.shard/web/chrome_test.dart +++ b/packages/flutter_tools/test/general.shard/web/chrome_test.dart
@@ -54,10 +54,10 @@ resetChromeForTesting(); }); - test('can launch chrome and connect to the devtools', () => testbed.run(() async { - const List<String> expected = <String>[ + List<String> expectChromeArgs({int debugPort = 1234}) { + return <String>[ 'example_chrome', - '--remote-debugging-port=1234', + '--remote-debugging-port=$debugPort', '--disable-background-timer-throttling', '--disable-extensions', '--disable-popup-blocking', @@ -68,11 +68,18 @@ '--disable-translate', 'example_url', ]; + } + test('can launch chrome and connect to the devtools', () => testbed.run(() async { await chromeLauncher.launch('example_url', skipCheck: true); final VerificationResult result = verify(globals.processManager.start(captureAny)); + expect(result.captured.single, containsAll(expectChromeArgs())); + })); - expect(result.captured.single, containsAll(expected)); + test('can launch chrome with a custom debug port', () => testbed.run(() async { + await chromeLauncher.launch('example_url', skipCheck: true, debugPort: 10000); + final VerificationResult result = verify(globals.processManager.start(captureAny)); + expect(result.captured.single, containsAll(expectChromeArgs(debugPort: 10000))); })); test('can seed chrome temp directory with existing preferences', () => testbed.run(() async {
diff --git a/packages/flutter_tools/test/integration.shard/test_driver.dart b/packages/flutter_tools/test/integration.shard/test_driver.dart index 7fa4c91..d46e5d8 100644 --- a/packages/flutter_tools/test/integration.shard/test_driver.dart +++ b/packages/flutter_tools/test/integration.shard/test_driver.dart
@@ -498,7 +498,7 @@ // fast. unawaited(_process.exitCode.then((_) { if (!prematureExitGuard.isCompleted) { - prematureExitGuard.completeError('Process existed prematurely: ${args.join(' ')}: $_errorBuffer'); + prematureExitGuard.completeError('Process exited prematurely: ${args.join(' ')}: $_errorBuffer'); } }));