Fix invocations of ideviceinstaller not passing DYLD_LIBRARY_PATH (#36327) * add failing tests * fix tests * be more specific with try-catch * add further mocking to get tests to pass again * fix analyzer failure
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart index 33e520d..4ed40de 100644 --- a/packages/flutter_tools/lib/src/ios/devices.dart +++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -191,15 +191,18 @@ @override Future<bool> isAppInstalled(ApplicationPackage app) async { + RunResult apps; try { - final RunResult apps = await runCheckedAsync(<String>[_installerPath, '--list-apps']); - if (RegExp(app.id, multiLine: true).hasMatch(apps.stdout)) { - return true; - } - } catch (e) { + apps = await runCheckedAsync( + <String>[_installerPath, '--list-apps'], + environment: Map<String, String>.fromEntries( + <MapEntry<String, String>>[cache.dyLdLibEntry], + ), + ); + } on ProcessException { return false; } - return false; + return RegExp(app.id, multiLine: true).hasMatch(apps.stdout); } @override @@ -215,9 +218,14 @@ } try { - await runCheckedAsync(<String>[_installerPath, '-i', iosApp.deviceBundlePath]); + await runCheckedAsync( + <String>[_installerPath, '-i', iosApp.deviceBundlePath], + environment: Map<String, String>.fromEntries( + <MapEntry<String, String>>[cache.dyLdLibEntry], + ), + ); return true; - } catch (e) { + } on ProcessException { return false; } } @@ -225,9 +233,14 @@ @override Future<bool> uninstallApp(ApplicationPackage app) async { try { - await runCheckedAsync(<String>[_installerPath, '-U', app.id]); + await runCheckedAsync( + <String>[_installerPath, '-U', app.id], + environment: Map<String, String>.fromEntries( + <MapEntry<String, String>>[cache.dyLdLibEntry], + ), + ); return true; - } catch (e) { + } on ProcessException { return false; } }
diff --git a/packages/flutter_tools/test/general.shard/ios/devices_test.dart b/packages/flutter_tools/test/general.shard/ios/devices_test.dart index 0716212..f01894e 100644 --- a/packages/flutter_tools/test/general.shard/ios/devices_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/devices_test.dart
@@ -7,8 +7,10 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/application_package.dart'; +import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/mac.dart'; @@ -22,6 +24,11 @@ import '../../src/context.dart'; import '../../src/mocks.dart'; +class MockIOSApp extends Mock implements IOSApp {} +class MockArtifacts extends Mock implements Artifacts {} +class MockCache extends Mock implements Cache {} +class MockDirectory extends Mock implements Directory {} +class MockFileSystem extends Mock implements FileSystem {} class MockIMobileDevice extends Mock implements IMobileDevice {} class MockProcessManager extends Mock implements ProcessManager {} class MockXcode extends Mock implements Xcode {} @@ -29,8 +36,96 @@ class MockProcess extends Mock implements Process {} void main() { - final FakePlatform osx = FakePlatform.fromPlatform(const LocalPlatform()); - osx.operatingSystem = 'macos'; + final FakePlatform macPlatform = FakePlatform.fromPlatform(const LocalPlatform()); + macPlatform.operatingSystem = 'macos'; + + group('Process calls', () { + MockIOSApp mockApp; + MockArtifacts mockArtifacts; + MockCache mockCache; + MockFileSystem mockFileSystem; + MockProcessManager mockProcessManager; + const String installerPath = '/path/to/ideviceinstaller'; + const String appId = '789'; + const MapEntry<String, String> libraryEntry = MapEntry<String, String>( + 'DYLD_LIBRARY_PATH', + '/path/to/libraries' + ); + final Map<String, String> env = Map<String, String>.fromEntries( + <MapEntry<String, String>>[libraryEntry] + ); + + setUp(() { + mockApp = MockIOSApp(); + mockArtifacts = MockArtifacts(); + mockCache = MockCache(); + when(mockCache.dyLdLibEntry).thenReturn(libraryEntry); + mockFileSystem = MockFileSystem(); + mockProcessManager = MockProcessManager(); + when( + mockArtifacts.getArtifactPath( + Artifact.ideviceinstaller, + platform: anyNamed('platform'), + ) + ).thenReturn(installerPath); + }); + + testUsingContext('installApp() invokes process with correct environment', () async { + final IOSDevice device = IOSDevice('123'); + const String bundlePath = '/path/to/bundle'; + final List<String> args = <String>[installerPath, '-i', bundlePath]; + when(mockApp.deviceBundlePath).thenReturn(bundlePath); + final MockDirectory directory = MockDirectory(); + when(mockFileSystem.directory(bundlePath)).thenReturn(directory); + when(directory.existsSync()).thenReturn(true); + when(mockProcessManager.run(args, environment: env)) + .thenAnswer( + (_) => Future<ProcessResult>.value(ProcessResult(1, 0, '', '')) + ); + await device.installApp(mockApp); + verify(mockProcessManager.run(args, environment: env)); + }, overrides: <Type, Generator>{ + Artifacts: () => mockArtifacts, + Cache: () => mockCache, + FileSystem: () => mockFileSystem, + Platform: () => macPlatform, + ProcessManager: () => mockProcessManager, + }); + + testUsingContext('isAppInstalled() invokes process with correct environment', () async { + final IOSDevice device = IOSDevice('123'); + final List<String> args = <String>[installerPath, '--list-apps']; + when(mockProcessManager.run(args, environment: env)) + .thenAnswer( + (_) => Future<ProcessResult>.value(ProcessResult(1, 0, '', '')) + ); + when(mockApp.id).thenReturn(appId); + await device.isAppInstalled(mockApp); + verify(mockProcessManager.run(args, environment: env)); + }, overrides: <Type, Generator>{ + Artifacts: () => mockArtifacts, + Cache: () => mockCache, + Platform: () => macPlatform, + ProcessManager: () => mockProcessManager, + }); + + testUsingContext('uninstallApp() invokes process with correct environment', () async { + final IOSDevice device = IOSDevice('123'); + final List<String> args = <String>[installerPath, '-U', appId]; + when(mockApp.id).thenReturn(appId); + when(mockProcessManager.run(args, environment: env)) + .thenAnswer( + (_) => Future<ProcessResult>.value(ProcessResult(1, 0, '', '')) + ); + await device.uninstallApp(mockApp); + verify(mockProcessManager.run(args, environment: env)); + }, overrides: <Type, Generator>{ + Artifacts: () => mockArtifacts, + Cache: () => mockCache, + Platform: () => macPlatform, + ProcessManager: () => mockProcessManager, + }); + }); group('getAttachedDevices', () { MockIMobileDevice mockIMobileDevice;