diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index 9e6017e97f..999f545f06 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -913,7 +913,45 @@ class DebuggingOptions { /// * https://github.com/dart-lang/sdk/blob/main/sdk/lib/html/doc/NATIVE_NULL_ASSERTIONS.md final bool nativeNullAssertions; - bool get hasObservatoryPort => hostVmServicePort != null; + List getIOSLaunchArguments(EnvironmentType environmentType, String? route, Map platformArgs) { + final String dartVmFlags = computeDartVmFlags(this); + return [ + '--enable-dart-profiling', + if (disableServiceAuthCodes) '--disable-service-auth-codes', + if (disablePortPublication) '--disable-observatory-publication', + if (startPaused) '--start-paused', + // Wrap dart flags in quotes for physical devices + if (environmentType == EnvironmentType.physical && dartVmFlags.isNotEmpty) + '--dart-flags="$dartVmFlags"', + if (environmentType == EnvironmentType.simulator && dartVmFlags.isNotEmpty) + '--dart-flags=$dartVmFlags', + if (useTestFonts) '--use-test-fonts', + if (debuggingEnabled) ...[ + '--enable-checked-mode', + '--verify-entry-points', + ], + if (enableSoftwareRendering) '--enable-software-rendering', + if (traceSystrace) '--trace-systrace', + if (skiaDeterministicRendering) '--skia-deterministic-rendering', + if (traceSkia) '--trace-skia', + if (traceAllowlist != null) '--trace-allowlist="$traceAllowlist"', + if (traceSkiaAllowlist != null) '--trace-skia-allowlist="$traceSkiaAllowlist"', + if (endlessTraceBuffer) '--endless-trace-buffer', + if (dumpSkpOnShaderCompilation) '--dump-skp-on-shader-compilation', + if (verboseSystemLogs) '--verbose-logging', + if (cacheSkSL) '--cache-sksl', + if (purgePersistentCache) '--purge-persistent-cache', + if (route != null) '--route=$route', + if (platformArgs['trace-startup'] as bool? ?? false) '--trace-startup', + if (enableImpeller) '--enable-impeller', + if (environmentType == EnvironmentType.physical && deviceVmServicePort != null) + '--observatory-port=$deviceVmServicePort', + // The simulator "device" is actually on the host machine so no ports will be forwarded. + // Use the suggested host port. + if (environmentType == EnvironmentType.simulator && hostVmServicePort != null) + '--observatory-port=$hostVmServicePort', + ]; + } Map toJson() => { 'debuggingEnabled': debuggingEnabled, diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart index e9f662905c..8e94f5a29f 100644 --- a/packages/flutter_tools/lib/src/ios/devices.dart +++ b/packages/flutter_tools/lib/src/ios/devices.dart @@ -348,34 +348,11 @@ class IOSDevice extends Device { } // Step 3: Attempt to install the application on the device. - final String dartVmFlags = computeDartVmFlags(debuggingOptions); - final List launchArguments = [ - '--enable-dart-profiling', - '--disable-service-auth-codes', - if (debuggingOptions.disablePortPublication) '--disable-observatory-publication', - if (debuggingOptions.startPaused) '--start-paused', - if (dartVmFlags.isNotEmpty) '--dart-flags="$dartVmFlags"', - if (debuggingOptions.useTestFonts) '--use-test-fonts', - if (debuggingOptions.debuggingEnabled) ...[ - '--enable-checked-mode', - '--verify-entry-points', - ], - if (debuggingOptions.enableSoftwareRendering) '--enable-software-rendering', - if (debuggingOptions.traceSystrace) '--trace-systrace', - if (debuggingOptions.skiaDeterministicRendering) '--skia-deterministic-rendering', - if (debuggingOptions.traceSkia) '--trace-skia', - if (debuggingOptions.traceAllowlist != null) '--trace-allowlist="${debuggingOptions.traceAllowlist}"', - if (debuggingOptions.traceSkiaAllowlist != null) '--trace-skia-allowlist="${debuggingOptions.traceSkiaAllowlist}"', - if (debuggingOptions.endlessTraceBuffer) '--endless-trace-buffer', - if (debuggingOptions.dumpSkpOnShaderCompilation) '--dump-skp-on-shader-compilation', - if (debuggingOptions.verboseSystemLogs) '--verbose-logging', - if (debuggingOptions.cacheSkSL) '--cache-sksl', - if (debuggingOptions.purgePersistentCache) '--purge-persistent-cache', - if (route != null) '--route=$route', - if (platformArgs['trace-startup'] as bool? ?? false) '--trace-startup', - if (debuggingOptions.enableImpeller) '--enable-impeller', - ]; - + final List launchArguments = debuggingOptions.getIOSLaunchArguments( + EnvironmentType.physical, + route, + platformArgs, + ); final Status installStatus = _logger.startProgress( 'Installing and launching...', ); diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index 3366b9dbb8..6da27e7c4c 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart @@ -445,27 +445,11 @@ class IOSSimulator extends Device { } // Prepare launch arguments. - final String dartVmFlags = computeDartVmFlags(debuggingOptions); - final List args = [ - '--enable-dart-profiling', - if (debuggingOptions.debuggingEnabled) ...[ - if (debuggingOptions.buildInfo.isDebug) ...[ - '--enable-checked-mode', - '--verify-entry-points', - ], - if (debuggingOptions.enableSoftwareRendering) '--enable-software-rendering', - if (debuggingOptions.startPaused) '--start-paused', - if (debuggingOptions.disableServiceAuthCodes) '--disable-service-auth-codes', - if (debuggingOptions.skiaDeterministicRendering) '--skia-deterministic-rendering', - if (debuggingOptions.useTestFonts) '--use-test-fonts', - if (debuggingOptions.traceAllowlist != null) '--trace-allowlist="${debuggingOptions.traceAllowlist}"', - if (debuggingOptions.traceSkiaAllowlist != null) '--trace-skia-allowlist="${debuggingOptions.traceSkiaAllowlist}"', - if (dartVmFlags.isNotEmpty) '--dart-flags=$dartVmFlags', - if (debuggingOptions.enableImpeller) '--enable-impeller', - '--observatory-port=${debuggingOptions.hostVmServicePort ?? 0}', - if (route != null) '--route=$route', - ], - ]; + final List launchArguments = debuggingOptions.getIOSLaunchArguments( + EnvironmentType.simulator, + route, + platformArgs, + ); ProtocolDiscovery? observatoryDiscovery; if (debuggingOptions.debuggingEnabled) { @@ -491,7 +475,7 @@ class IOSSimulator extends Device { return LaunchResult.failed(); } - await _simControl.launch(id, bundleIdentifier, args); + await _simControl.launch(id, bundleIdentifier, launchArguments); } on Exception catch (error) { globals.printError('$error'); return LaunchResult.failed(); diff --git a/packages/flutter_tools/lib/src/tester/flutter_tester.dart b/packages/flutter_tools/lib/src/tester/flutter_tester.dart index 8662321a0f..57af3e8304 100644 --- a/packages/flutter_tools/lib/src/tester/flutter_tester.dart +++ b/packages/flutter_tools/lib/src/tester/flutter_tester.dart @@ -172,7 +172,7 @@ class FlutterTesterDevice extends Device { '--start-paused', if (debuggingOptions.disableServiceAuthCodes) '--disable-service-auth-codes', - if (debuggingOptions.hasObservatoryPort) + if (debuggingOptions.hostVmServicePort != null) '--observatory-port=${debuggingOptions.hostVmServicePort}', applicationKernelFilePath, ]; diff --git a/packages/flutter_tools/test/general.shard/device_test.dart b/packages/flutter_tools/test/general.shard/device_test.dart index e03ed0ed67..06133a4f3a 100644 --- a/packages/flutter_tools/test/general.shard/device_test.dart +++ b/packages/flutter_tools/test/general.shard/device_test.dart @@ -466,6 +466,205 @@ void main() { expect(deserialized.enableImpeller, original.enableImpeller); }); }); + + group('Get iOS launch arguments from DebuggingOptions', () { + testWithoutContext('Get launch arguments for physical device with debugging enabled with all launch arguments', () { + final DebuggingOptions original = DebuggingOptions.enabled( + BuildInfo.debug, + startPaused: true, + disableServiceAuthCodes: true, + disablePortPublication: true, + dartFlags: '--foo', + useTestFonts: true, + enableSoftwareRendering: true, + skiaDeterministicRendering: true, + traceSkia: true, + traceAllowlist: 'foo', + traceSkiaAllowlist: 'skia.a,skia.b', + traceSystrace: true, + endlessTraceBuffer: true, + dumpSkpOnShaderCompilation: true, + cacheSkSL: true, + purgePersistentCache: true, + verboseSystemLogs: true, + nullAssertions: true, + enableImpeller: true, + deviceVmServicePort: 0, + hostVmServicePort: 1, + ); + + final List launchArguments = original.getIOSLaunchArguments( + EnvironmentType.physical, + '/test', + { + 'trace-startup': true, + }, + ); + + expect( + launchArguments.join(' '), + [ + '--enable-dart-profiling', + '--disable-service-auth-codes', + '--disable-observatory-publication', + '--start-paused', + '--dart-flags="--foo,--null_assertions"', + '--use-test-fonts', + '--enable-checked-mode', + '--verify-entry-points', + '--enable-software-rendering', + '--trace-systrace', + '--skia-deterministic-rendering', + '--trace-skia', + '--trace-allowlist="foo"', + '--trace-skia-allowlist="skia.a,skia.b"', + '--endless-trace-buffer', + '--dump-skp-on-shader-compilation', + '--verbose-logging', + '--cache-sksl', + '--purge-persistent-cache', + '--route=/test', + '--trace-startup', + '--enable-impeller', + '--observatory-port=0', + ].join(' '), + ); + }); + + testWithoutContext('Get launch arguments for physical device with debugging enabled with no launch arguments', () { + final DebuggingOptions original = DebuggingOptions.enabled( + BuildInfo.debug, + ); + + final List launchArguments = original.getIOSLaunchArguments( + EnvironmentType.physical, + null, + {}, + ); + + expect( + launchArguments.join(' '), + [ + '--enable-dart-profiling', + '--enable-checked-mode', + '--verify-entry-points', + ].join(' '), + ); + }); + + testWithoutContext('Get launch arguments for physical device with debugging disabled with available launch arguments', () { + final DebuggingOptions original = DebuggingOptions.disabled( + BuildInfo.debug, + traceAllowlist: 'foo', + cacheSkSL: true, + enableImpeller: true, + ); + + final List launchArguments = original.getIOSLaunchArguments( + EnvironmentType.physical, + '/test', + { + 'trace-startup': true, + }, + ); + + expect( + launchArguments.join(' '), + [ + '--enable-dart-profiling', + '--trace-allowlist="foo"', + '--cache-sksl', + '--route=/test', + '--trace-startup', + '--enable-impeller', + ].join(' '), + ); + }); + + testWithoutContext('Get launch arguments for simulator device with debugging enabled with all launch arguments', () { + final DebuggingOptions original = DebuggingOptions.enabled( + BuildInfo.debug, + startPaused: true, + disableServiceAuthCodes: true, + disablePortPublication: true, + dartFlags: '--foo', + useTestFonts: true, + enableSoftwareRendering: true, + skiaDeterministicRendering: true, + traceSkia: true, + traceAllowlist: 'foo', + traceSkiaAllowlist: 'skia.a,skia.b', + traceSystrace: true, + endlessTraceBuffer: true, + dumpSkpOnShaderCompilation: true, + cacheSkSL: true, + purgePersistentCache: true, + verboseSystemLogs: true, + nullAssertions: true, + enableImpeller: true, + deviceVmServicePort: 0, + hostVmServicePort: 1, + ); + + final List launchArguments = original.getIOSLaunchArguments( + EnvironmentType.simulator, + '/test', + { + 'trace-startup': true, + }, + ); + + expect( + launchArguments.join(' '), + [ + '--enable-dart-profiling', + '--disable-service-auth-codes', + '--disable-observatory-publication', + '--start-paused', + '--dart-flags=--foo,--null_assertions', + '--use-test-fonts', + '--enable-checked-mode', + '--verify-entry-points', + '--enable-software-rendering', + '--trace-systrace', + '--skia-deterministic-rendering', + '--trace-skia', + '--trace-allowlist="foo"', + '--trace-skia-allowlist="skia.a,skia.b"', + '--endless-trace-buffer', + '--dump-skp-on-shader-compilation', + '--verbose-logging', + '--cache-sksl', + '--purge-persistent-cache', + '--route=/test', + '--trace-startup', + '--enable-impeller', + '--observatory-port=1', + ].join(' '), + ); + }); + + testWithoutContext('Get launch arguments for simulator device with debugging enabled with no launch arguments', () { + final DebuggingOptions original = DebuggingOptions.enabled( + BuildInfo.debug, + ); + + final List launchArguments = original.getIOSLaunchArguments( + EnvironmentType.simulator, + null, + {}, + ); + + expect( + launchArguments.join(' '), + [ + '--enable-dart-profiling', + '--enable-checked-mode', + '--verify-entry-points', + ].join(' '), + ); + }); + }); } class TestDeviceManager extends DeviceManager { diff --git a/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart index 9ca197f3bc..853707c97c 100644 --- a/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart @@ -205,7 +205,6 @@ void main() { '--args', const [ '--enable-dart-profiling', - '--disable-service-auth-codes', ].join(' '), ]) ); diff --git a/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart index 1cff1a0aa7..c212a08b1f 100644 --- a/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/ios_device_start_prebuilt_test.dart @@ -38,7 +38,7 @@ const FakeCommand kLaunchReleaseCommand = FakeCommand( '--justlaunch', // These args are the default on DebuggingOptions. '--args', - '--enable-dart-profiling --disable-service-auth-codes', + '--enable-dart-profiling', ], environment: { 'PATH': '/usr/bin:null', @@ -56,7 +56,7 @@ const FakeCommand kLaunchDebugCommand = FakeCommand(command: [ '--no-wifi', '--justlaunch', '--args', - '--enable-dart-profiling --disable-service-auth-codes --enable-checked-mode --verify-entry-points', + '--enable-dart-profiling --enable-checked-mode --verify-entry-points', ], environment: { 'PATH': '/usr/bin:null', 'DYLD_LIBRARY_PATH': '/path/to/libraries', @@ -81,7 +81,7 @@ FakeCommand attachDebuggerCommand({ '--debug', '--no-wifi', '--args', - '--enable-dart-profiling --disable-service-auth-codes --enable-checked-mode --verify-entry-points', + '--enable-dart-profiling --enable-checked-mode --verify-entry-points', ], completer: completer, environment: const { @@ -285,12 +285,15 @@ void main() { '--disable-observatory-publication', '--start-paused', '--dart-flags="--foo,--null_assertions"', + '--use-test-fonts', '--enable-checked-mode', '--verify-entry-points', '--enable-software-rendering', '--trace-systrace', '--skia-deterministic-rendering', '--trace-skia', + '--trace-allowlist="foo"', + '--trace-skia-allowlist="skia.a,skia.b"', '--endless-trace-buffer', '--dump-skp-on-shader-compilation', '--verbose-logging', @@ -335,9 +338,12 @@ void main() { disableServiceAuthCodes: true, disablePortPublication: true, dartFlags: '--foo', + useTestFonts: true, enableSoftwareRendering: true, skiaDeterministicRendering: true, traceSkia: true, + traceAllowlist: 'foo', + traceSkiaAllowlist: 'skia.a,skia.b', traceSystrace: true, endlessTraceBuffer: true, dumpSkpOnShaderCompilation: true, @@ -354,6 +360,143 @@ void main() { expect(await device.stopApp(iosApp), false); expect(processManager, hasNoRemainingExpectations); }); + + testWithoutContext('startApp using route', () async { + final FileSystem fileSystem = MemoryFileSystem.test(); + final FakeProcessManager processManager = FakeProcessManager.list([ + FakeCommand( + command: [ + 'script', + '-t', + '0', + '/dev/null', + 'HostArtifact.iosDeploy', + '--id', + '123', + '--bundle', + '/', + '--debug', + '--no-wifi', + '--args', + [ + '--enable-dart-profiling', + '--enable-checked-mode', + '--verify-entry-points', + // The --route argument below is determined by what is passed into + // route argument to startApp. + '--route=/animation', + ].join(' '), + ], + environment: const { + 'PATH': '/usr/bin:null', + 'DYLD_LIBRARY_PATH': '/path/to/libraries', + }, + stdout: '(lldb) run\nsuccess', + ), + ]); + final IOSDevice device = setUpIOSDevice( + sdkVersion: '13.3', + processManager: processManager, + fileSystem: fileSystem, + ); + final IOSApp iosApp = PrebuiltIOSApp( + projectBundleId: 'app', + bundleName: 'Runner', + uncompressedBundle: fileSystem.currentDirectory, + applicationPackage: fileSystem.currentDirectory, + ); + final FakeDeviceLogReader deviceLogReader = FakeDeviceLogReader(); + + device.portForwarder = const NoOpDevicePortForwarder(); + device.setLogReader(iosApp, deviceLogReader); + + // Start writing messages to the log reader. + Timer.run(() { + deviceLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:1234'); + }); + + final LaunchResult launchResult = await device.startApp(iosApp, + prebuiltApplication: true, + debuggingOptions: DebuggingOptions.enabled( + BuildInfo.debug, + ), + platformArgs: {}, + route: '/animation', + ); + + expect(launchResult.started, true); + expect(await device.stopApp(iosApp), false); + expect(processManager, hasNoRemainingExpectations); + }); + + testWithoutContext('startApp using trace-startup', () async { + final FileSystem fileSystem = MemoryFileSystem.test(); + final FakeProcessManager processManager = FakeProcessManager.list([ + FakeCommand( + command: [ + 'script', + '-t', + '0', + '/dev/null', + 'HostArtifact.iosDeploy', + '--id', + '123', + '--bundle', + '/', + '--debug', + '--no-wifi', + '--args', + [ + '--enable-dart-profiling', + '--enable-checked-mode', + '--verify-entry-points', + // The --trace-startup argument below is determined by what is passed into + // platformArgs argument to startApp. + '--trace-startup', + ].join(' '), + ], + environment: const { + 'PATH': '/usr/bin:null', + 'DYLD_LIBRARY_PATH': '/path/to/libraries', + }, + stdout: '(lldb) run\nsuccess', + ), + ]); + final IOSDevice device = setUpIOSDevice( + sdkVersion: '13.3', + processManager: processManager, + fileSystem: fileSystem, + ); + final IOSApp iosApp = PrebuiltIOSApp( + projectBundleId: 'app', + bundleName: 'Runner', + uncompressedBundle: fileSystem.currentDirectory, + applicationPackage: fileSystem.currentDirectory, + ); + final FakeDeviceLogReader deviceLogReader = FakeDeviceLogReader(); + + device.portForwarder = const NoOpDevicePortForwarder(); + device.setLogReader(iosApp, deviceLogReader); + + // Start writing messages to the log reader. + Timer.run(() { + deviceLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:1234'); + }); + + final LaunchResult launchResult = await device.startApp(iosApp, + prebuiltApplication: true, + debuggingOptions: DebuggingOptions.enabled( + BuildInfo.debug, + ), + platformArgs: { + 'trace-startup': true, + }, + ); + + expect(launchResult.started, true); + expect(await device.stopApp(iosApp), false); + expect(processManager, hasNoRemainingExpectations); + }); } IOSDevice setUpIOSDevice({ diff --git a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart index b82d401aad..367b7848eb 100644 --- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart @@ -1027,14 +1027,23 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' final DebuggingOptions mockOptions = DebuggingOptions.enabled( mockInfo, enableSoftwareRendering: true, + traceSystrace: true, startPaused: true, disableServiceAuthCodes: true, skiaDeterministicRendering: true, useTestFonts: true, + traceSkia: true, traceAllowlist: 'foo,bar', traceSkiaAllowlist: 'skia.a,skia.b', + endlessTraceBuffer: true, + dumpSkpOnShaderCompilation: true, + verboseSystemLogs: true, + cacheSkSL: true, + purgePersistentCache: true, dartFlags: '--baz', + nullAssertions: true, enableImpeller: true, + hostVmServicePort: 0, ); await device.startApp(package, prebuiltApplication: true, debuggingOptions: mockOptions); @@ -1043,13 +1052,20 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' '--enable-checked-mode', '--verify-entry-points', '--enable-software-rendering', + '--trace-systrace', '--start-paused', '--disable-service-auth-codes', '--skia-deterministic-rendering', '--use-test-fonts', + '--trace-skia', '--trace-allowlist="foo,bar"', '--trace-skia-allowlist="skia.a,skia.b"', - '--dart-flags=--baz', + '--endless-trace-buffer', + '--dump-skp-on-shader-compilation', + '--verbose-logging', + '--cache-sksl', + '--purge-persistent-cache', + '--dart-flags=--baz,--null_assertions', '--enable-impeller', '--observatory-port=0', ]));