[flutter tools] rewrite launch non-prebuilt app tests (#53351)
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 84d9f6e..a5fa58d 100644
--- a/packages/flutter_tools/test/general.shard/ios/devices_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/devices_test.dart
@@ -3,11 +3,12 @@
// found in the LICENSE file.
import 'dart:async';
-import 'dart:convert';
-import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
+import 'package:mockito/mockito.dart';
+import 'package:platform/platform.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';
@@ -15,26 +16,15 @@
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
-import 'package:flutter_tools/src/commands/create.dart';
import 'package:flutter_tools/src/device.dart';
-import 'package:flutter_tools/src/doctor.dart';
import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/ios/ios_deploy.dart';
import 'package:flutter_tools/src/ios/ios_workflow.dart';
import 'package:flutter_tools/src/macos/xcode.dart';
-import 'package:flutter_tools/src/project.dart';
-import 'package:flutter_tools/src/globals.dart' as globals;
-
-import 'package:meta/meta.dart';
-import 'package:mockito/mockito.dart';
-import 'package:platform/platform.dart';
-import 'package:process/process.dart';
-import 'package:quiver/testing/async.dart';
import '../../src/common.dart';
import '../../src/context.dart';
-import '../../src/fakes.dart';
import '../../src/mocks.dart';
void main() {
@@ -59,7 +49,6 @@
mockCache = MockCache();
const MapEntry<String, String> dyLdLibEntry = MapEntry<String, String>('DYLD_LIBRARY_PATH', '/path/to/libs');
when(mockCache.dyLdLibEntry).thenReturn(dyLdLibEntry);
- mockFileSystem = MockFileSystem();
logger = BufferLogger.test();
iosDeploy = IOSDeploy(
artifacts: mockArtifacts,
@@ -231,7 +220,6 @@
forwardedPort = ForwardedPort.withContext(123, 456, mockProcess3);
mockArtifacts = MockArtifacts();
mockCache = MockCache();
- mockFileSystem = MockFileSystem();
iosDeploy = IOSDeploy(
artifacts: mockArtifacts,
cache: mockCache,
@@ -268,271 +256,12 @@
verify(mockProcess3.kill());
});
});
-
- group('startApp', () {
- MockIOSApp mockApp;
- MockArtifacts mockArtifacts;
- MockCache mockCache;
- MockFileSystem mockFileSystem;
- MockPlatform mockPlatform;
- MockProcessManager mockProcessManager;
- FakeDeviceLogReader mockLogReader;
- MockPortForwarder mockPortForwarder;
- MockIMobileDevice mockIMobileDevice;
- MockIOSDeploy mockIosDeploy;
-
- Directory tempDir;
- Directory projectDir;
-
- const int devicePort = 499;
- const int hostPort = 42;
- const String installerPath = '/path/to/ideviceinstaller';
- const String iosDeployPath = '/path/to/iosdeploy';
- const String iproxyPath = '/path/to/iproxy';
- 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(() {
- Cache.disableLocking();
-
- mockApp = MockIOSApp();
- mockArtifacts = MockArtifacts();
- mockCache = MockCache();
- when(mockCache.dyLdLibEntry).thenReturn(libraryEntry);
- mockFileSystem = MockFileSystem();
- mockPlatform = MockPlatform();
- when(mockPlatform.isMacOS).thenReturn(true);
- mockProcessManager = MockProcessManager();
- mockLogReader = FakeDeviceLogReader();
- mockPortForwarder = MockPortForwarder();
- mockIMobileDevice = MockIMobileDevice();
- mockIosDeploy = MockIOSDeploy();
-
- tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_create_test.');
- projectDir = tempDir.childDirectory('flutter_project');
-
- when(
- mockArtifacts.getArtifactPath(
- Artifact.ideviceinstaller,
- platform: anyNamed('platform'),
- ),
- ).thenReturn(installerPath);
-
- when(
- mockArtifacts.getArtifactPath(
- Artifact.iosDeploy,
- platform: anyNamed('platform'),
- ),
- ).thenReturn(iosDeployPath);
-
- when(
- mockArtifacts.getArtifactPath(
- Artifact.iproxy,
- platform: anyNamed('platform'),
- ),
- ).thenReturn(iproxyPath);
-
- when(mockPortForwarder.forward(devicePort, hostPort: anyNamed('hostPort')))
- .thenAnswer((_) async => hostPort);
- when(mockPortForwarder.forwardedPorts)
- .thenReturn(<ForwardedPort>[ForwardedPort(hostPort, devicePort)]);
- when(mockPortForwarder.unforward(any))
- .thenAnswer((_) async => null);
-
- final MemoryFileSystem memoryFileSystem = MemoryFileSystem();
- when(mockFileSystem.currentDirectory)
- .thenReturn(memoryFileSystem.currentDirectory);
-
- const String bundlePath = '/path/to/bundle';
- final List<String> installArgs = <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(
- installArgs,
- workingDirectory: anyNamed('workingDirectory'),
- environment: env,
- )).thenAnswer(
- (_) => Future<ProcessResult>.value(ProcessResult(1, 0, '', ''))
- );
- });
-
- tearDown(() {
- mockLogReader.dispose();
- tryToDelete(tempDir);
-
- Cache.enableLocking();
- });
-
- void testNonPrebuilt(
- String name, {
- @required bool showBuildSettingsFlakes,
- void Function() additionalSetup,
- void Function() additionalExpectations,
- }) {
- testUsingContext('non-prebuilt succeeds in debug mode $name', () async {
- final Directory targetBuildDir =
- projectDir.childDirectory('build/ios/iphoneos/Debug-arm64');
-
- // The -showBuildSettings calls have a timeout and so go through
- // globals.processManager.start().
- mockProcessManager.processFactory = flakyProcessFactory(
- flakes: showBuildSettingsFlakes ? 1 : 0,
- delay: const Duration(seconds: 62),
- filter: (List<String> args) => args.contains('-showBuildSettings'),
- stdout:
- () => Stream<String>
- .fromIterable(
- <String>['TARGET_BUILD_DIR = ${targetBuildDir.path}\n'])
- .transform(utf8.encoder),
- );
-
- // Make all other subcommands succeed.
- when(mockProcessManager.run(
- any,
- workingDirectory: anyNamed('workingDirectory'),
- environment: anyNamed('environment'),
- )).thenAnswer((Invocation inv) {
- return Future<ProcessResult>.value(ProcessResult(0, 0, '', ''));
- });
-
- when(mockProcessManager.run(
- argThat(contains('find-identity')),
- environment: anyNamed('environment'),
- workingDirectory: anyNamed('workingDirectory'),
- )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(
- 1, // pid
- 0, // exitCode
- '''
- 1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)"
- 2) da4b9237bacccdf19c0760cab7aec4a8359010b0 "iPhone Developer: Profile 2 (2222BBBB22)"
- 3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)"
- 3 valid identities found''',
- '',
- )));
-
- // Deploy works.
- when(mockIosDeploy.runApp(
- deviceId: anyNamed('deviceId'),
- bundlePath: anyNamed('bundlePath'),
- launchArguments: anyNamed('launchArguments'),
- )).thenAnswer((_) => Future<int>.value(0));
-
- // Create a dummy project to avoid mocking out the whole directory
- // structure expected by device.startApp().
- Cache.flutterRoot = '../..';
- final CreateCommand command = CreateCommand();
- final CommandRunner<void> runner = createTestCommandRunner(command);
- await runner.run(<String>[
- 'create',
- '--no-pub',
- projectDir.path,
- ]);
-
- if (additionalSetup != null) {
- additionalSetup();
- }
-
- final IOSApp app = await AbsoluteBuildableIOSApp.fromProject(
- FlutterProject.fromDirectory(projectDir).ios);
- final IOSDevice device = IOSDevice(
- '123',
- name: 'iPhone 1',
- sdkVersion: '13.3',
- artifacts: mockArtifacts,
- fileSystem: globals.fs,
- logger: testLogger,
- platform: macPlatform,
- iosDeploy: mockIosDeploy,
- iMobileDevice: iMobileDevice,
- cpuArchitecture: DarwinArch.arm64,
- );
-
- // Pre-create the expected build products.
- targetBuildDir.createSync(recursive: true);
- projectDir.childDirectory('build/ios/iphoneos/Runner.app').createSync(recursive: true);
-
- final Completer<LaunchResult> completer = Completer<LaunchResult>();
- FakeAsync().run((FakeAsync time) {
- device.startApp(
- app,
- prebuiltApplication: false,
- debuggingOptions: DebuggingOptions.disabled(const BuildInfo(BuildMode.debug, null, treeShakeIcons: false)),
- platformArgs: <String, dynamic>{},
- ).then((LaunchResult result) {
- completer.complete(result);
- });
- time.flushMicrotasks();
- time.elapse(const Duration(seconds: 65));
- });
- final LaunchResult launchResult = await completer.future;
- expect(launchResult.started, isTrue);
- expect(launchResult.hasObservatory, isFalse);
- expect(await device.stopApp(mockApp), isFalse);
-
- if (additionalExpectations != null) {
- additionalExpectations();
- }
- }, overrides: <Type, Generator>{
- DoctorValidatorsProvider: () => FakeIosDoctorProvider(),
- IMobileDevice: () => mockIMobileDevice,
- Platform: () => macPlatform,
- ProcessManager: () => mockProcessManager,
- });
- }
-
- testNonPrebuilt('flaky: false', showBuildSettingsFlakes: false);
- testNonPrebuilt('flaky: true', showBuildSettingsFlakes: true);
- testNonPrebuilt('with concurrent build failiure',
- showBuildSettingsFlakes: false,
- additionalSetup: () {
- int callCount = 0;
- when(mockProcessManager.run(
- argThat(allOf(
- contains('xcodebuild'),
- contains('-configuration'),
- contains('Debug'),
- )),
- workingDirectory: anyNamed('workingDirectory'),
- environment: anyNamed('environment'),
- )).thenAnswer((Invocation inv) {
- // Succeed after 2 calls.
- if (++callCount > 2) {
- return Future<ProcessResult>.value(ProcessResult(0, 0, '', ''));
- }
- // Otherwise fail with the Xcode concurrent error.
- return Future<ProcessResult>.value(ProcessResult(
- 0,
- 1,
- '''
- "/Developer/Xcode/DerivedData/foo/XCBuildData/build.db":
- database is locked
- Possibly there are two concurrent builds running in the same filesystem location.
- ''',
- '',
- ));
- });
- },
- additionalExpectations: () {
- expect(testLogger.statusText, contains('will retry in 2 seconds'));
- expect(testLogger.statusText, contains('will retry in 4 seconds'));
- expect(testLogger.statusText, contains('Xcode build done.'));
- },
- );
- });
});
group('pollingGetDevices', () {
MockXcdevice mockXcdevice;
MockArtifacts mockArtifacts;
MockCache mockCache;
- MockFileSystem mockFileSystem;
FakeProcessManager fakeProcessManager;
Logger logger;
IOSDeploy iosDeploy;
@@ -544,7 +273,6 @@
mockArtifacts = MockArtifacts();
mockCache = MockCache();
logger = BufferLogger.test();
- mockFileSystem = MockFileSystem();
mockIosWorkflow = MockIOSWorkflow();
fakeProcessManager = FakeProcessManager.any();
iosDeploy = IOSDeploy(
@@ -596,7 +324,7 @@
iMobileDevice: iMobileDevice,
logger: logger,
platform: macPlatform,
- fileSystem: mockFileSystem,
+ fileSystem: MemoryFileSystem.test(),
);
when(mockXcdevice.getAvailableTetheredIOSDevices())
.thenAnswer((Invocation invocation) => Future<List<IOSDevice>>.value(<IOSDevice>[device]));
@@ -646,48 +374,10 @@
});
}
-class AbsoluteBuildableIOSApp extends BuildableIOSApp {
- AbsoluteBuildableIOSApp(IosProject project, String projectBundleId) :
- super(project, projectBundleId);
-
- static Future<AbsoluteBuildableIOSApp> fromProject(IosProject project) async {
- final String projectBundleId = await project.productBundleIdentifier;
- return AbsoluteBuildableIOSApp(project, projectBundleId);
- }
-
- @override
- String get deviceBundlePath =>
- globals.fs.path.join(project.parent.directory.path, 'build', 'ios', 'iphoneos', name);
-
-}
-
-class FakeIosDoctorProvider implements DoctorValidatorsProvider {
- List<Workflow> _workflows;
-
- @override
- List<DoctorValidator> get validators => <DoctorValidator>[];
-
- @override
- List<Workflow> get workflows {
- if (_workflows == null) {
- _workflows = <Workflow>[];
- if (globals.iosWorkflow.appliesToHostPlatform) {
- _workflows.add(globals.iosWorkflow);
- }
- }
- return _workflows;
- }
-}
-
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 MockFile extends Mock implements File {}
-class MockFileSystem extends Mock implements FileSystem {}
class MockIMobileDevice extends Mock implements IMobileDevice {}
class MockIOSDeploy extends Mock implements IOSDeploy {}
class MockIOSWorkflow extends Mock implements IOSWorkflow {}
-class MockPlatform extends Mock implements Platform {}
-class MockPortForwarder extends Mock implements DevicePortForwarder {}
class MockXcdevice extends Mock implements XCDevice {}
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
new file mode 100644
index 0000000..e82ed73
--- /dev/null
+++ b/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart
@@ -0,0 +1,300 @@
+// Copyright 2014 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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/logger.dart';
+import 'package:flutter_tools/src/build_info.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/ios_deploy.dart';
+import 'package:flutter_tools/src/ios/mac.dart';
+import 'package:flutter_tools/src/project.dart';
+import 'package:mockito/mockito.dart';
+import 'package:platform/platform.dart';
+import 'package:quiver/testing/async.dart';
+
+import '../../src/common.dart';
+import '../../src/context.dart';
+import '../../src/fake_process_manager.dart';
+
+const List<String> kRunReleaseArgs = <String>[
+ '/usr/bin/env',
+ 'xcrun',
+ 'xcodebuild',
+ '-configuration',
+ 'Release',
+ '-quiet',
+ '-workspace',
+ 'Runner.xcworkspace',
+ '-scheme',
+ 'Runner',
+ 'BUILD_DIR=/build/ios',
+ '-sdk',
+ 'iphoneos',
+ 'ONLY_ACTIVE_ARCH=YES',
+ 'ARCHS=arm64',
+ 'FLUTTER_SUPPRESS_ANALYTICS=true',
+ 'COMPILER_INDEX_STORE_ENABLE=NO',
+];
+
+const String kConcurrentBuildErrorMessage = '''
+"/Developer/Xcode/DerivedData/foo/XCBuildData/build.db":
+database is locked
+Possibly there are two concurrent builds running in the same filesystem location.
+''';
+
+final FakePlatform macPlatform = FakePlatform(
+ operatingSystem: 'macos',
+ environment: <String, String>{},
+);
+
+void main() {
+ FileSystem fileSystem;
+ FakeProcessManager processManager;
+ BufferLogger logger;
+
+ setUp(() {
+ logger = BufferLogger.test();
+ fileSystem = MemoryFileSystem.test();
+ processManager = FakeProcessManager.list(<FakeCommand>[]);
+ });
+
+ testUsingContext('IOSDevice.startApp succeeds in release mode with buildable app', () async {
+ final IOSDevice iosDevice = setUpIOSDevice(
+ fileSystem: fileSystem,
+ processManager: processManager,
+ logger: logger,
+ );
+ setUpIOSProject(fileSystem);
+ final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
+ final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter');
+
+ processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
+ processManager.addCommand(const FakeCommand(command: <String>[...kRunReleaseArgs, '-showBuildSettings']));
+ processManager.addCommand(FakeCommand(
+ command: <String>[
+ 'ios-deploy',
+ '--id',
+ '123',
+ '--bundle',
+ 'build/ios/iphoneos/Runner.app',
+ '--no-wifi',
+ '--justlaunch',
+ '--args',
+ const <String>[
+ '--enable-dart-profiling',
+ '--enable-service-port-fallback',
+ '--disable-service-auth-codes',
+ '--observatory-port=53781',
+ ].join(' ')
+ ])
+ );
+
+ final LaunchResult launchResult = await iosDevice.startApp(
+ buildableIOSApp,
+ debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
+ platformArgs: <String, Object>{},
+ );
+
+ expect(launchResult.started, true);
+ expect(processManager.hasRemainingExpectations, false);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => processManager,
+ FileSystem: () => fileSystem,
+ Logger: () => logger,
+ Platform: () => macPlatform,
+ });
+
+ testUsingContext('IOSDevice.startApp succeeds in release mode with buildable '
+ 'app with flaky buildSettings call', () async {
+ LaunchResult launchResult;
+ FakeAsync().run((FakeAsync time) {
+ final IOSDevice iosDevice = setUpIOSDevice(
+ fileSystem: fileSystem,
+ processManager: processManager,
+ logger: logger,
+ );
+ setUpIOSProject(fileSystem);
+ final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
+ final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter');
+
+ processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
+ // The first showBuildSettings call should timeout.
+ processManager.addCommand(
+ const FakeCommand(
+ command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
+ duration: Duration(minutes: 5), // this is longer than the timeout of 1 minute.
+ ));
+ // The second call succeedes and is made after the first times out.
+ processManager.addCommand(
+ const FakeCommand(
+ command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
+ exitCode: 0,
+ ));
+ processManager.addCommand(FakeCommand(
+ command: <String>[
+ 'ios-deploy',
+ '--id',
+ '123',
+ '--bundle',
+ 'build/ios/iphoneos/Runner.app',
+ '--no-wifi',
+ '--justlaunch',
+ '--args',
+ const <String>[
+ '--enable-dart-profiling',
+ '--enable-service-port-fallback',
+ '--disable-service-auth-codes',
+ '--observatory-port=53781',
+ ].join(' ')
+ ])
+ );
+
+ iosDevice.startApp(
+ buildableIOSApp,
+ debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
+ platformArgs: <String, Object>{},
+ ).then((LaunchResult result) {
+ launchResult = result;
+ });
+
+ // Elapse duration for process timeout.
+ time.flushMicrotasks();
+ time.elapse(const Duration(minutes: 1));
+
+ // Elapse duration for overall process timer.
+ time.flushMicrotasks();
+ time.elapse(const Duration(minutes: 5));
+
+ time.flushTimers();
+ });
+
+ expect(launchResult?.started, true);
+ expect(processManager.hasRemainingExpectations, false);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => processManager,
+ FileSystem: () => fileSystem,
+ Logger: () => logger,
+ Platform: () => macPlatform,
+ });
+
+ testUsingContext('IOSDevice.startApp succeeds in release mode with buildable '
+ 'app with concurrent build failure', () async {
+ final IOSDevice iosDevice = setUpIOSDevice(
+ fileSystem: fileSystem,
+ processManager: processManager,
+ logger: logger,
+ );
+ setUpIOSProject(fileSystem);
+ final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
+ final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter');
+
+ // The first xcrun call should fail with a
+ // concurrent build exception.
+ processManager.addCommand(
+ const FakeCommand(
+ command: kRunReleaseArgs,
+ exitCode: 1,
+ stdout: kConcurrentBuildErrorMessage,
+ ));
+ processManager.addCommand(const FakeCommand(command: kRunReleaseArgs));
+ processManager.addCommand(
+ const FakeCommand(
+ command: <String>[...kRunReleaseArgs, '-showBuildSettings'],
+ exitCode: 0,
+ ));
+ processManager.addCommand(FakeCommand(
+ command: <String>[
+ 'ios-deploy',
+ '--id',
+ '123',
+ '--bundle',
+ 'build/ios/iphoneos/Runner.app',
+ '--no-wifi',
+ '--justlaunch',
+ '--args',
+ const <String>[
+ '--enable-dart-profiling',
+ '--enable-service-port-fallback',
+ '--disable-service-auth-codes',
+ '--observatory-port=53781',
+ ].join(' ')
+ ])
+ );
+
+ final LaunchResult launchResult = await iosDevice.startApp(
+ buildableIOSApp,
+ debuggingOptions: DebuggingOptions.disabled(BuildInfo.release),
+ platformArgs: <String, Object>{},
+ );
+
+ expect(logger.statusText,
+ contains('Xcode build failed due to concurrent builds, will retry in 2 seconds'));
+ expect(launchResult.started, true);
+ expect(processManager.hasRemainingExpectations, false);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => processManager,
+ FileSystem: () => fileSystem,
+ Logger: () => logger,
+ Platform: () => macPlatform,
+ });
+}
+
+void setUpIOSProject(FileSystem fileSystem) {
+ fileSystem.file('pubspec.yaml').createSync();
+ fileSystem.file('.packages').writeAsStringSync('\n');
+ fileSystem.directory('ios').createSync();
+ fileSystem.directory('ios/Runner.xcworkspace').createSync();
+ fileSystem.directory('ios/Runner.xcodeproj').createSync();
+ fileSystem.file('ios/Runner.xcodeproj/project.pbxproj').createSync();
+ // This is the expected output directory.
+ fileSystem.directory('build/ios/iphoneos/Runner.app').createSync(recursive: true);
+}
+
+IOSDevice setUpIOSDevice({
+ String sdkVersion = '13.0.1',
+ FileSystem fileSystem,
+ Logger logger,
+ ProcessManager processManager,
+}) {
+ const MapEntry<String, String> dyldLibraryEntry = MapEntry<String, String>(
+ 'DYLD_LIBRARY_PATH',
+ '/path/to/libraries',
+ );
+ final MockCache cache = MockCache();
+ final MockArtifacts artifacts = MockArtifacts();
+ logger ??= BufferLogger.test();
+ when(cache.dyLdLibEntry).thenReturn(dyldLibraryEntry);
+ when(artifacts.getArtifactPath(Artifact.iosDeploy, platform: anyNamed('platform')))
+ .thenReturn('ios-deploy');
+ return IOSDevice('123',
+ name: 'iPhone 1',
+ sdkVersion: sdkVersion,
+ fileSystem: fileSystem ?? MemoryFileSystem.test(),
+ platform: macPlatform,
+ artifacts: artifacts,
+ logger: logger,
+ iosDeploy: IOSDeploy(
+ logger: logger,
+ platform: macPlatform,
+ processManager: processManager ?? FakeProcessManager.any(),
+ artifacts: artifacts,
+ cache: cache,
+ ),
+ iMobileDevice: IMobileDevice(
+ logger: logger,
+ processManager: processManager ?? FakeProcessManager.any(),
+ artifacts: artifacts,
+ cache: cache,
+ ),
+ cpuArchitecture: DarwinArch.arm64,
+ );
+}
+
+class MockArtifacts extends Mock implements Artifacts {}
+class MockCache extends Mock implements Cache {}