| // 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 'dart:async'; | |
| import 'package:file/memory.dart'; | |
| import 'package:file_testing/file_testing.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/base/process.dart'; | |
| import 'package:flutter_tools/src/base/version.dart'; | |
| import 'package:flutter_tools/src/build_info.dart'; | |
| import 'package:flutter_tools/src/build_system/build_system.dart'; | |
| import 'package:flutter_tools/src/cache.dart'; | |
| import 'package:flutter_tools/src/features.dart'; | |
| import 'package:flutter_tools/src/flutter_manifest.dart'; | |
| import 'package:flutter_tools/src/ios/xcodeproj.dart'; | |
| import 'package:flutter_tools/src/project.dart'; | |
| import 'package:test/fake.dart'; | |
| import '../src/common.dart'; | |
| import '../src/context.dart'; | |
| import '../src/fake_process_manager.dart'; | |
| import '../src/fakes.dart'; | |
| void main() { | |
| group('IosProject', () { | |
| testWithoutContext('managedDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect(project.managedDirectory.path, 'app_name/ios/Flutter'); | |
| }); | |
| testWithoutContext('module managedDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs, isModule: true)); | |
| expect(project.managedDirectory.path, 'app_name/.ios/Flutter'); | |
| }); | |
| testWithoutContext('ephemeralDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect(project.ephemeralDirectory.path, 'app_name/ios/Flutter/ephemeral'); | |
| }); | |
| testWithoutContext('module ephemeralDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs, isModule: true)); | |
| expect(project.ephemeralDirectory.path, 'app_name/.ios/Flutter/ephemeral'); | |
| }); | |
| testWithoutContext('flutterSwiftPackagesDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect(project.flutterSwiftPackagesDirectory.path, 'app_name/ios/Flutter/ephemeral/Packages'); | |
| }); | |
| testWithoutContext('relativeSwiftPackagesDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect( | |
| project.relativeSwiftPackagesDirectory.path, | |
| 'app_name/ios/Flutter/ephemeral/Packages/.packages', | |
| ); | |
| }); | |
| testWithoutContext('flutterFrameworkSwiftPackageDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect( | |
| project.flutterFrameworkSwiftPackageDirectory.path, | |
| 'app_name/ios/Flutter/ephemeral/Packages/.packages/FlutterFramework', | |
| ); | |
| }); | |
| testWithoutContext('flutterPluginSwiftPackageDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect( | |
| project.flutterPluginSwiftPackageDirectory.path, | |
| 'app_name/ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage', | |
| ); | |
| }); | |
| testWithoutContext('module flutterPluginSwiftPackageDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs, isModule: true)); | |
| expect( | |
| project.flutterPluginSwiftPackageDirectory.path, | |
| 'app_name/.ios/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage', | |
| ); | |
| }); | |
| testWithoutContext('xcodeConfigFor', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect(project.xcodeConfigFor('Debug').path, 'app_name/ios/Flutter/Debug.xcconfig'); | |
| }); | |
| testWithoutContext('lldbInitFile', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect(project.lldbInitFile.path, 'app_name/ios/Flutter/ephemeral/flutter_lldbinit'); | |
| }); | |
| testWithoutContext('lldbHelperPythonFile', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect( | |
| project.lldbHelperPythonFile.path, | |
| 'app_name/ios/Flutter/ephemeral/flutter_lldb_helper.py', | |
| ); | |
| }); | |
| group('projectInfo', () { | |
| testUsingContext('is null if XcodeProjectInterpreter is null', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| project.xcodeProject.createSync(recursive: true); | |
| expect(await project.projectInfo(), isNull); | |
| }, overrides: <Type, Generator>{XcodeProjectInterpreter: () => null}); | |
| testUsingContext( | |
| 'is null if XcodeProjectInterpreter is not installed', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| project.xcodeProject.createSync(recursive: true); | |
| expect(await project.projectInfo(), isNull); | |
| }, | |
| overrides: <Type, Generator>{ | |
| XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(isInstalled: false), | |
| }, | |
| ); | |
| testUsingContext( | |
| 'is null if xcodeproj does not exist', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect(await project.projectInfo(), isNull); | |
| }, | |
| overrides: <Type, Generator>{XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter()}, | |
| ); | |
| testUsingContext( | |
| 'returns XcodeProjectInfo', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| project.xcodeProject.createSync(recursive: true); | |
| expect(await project.projectInfo(), isNotNull); | |
| }, | |
| overrides: <Type, Generator>{XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter()}, | |
| ); | |
| }); | |
| testUsingContext('schemeForBuildInfo succeeds', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| project.xcodeProject.createSync(recursive: true); | |
| const BuildInfo buildInfo = BuildInfo.debug; | |
| expect(await project.schemeForBuildInfo(buildInfo), 'Runner'); | |
| }, overrides: <Type, Generator>{XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter()}); | |
| testUsingContext('schemeForBuildInfo returns null if unable to find project', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| const BuildInfo buildInfo = BuildInfo.debug; | |
| expect(await project.schemeForBuildInfo(buildInfo), isNull); | |
| }, overrides: <Type, Generator>{XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter()}); | |
| testUsingContext( | |
| 'schemeForBuildInfo succeeds with flavor', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| project.xcodeProject.createSync(recursive: true); | |
| const buildInfo = BuildInfo( | |
| BuildMode.debug, | |
| 'my_flavor', | |
| treeShakeIcons: true, | |
| packageConfigPath: '', | |
| ); | |
| expect(await project.schemeForBuildInfo(buildInfo), 'my_flavor'); | |
| }, | |
| overrides: <Type, Generator>{ | |
| XcodeProjectInterpreter: () => | |
| FakeXcodeProjectInterpreter(schemes: ['Runner', 'my_flavor']), | |
| }, | |
| ); | |
| testUsingContext( | |
| 'schemeForBuildInfo throws error if flavor is not found', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| project.xcodeProject.createSync(recursive: true); | |
| const buildInfo = BuildInfo( | |
| BuildMode.debug, | |
| 'invalid_flavor', | |
| treeShakeIcons: true, | |
| packageConfigPath: '', | |
| ); | |
| await expectLater(project.schemeForBuildInfo(buildInfo), throwsToolExit()); | |
| }, | |
| overrides: <Type, Generator>{ | |
| XcodeProjectInterpreter: () => | |
| FakeXcodeProjectInterpreter(schemes: ['Runner', 'my_flavor']), | |
| }, | |
| ); | |
| group('usesSwiftPackageManager', () { | |
| testUsingContext( | |
| 'is true when iOS project exists', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final Directory projectDirectory = fs.directory('path'); | |
| projectDirectory.childDirectory('ios').createSync(recursive: true); | |
| final FlutterManifest manifest = FakeFlutterManifest(); | |
| final project = FlutterProject(projectDirectory, manifest, manifest); | |
| expect(project.ios.usesSwiftPackageManager, isTrue); | |
| }, | |
| overrides: <Type, Generator>{ | |
| FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), | |
| XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), | |
| }, | |
| ); | |
| testUsingContext( | |
| "is false when iOS project doesn't exist", | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final Directory projectDirectory = fs.directory('path'); | |
| final FlutterManifest manifest = FakeFlutterManifest(); | |
| final project = FlutterProject(projectDirectory, manifest, manifest); | |
| expect(project.ios.usesSwiftPackageManager, isFalse); | |
| }, | |
| overrides: <Type, Generator>{ | |
| FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), | |
| XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), | |
| }, | |
| ); | |
| testUsingContext( | |
| 'is false when Swift Package Manager feature is not enabled', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final Directory projectDirectory = fs.directory('path'); | |
| projectDirectory.childDirectory('ios').createSync(recursive: true); | |
| final FlutterManifest manifest = FakeFlutterManifest(); | |
| final project = FlutterProject(projectDirectory, manifest, manifest); | |
| expect(project.ios.usesSwiftPackageManager, isFalse); | |
| }, | |
| overrides: <Type, Generator>{ | |
| FeatureFlags: () => TestFeatureFlags(), | |
| XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), | |
| }, | |
| ); | |
| testUsingContext( | |
| 'is false when project is a module', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final Directory projectDirectory = fs.directory('path'); | |
| projectDirectory.childDirectory('ios').createSync(recursive: true); | |
| final FlutterManifest manifest = FakeFlutterManifest(isModule: true); | |
| final project = FlutterProject(projectDirectory, manifest, manifest); | |
| expect(project.ios.usesSwiftPackageManager, isFalse); | |
| }, | |
| overrides: <Type, Generator>{ | |
| FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), | |
| XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), | |
| }, | |
| ); | |
| }); | |
| group('parseFlavorFromConfiguration', () { | |
| testWithoutContext('from FLAVOR when CONFIGURATION is null', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| final env = Environment.test( | |
| fs.currentDirectory, | |
| fileSystem: fs, | |
| logger: BufferLogger.test(), | |
| artifacts: Artifacts.test(), | |
| processManager: FakeProcessManager.any(), | |
| defines: <String, String>{kFlavor: 'strawberry'}, | |
| ); | |
| expect(await project.parseFlavorFromConfiguration(env), 'strawberry'); | |
| }); | |
| testWithoutContext('from FLAVOR when CONFIGURATION is does not contain delimiter', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| final env = Environment.test( | |
| fs.currentDirectory, | |
| fileSystem: fs, | |
| logger: BufferLogger.test(), | |
| artifacts: Artifacts.test(), | |
| processManager: FakeProcessManager.any(), | |
| defines: <String, String>{kFlavor: 'strawberry', kXcodeConfiguration: 'Debug'}, | |
| ); | |
| expect(await project.parseFlavorFromConfiguration(env), 'strawberry'); | |
| }); | |
| testUsingContext( | |
| 'from CONFIGURATION when has flavor following a hyphen that matches a scheme', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| final env = Environment.test( | |
| fs.currentDirectory, | |
| fileSystem: fs, | |
| logger: BufferLogger.test(), | |
| artifacts: Artifacts.test(), | |
| processManager: FakeProcessManager.any(), | |
| defines: <String, String>{kFlavor: 'strawberry', kXcodeConfiguration: 'Debug-vanilla'}, | |
| ); | |
| project.xcodeProject.createSync(recursive: true); | |
| expect(await project.parseFlavorFromConfiguration(env), 'vanilla'); | |
| }, | |
| overrides: <Type, Generator>{ | |
| XcodeProjectInterpreter: () => | |
| FakeXcodeProjectInterpreter(schemes: <String>['Runner', 'vanilla']), | |
| }, | |
| ); | |
| testUsingContext( | |
| 'from CONFIGURATION when has flavor following a space that matches a scheme', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| final env = Environment.test( | |
| fs.currentDirectory, | |
| fileSystem: fs, | |
| logger: BufferLogger.test(), | |
| artifacts: Artifacts.test(), | |
| processManager: FakeProcessManager.any(), | |
| defines: <String, String>{kFlavor: 'strawberry', kXcodeConfiguration: 'Debug vanilla'}, | |
| ); | |
| project.xcodeProject.createSync(recursive: true); | |
| expect(await project.parseFlavorFromConfiguration(env), 'vanilla'); | |
| }, | |
| overrides: <Type, Generator>{ | |
| XcodeProjectInterpreter: () => | |
| FakeXcodeProjectInterpreter(schemes: <String>['Runner', 'vanilla']), | |
| }, | |
| ); | |
| testUsingContext( | |
| 'from FLAVOR when CONFIGURATION does not match a scheme', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final project = IosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| final env = Environment.test( | |
| fs.currentDirectory, | |
| fileSystem: fs, | |
| logger: BufferLogger.test(), | |
| artifacts: Artifacts.test(), | |
| processManager: FakeProcessManager.any(), | |
| defines: <String, String>{kFlavor: 'strawberry', kXcodeConfiguration: 'Debug-random'}, | |
| ); | |
| project.xcodeProject.createSync(recursive: true); | |
| expect(await project.parseFlavorFromConfiguration(env), 'strawberry'); | |
| }, | |
| overrides: <Type, Generator>{ | |
| XcodeProjectInterpreter: () => | |
| FakeXcodeProjectInterpreter(schemes: <String>['Runner', 'vanilla']), | |
| }, | |
| ); | |
| }); | |
| group('ensureReadyForPlatformSpecificTooling', () { | |
| group('lldb files are generated', () { | |
| testUsingContext('when they are missing', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final Directory projectDirectory = fs.directory('path'); | |
| projectDirectory.childDirectory('ios').createSync(recursive: true); | |
| final FlutterManifest manifest = FakeFlutterManifest(); | |
| final flutterProject = FlutterProject(projectDirectory, manifest, manifest); | |
| final project = IosProject.fromFlutter(flutterProject); | |
| expect(project.lldbInitFile, isNot(exists)); | |
| expect(project.lldbHelperPythonFile, isNot(exists)); | |
| await project.ensureReadyForPlatformSpecificTooling(); | |
| expect(project.lldbInitFile, exists); | |
| expect(project.lldbHelperPythonFile, exists); | |
| }, overrides: <Type, Generator>{Cache: () => FakeCache(olderThanToolsStamp: true)}); | |
| testUsingContext('when they are older than tool', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final Directory projectDirectory = fs.directory('path'); | |
| projectDirectory.childDirectory('ios').createSync(recursive: true); | |
| final FlutterManifest manifest = FakeFlutterManifest(); | |
| final flutterProject = FlutterProject(projectDirectory, manifest, manifest); | |
| final project = IosProject.fromFlutter(flutterProject); | |
| project.lldbInitFile.createSync(recursive: true); | |
| project.lldbInitFile.writeAsStringSync('old'); | |
| project.lldbHelperPythonFile.createSync(recursive: true); | |
| project.lldbHelperPythonFile.writeAsStringSync('old'); | |
| await project.ensureReadyForPlatformSpecificTooling(); | |
| expect(project.lldbInitFile.readAsStringSync(), contains('Generated file, do not edit.')); | |
| expect( | |
| project.lldbHelperPythonFile.readAsStringSync(), | |
| contains('Generated file, do not edit.'), | |
| ); | |
| }, overrides: <Type, Generator>{Cache: () => FakeCache(olderThanToolsStamp: true)}); | |
| }); | |
| }); | |
| }); | |
| group('MacOSProject', () { | |
| testWithoutContext('managedDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = MacOSProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect(project.managedDirectory.path, 'app_name/macos/Flutter'); | |
| }); | |
| testWithoutContext('module managedDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = MacOSProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect(project.managedDirectory.path, 'app_name/macos/Flutter'); | |
| }); | |
| testWithoutContext('ephemeralDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = MacOSProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect(project.ephemeralDirectory.path, 'app_name/macos/Flutter/ephemeral'); | |
| }); | |
| testWithoutContext('flutterSwiftPackagesDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = MacOSProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect( | |
| project.flutterSwiftPackagesDirectory.path, | |
| 'app_name/macos/Flutter/ephemeral/Packages', | |
| ); | |
| }); | |
| testWithoutContext('relativeSwiftPackagesDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = MacOSProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect( | |
| project.relativeSwiftPackagesDirectory.path, | |
| 'app_name/macos/Flutter/ephemeral/Packages/.packages', | |
| ); | |
| }); | |
| testWithoutContext('flutterPluginSwiftPackageDirectory', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = MacOSProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect( | |
| project.flutterPluginSwiftPackageDirectory.path, | |
| 'app_name/macos/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage', | |
| ); | |
| }); | |
| testWithoutContext('xcodeConfigFor', () { | |
| final fs = MemoryFileSystem.test(); | |
| final project = MacOSProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| expect(project.xcodeConfigFor('Debug').path, 'app_name/macos/Flutter/Flutter-Debug.xcconfig'); | |
| }); | |
| group('usesSwiftPackageManager', () { | |
| testUsingContext( | |
| 'is true when macOS project exists', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final Directory projectDirectory = fs.directory('path'); | |
| projectDirectory.childDirectory('macos').createSync(recursive: true); | |
| final FlutterManifest manifest = FakeFlutterManifest(); | |
| final project = FlutterProject(projectDirectory, manifest, manifest); | |
| expect(project.macos.usesSwiftPackageManager, isTrue); | |
| }, | |
| overrides: <Type, Generator>{ | |
| FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), | |
| XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), | |
| }, | |
| ); | |
| testUsingContext( | |
| "is false when macOS project doesn't exist", | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final Directory projectDirectory = fs.directory('path'); | |
| final FlutterManifest manifest = FakeFlutterManifest(); | |
| final project = FlutterProject(projectDirectory, manifest, manifest); | |
| expect(project.ios.usesSwiftPackageManager, isFalse); | |
| expect(project.macos.usesSwiftPackageManager, isFalse); | |
| }, | |
| overrides: <Type, Generator>{ | |
| FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), | |
| XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), | |
| }, | |
| ); | |
| testUsingContext( | |
| 'is false when Swift Package Manager feature is not enabled', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final Directory projectDirectory = fs.directory('path'); | |
| projectDirectory.childDirectory('macos').createSync(recursive: true); | |
| final FlutterManifest manifest = FakeFlutterManifest(); | |
| final project = FlutterProject(projectDirectory, manifest, manifest); | |
| expect(project.macos.usesSwiftPackageManager, isFalse); | |
| }, | |
| overrides: <Type, Generator>{ | |
| FeatureFlags: () => TestFeatureFlags(), | |
| XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), | |
| }, | |
| ); | |
| testUsingContext( | |
| 'is false when project is a module', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final Directory projectDirectory = fs.directory('path'); | |
| projectDirectory.childDirectory('macos').createSync(recursive: true); | |
| final FlutterManifest manifest = FakeFlutterManifest(isModule: true); | |
| final project = FlutterProject(projectDirectory, manifest, manifest); | |
| expect(project.macos.usesSwiftPackageManager, isFalse); | |
| }, | |
| overrides: <Type, Generator>{ | |
| FeatureFlags: () => TestFeatureFlags(isSwiftPackageManagerEnabled: true), | |
| XcodeProjectInterpreter: () => FakeXcodeProjectInterpreter(version: Version(15, 0, 0)), | |
| }, | |
| ); | |
| }); | |
| group('prefetchSwiftPackages', () { | |
| testWithoutContext('returns early if usesSwiftPackageManager is false', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final testLogger = BufferLogger.test(); | |
| final fakeProcessManager = FakeProcessManager.empty(); | |
| final processUtils = ProcessUtils(logger: testLogger, processManager: fakeProcessManager); | |
| final iosProject = FakeIosProjectWithCustomFlags.fromFlutter( | |
| FakeFlutterProject(fileSystem: fs), | |
| usesSwiftPackageManager: false, | |
| ); | |
| await iosProject.prefetchSwiftPackages( | |
| xcodebuildProjectCommandArguments: <String>[], | |
| processUtils: processUtils, | |
| logger: testLogger, | |
| ); | |
| expect(fakeProcessManager, hasNoRemainingExpectations); | |
| }); | |
| testWithoutContext( | |
| 'returns early if flutterPluginSwiftPackageInProjectSettings is false', | |
| () async { | |
| final fs = MemoryFileSystem.test(); | |
| final testLogger = BufferLogger.test(); | |
| final fakeProcessManager = FakeProcessManager.empty(); | |
| final processUtils = ProcessUtils(logger: testLogger, processManager: fakeProcessManager); | |
| final iosProject = FakeIosProjectWithCustomFlags.fromFlutter( | |
| FakeFlutterProject(fileSystem: fs), | |
| flutterPluginSwiftPackageInProjectSettings: false, | |
| ); | |
| await iosProject.prefetchSwiftPackages( | |
| xcodebuildProjectCommandArguments: <String>[], | |
| processUtils: processUtils, | |
| logger: testLogger, | |
| ); | |
| expect(fakeProcessManager, hasNoRemainingExpectations); | |
| }, | |
| ); | |
| testWithoutContext('starts the process and resolves packages successfully', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final testLogger = BufferLogger.test(); | |
| const projectPath = 'path/to/project'; | |
| final Directory buildDirectory = fs.directory('$projectPath/build/ios'); | |
| final fakeProcessManager = FakeProcessManager.empty(); | |
| fakeProcessManager.addCommands(<FakeCommand>[ | |
| FakeCommand( | |
| command: <String>[ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${buildDirectory.path}/SourcePackages', | |
| '-resolvePackageDependencies', | |
| ], | |
| stdout: ''' | |
| Resolve Package Graph | |
| Fetching from https://github.com/apple/swift-algorithms.git (cached) | |
| Fetching from https://github.com/apple/swift-numerics.git (cached) | |
| Creating working copy of package ‘swift-numerics’ | |
| Creating working copy of package ‘swift-algorithms’ | |
| Checking out 1.2.2 of package ‘swift-algorithms’ | |
| Checking out 1.1.1 of package ‘swift-numerics’ | |
| Resolved source packages: | |
| swift-numerics: https://github.com/apple/swift-numerics.git @ 1.1.1 | |
| swift-algorithms: https://github.com/apple/swift-algorithms.git @ 1.2.2 | |
| ''', | |
| ), | |
| ]); | |
| final processUtils = ProcessUtils(logger: testLogger, processManager: fakeProcessManager); | |
| final iosProject = FakeIosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| await iosProject.prefetchSwiftPackages( | |
| xcodebuildProjectCommandArguments: <String>[ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${buildDirectory.path}/SourcePackages', | |
| ], | |
| processUtils: processUtils, | |
| logger: testLogger, | |
| ); | |
| expect(fakeProcessManager, hasNoRemainingExpectations); | |
| expect( | |
| testLogger.statusText, | |
| contains(''' | |
| Xcode is fetching Swift Package Manager dependencies. This may take several minutes... | |
| Fetching from https://github.com/apple/swift-algorithms.git (cached)... | |
| Fetching from https://github.com/apple/swift-numerics.git (cached)...'''), | |
| ); | |
| }); | |
| testWithoutContext('skips running if already completed or in progress', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final testLogger = BufferLogger.test(); | |
| const projectPath = 'path/to/project'; | |
| final Directory buildDirectory = fs.directory('$projectPath/build/ios'); | |
| final fakeProcessManager = FakeProcessManager.empty(); | |
| fakeProcessManager.addCommands(<FakeCommand>[ | |
| FakeCommand( | |
| command: <String>[ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${buildDirectory.path}/SourcePackages', | |
| '-resolvePackageDependencies', | |
| ], | |
| ), | |
| ]); | |
| final processUtils = ProcessUtils(logger: testLogger, processManager: fakeProcessManager); | |
| final iosProject = FakeIosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| await iosProject.prefetchSwiftPackages( | |
| xcodebuildProjectCommandArguments: <String>[ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${buildDirectory.path}/SourcePackages', | |
| ], | |
| processUtils: processUtils, | |
| logger: testLogger, | |
| ); | |
| expect(fakeProcessManager, hasNoRemainingExpectations); | |
| // Second call should skip starting a new process | |
| await iosProject.prefetchSwiftPackages( | |
| xcodebuildProjectCommandArguments: <String>[ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${buildDirectory.path}/SourcePackages', | |
| ], | |
| processUtils: processUtils, | |
| logger: testLogger, | |
| ); | |
| expect(fakeProcessManager, hasNoRemainingExpectations); | |
| }); | |
| testWithoutContext('throws exception when resolving fails', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final testLogger = BufferLogger.test(); | |
| const projectPath = 'path/to/project'; | |
| final Directory buildDirectory = fs.directory('$projectPath/build/ios'); | |
| final fakeProcessManager = FakeProcessManager.empty(); | |
| fakeProcessManager.addCommands(<FakeCommand>[ | |
| FakeCommand( | |
| command: <String>[ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${buildDirectory.path}/SourcePackages', | |
| '-resolvePackageDependencies', | |
| ], | |
| exitCode: 1, | |
| stderr: 'error: some error', | |
| ), | |
| ]); | |
| final processUtils = ProcessUtils(logger: testLogger, processManager: fakeProcessManager); | |
| final iosProject = FakeIosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| await expectLater( | |
| iosProject.prefetchSwiftPackages( | |
| xcodebuildProjectCommandArguments: <String>[ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${buildDirectory.path}/SourcePackages', | |
| ], | |
| processUtils: processUtils, | |
| logger: testLogger, | |
| ), | |
| throwsToolExit(), | |
| ); | |
| }); | |
| testWithoutContext('prefetchSwiftPackages can run for both platforms', () async { | |
| final fs = MemoryFileSystem.test(); | |
| final testLogger = BufferLogger.test(); | |
| const projectPath = 'path/to/project'; | |
| final Directory iosBuildDirectory = fs.directory('$projectPath/build/ios'); | |
| final Directory macosBuildDirectory = fs.directory('$projectPath/build/macos'); | |
| final fakeProcessManager = FakeProcessManager.empty(); | |
| fakeProcessManager.addCommands(<FakeCommand>[ | |
| FakeCommand( | |
| command: <String>[ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${iosBuildDirectory.path}/SourcePackages', | |
| '-resolvePackageDependencies', | |
| ], | |
| ), | |
| FakeCommand( | |
| command: <String>[ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${macosBuildDirectory.path}/SourcePackages', | |
| '-resolvePackageDependencies', | |
| ], | |
| ), | |
| ]); | |
| final processUtils = ProcessUtils(logger: testLogger, processManager: fakeProcessManager); | |
| final iosProject = FakeIosProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| await iosProject.prefetchSwiftPackages( | |
| xcodebuildProjectCommandArguments: [ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${iosBuildDirectory.path}/SourcePackages', | |
| ], | |
| processUtils: processUtils, | |
| logger: testLogger, | |
| ); | |
| final macosProject = FakeMacOSProject.fromFlutter(FakeFlutterProject(fileSystem: fs)); | |
| await macosProject.prefetchSwiftPackages( | |
| xcodebuildProjectCommandArguments: [ | |
| 'xcrun', | |
| 'xcodebuild', | |
| '-clonedSourcePackagesDirPath', | |
| '/${macosBuildDirectory.path}/SourcePackages', | |
| ], | |
| processUtils: processUtils, | |
| logger: testLogger, | |
| ); | |
| expect(fakeProcessManager, hasNoRemainingExpectations); | |
| }); | |
| }); | |
| }); | |
| } | |
| class FakeFlutterProject extends Fake implements FlutterProject { | |
| FakeFlutterProject({required this.fileSystem, this.isModule = false}); | |
| MemoryFileSystem fileSystem; | |
| @override | |
| late final Directory directory = fileSystem.directory('app_name'); | |
| @override | |
| bool isModule = false; | |
| @override | |
| FlutterManifest get manifest => FakeFlutterManifest(); | |
| } | |
| class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterpreter { | |
| FakeXcodeProjectInterpreter({ | |
| this.isInstalled = true, | |
| this.version, | |
| this.schemes = const <String>['Runner'], | |
| }); | |
| @override | |
| final bool isInstalled; | |
| @override | |
| final Version? version; | |
| List<String> schemes; | |
| @override | |
| Future<XcodeProjectInfo?> getInfo( | |
| XcodeBasedProject xcodeProject, { | |
| String? projectFilename, | |
| required Directory buildDirectory, | |
| }) async { | |
| return XcodeProjectInfo(<String>[], <String>[], schemes, BufferLogger.test()); | |
| } | |
| } | |
| class FakeFlutterManifest extends Fake implements FlutterManifest { | |
| FakeFlutterManifest({this.isModule = false}); | |
| @override | |
| bool isModule; | |
| @override | |
| String? buildName; | |
| @override | |
| String? buildNumber; | |
| @override | |
| String? get iosBundleIdentifier => null; | |
| @override | |
| String get appName => ''; | |
| @override | |
| List<String> get workspace => <String>[]; | |
| @override | |
| PluginPlatformConfig? get ios => null; | |
| @override | |
| PluginPlatformConfig? get macos => null; | |
| } | |
| class FakeCache extends Fake implements Cache { | |
| FakeCache({this.olderThanToolsStamp = false}); | |
| bool olderThanToolsStamp; | |
| Map<String, bool> filesOlderThanToolsStamp = <String, bool>{}; | |
| @override | |
| bool isOlderThanToolsStamp(FileSystemEntity entity) { | |
| if (filesOlderThanToolsStamp.containsKey(entity.basename)) { | |
| return filesOlderThanToolsStamp[entity.basename]!; | |
| } | |
| return olderThanToolsStamp; | |
| } | |
| } | |
| class FakeIosProject extends IosProject { | |
| FakeIosProject.fromFlutter(super.parent) : super.fromFlutter(); | |
| @override | |
| bool usesSwiftPackageManager = true; | |
| @override | |
| bool flutterPluginSwiftPackageInProjectSettings = true; | |
| } | |
| class FakeMacOSProject extends MacOSProject { | |
| FakeMacOSProject.fromFlutter(super.parent) : super.fromFlutter(); | |
| @override | |
| bool usesSwiftPackageManager = true; | |
| @override | |
| bool flutterPluginSwiftPackageInProjectSettings = true; | |
| } | |
| class FakeIosProjectWithCustomFlags extends IosProject { | |
| FakeIosProjectWithCustomFlags.fromFlutter( | |
| super.parent, { | |
| this.usesSwiftPackageManager = true, | |
| this.flutterPluginSwiftPackageInProjectSettings = true, | |
| }) : super.fromFlutter(); | |
| @override | |
| final bool usesSwiftPackageManager; | |
| @override | |
| final bool flutterPluginSwiftPackageInProjectSettings; | |
| } |