diff --git a/dev/devicelab/bin/tasks/hot_mode_dev_cycle_ios_simulator.dart b/dev/devicelab/bin/tasks/hot_mode_dev_cycle_ios_simulator.dart index d8f178fa38..d41e414f62 100644 --- a/dev/devicelab/bin/tasks/hot_mode_dev_cycle_ios_simulator.dart +++ b/dev/devicelab/bin/tasks/hot_mode_dev_cycle_ios_simulator.dart @@ -16,7 +16,7 @@ Future main() async { await testWithNewIOSSimulator('TestHotReloadSim', (String deviceId) async { simulatorDeviceId = deviceId; // This isn't actually a benchmark test, so do not use the returned `benchmarkScoreKeys` result. - await createHotModeTest(deviceIdOverride: deviceId)(); + await createHotModeTest(deviceIdOverride: deviceId, checkAppRunningOnLocalDevice: true)(); }); } finally { await removeIOSimulator(simulatorDeviceId); diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index f789f5a02e..3366b9dbb8 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart @@ -257,6 +257,25 @@ class SimControl { return result; } + Future stopApp(String deviceId, String appIdentifier) async { + RunResult result; + try { + result = await _processUtils.run( + [ + ..._xcode.xcrunCommand(), + 'simctl', + 'terminate', + deviceId, + appIdentifier, + ], + throwOnError: true, + ); + } on ProcessException catch (exception) { + throwToolExit('Unable to terminate $appIdentifier on $deviceId:\n$exception'); + } + return result; + } + Future takeScreenshot(String deviceId, String outputPath) async { try { await _processUtils.run( @@ -533,11 +552,13 @@ class IOSSimulator extends Device { @override Future stopApp( - ApplicationPackage app, { + ApplicationPackage? app, { String? userIdentifier, }) async { - // Currently we don't have a way to stop an app running on iOS. - return false; + if (app == null) { + return false; + } + return (await _simControl.stopApp(id, app.id)).exitCode == 0; } String get logFilePath { 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 45f42816d8..b82d401aad 100644 --- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart @@ -901,6 +901,36 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' throwsToolExit(message: r'Unable to launch'), ); }); + + testWithoutContext('.stopApp() handles exceptions', () async { + fakeProcessManager.addCommand(const FakeCommand( + command: [ + 'xcrun', + 'simctl', + 'terminate', + deviceId, + appId, + ], + exception: ProcessException('xcrun', []), + )); + + expect( + () async => simControl.stopApp(deviceId, appId), + throwsToolExit(message: 'Unable to terminate'), + ); + expect(fakeProcessManager, hasNoRemainingExpectations); + }); + + testWithoutContext('simulator stopApp handles null app package', () async { + final IOSSimulator iosSimulator = IOSSimulator( + 'x', + name: 'Testo', + simulatorCategory: 'NaN', + simControl: simControl, + ); + + expect(await iosSimulator.stopApp(null), isFalse); + }); }); group('startApp', () {