Create Podfile dynamically part 1 (#11101) * start * with create * refactor cocoapod code, add tests * fix tests * throw when cocoapod missing * obj-c projects don’t use use_framework!
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index 4e724c0..a931d36 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -224,6 +224,9 @@ @override String get deviceBundlePath => _buildAppPath('iphoneos'); + /// True if the app is built from a Swift project. Null if unknown. + bool get isSwift => buildSettings?.containsKey('SWIFT_VERSION'); + String _buildAppPath(String type) { return fs.path.join(getIosBuildDirectory(), 'Release-$type', kBundleName); }
diff --git a/packages/flutter_tools/lib/src/ios/cocoapods.dart b/packages/flutter_tools/lib/src/ios/cocoapods.dart new file mode 100644 index 0000000..5b00037 --- /dev/null +++ b/packages/flutter_tools/lib/src/ios/cocoapods.dart
@@ -0,0 +1,136 @@ +// Copyright 2017 The Chromium 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:meta/meta.dart'; + +import '../base/common.dart'; +import '../base/context.dart'; +import '../base/file_system.dart'; +import '../base/io.dart'; +import '../base/logger.dart'; +import '../base/process.dart'; +import '../base/process_manager.dart'; +import '../base/version.dart'; +import '../cache.dart'; +import '../globals.dart'; + +final String noCocoaPodsConsequence = ''' + CocoaPods is used to retrieve the iOS platform side's plugin code that responds to your plugin usage on the Dart side. + Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS. + For more info, see https://flutter.io/platform-plugins'''; + +final String cocoaPodsInstallInstructions = ''' + brew update + brew install cocoapods + pod setup'''; + +final String cocoaPodsUpgradeInstructions = ''' + brew update + brew upgrade cocoapods + pod setup'''; + +CocoaPods get cocoaPods => context.putIfAbsent(CocoaPods, () => const CocoaPods()); + +class CocoaPods { + const CocoaPods(); + + Future<bool> get hasCocoaPods => exitsHappyAsync(<String>['pod', '--version']); + + String get cocoaPodsMinimumVersion => '1.0.0'; + + Future<String> get cocoaPodsVersionText async => (await runAsync(<String>['pod', '--version'])).processResult.stdout.trim(); + + Future<bool> get isCocoaPodsInstalledAndMeetsVersionCheck async { + if (!await hasCocoaPods) + return false; + try { + final Version installedVersion = new Version.parse(await cocoaPodsVersionText); + return installedVersion >= new Version.parse(cocoaPodsMinimumVersion); + } on FormatException { + return false; + } + } + + /// Whether CocoaPods ran 'pod setup' once where the costly pods' specs are cloned. + Future<bool> get isCocoaPodsInitialized => fs.isDirectory(fs.path.join(homeDirPath, '.cocoapods', 'repos', 'master')); + + Future<Null> processPods({ + @required Directory appIosDir, + @required String iosEngineDir, + bool isSwift: false, + }) async { + if (await _checkPodCondition()) { + if (!fs.file(fs.path.join(appIosDir.path, 'Podfile')).existsSync()) { + await _createPodfile(appIosDir, isSwift); + } // TODO(xster): Add more logic for handling merge conflicts. + + await _runPodInstall(appIosDir, iosEngineDir); + } else { + throwToolExit('CocoaPods not available for project using Flutter plugins'); + } + } + + Future<bool> _checkPodCondition() async { + if (!await isCocoaPodsInstalledAndMeetsVersionCheck) { + final String minimumVersion = cocoaPodsMinimumVersion; + printError( + 'Warning: CocoaPods version $minimumVersion or greater not installed. Skipping pod install.\n' + '$noCocoaPodsConsequence\n' + 'To install:\n' + '$cocoaPodsInstallInstructions\n', + emphasis: true, + ); + return false; + } + if (!await isCocoaPodsInitialized) { + printError( + 'Warning: CocoaPods installed but not initialized. Skipping pod install.\n' + '$noCocoaPodsConsequence\n' + 'To initialize CocoaPods, run:\n' + ' pod setup\n' + 'once to finalize CocoaPods\' installation.', + emphasis: true, + ); + return false; + } + + return true; + } + + Future<Null> _createPodfile(Directory bundle, bool isSwift) async { + final File podfileTemplate = fs.file(fs.path.join( + Cache.flutterRoot, + 'packages', + 'flutter_tools', + 'templates', + 'cocoapods', + isSwift ? 'Podfile-swift' : 'Podfile-objc', + )); + podfileTemplate.copySync(fs.path.join(bundle.path, 'Podfile')); + } + + Future<Null> _runPodInstall(Directory bundle, String engineDirectory) async { + final Status status = logger.startProgress('Running pod install...', expectSlowOperation: true); + final ProcessResult result = await processManager.run( + <String>['pod', 'install', '--verbose'], + workingDirectory: bundle.path, + environment: <String, String>{'FLUTTER_FRAMEWORK_DIR': engineDirectory}, + ); + status.stop(); + if (logger.isVerbose || result.exitCode != 0) { + if (result.stdout.isNotEmpty) { + printStatus('CocoaPods\' output:\n↳'); + printStatus(result.stdout, indent: 4); + } + if (result.stderr.isNotEmpty) { + printStatus('Error output from CocoaPods:\n↳'); + printStatus(result.stderr, indent: 4); + } + } + if (result.exitCode != 0) + throwToolExit('Error running pod install'); + } +}
diff --git a/packages/flutter_tools/lib/src/ios/ios_workflow.dart b/packages/flutter_tools/lib/src/ios/ios_workflow.dart index ee87270..a833ef5 100644 --- a/packages/flutter_tools/lib/src/ios/ios_workflow.dart +++ b/packages/flutter_tools/lib/src/ios/ios_workflow.dart
@@ -4,14 +4,13 @@ import 'dart:async'; -import '../base/common.dart'; import '../base/context.dart'; -import '../base/file_system.dart'; import '../base/os.dart'; import '../base/platform.dart'; import '../base/process.dart'; import '../base/version.dart'; import '../doctor.dart'; +import 'cocoapods.dart'; import 'mac.dart'; IOSWorkflow get iosWorkflow => context.putIfAbsent(IOSWorkflow, () => new IOSWorkflow()); @@ -43,12 +42,6 @@ bool get hasPythonSixModule => kPythonSix.isInstalled; - Future<bool> get hasCocoaPods => exitsHappyAsync(<String>['pod', '--version']); - - String get cocoaPodsMinimumVersion => '1.0.0'; - - Future<String> get cocoaPodsVersionText async => (await runAsync(<String>['pod', '--version'])).processResult.stdout.trim(); - Future<String> get macDevMode async => (await runAsync(<String>['DevToolsSecurity', '-status'])).processResult.stdout; Future<bool> get _iosDeployIsInstalledAndMeetsVersionCheck async { @@ -62,20 +55,6 @@ } } - Future<bool> get isCocoaPodsInstalledAndMeetsVersionCheck async { - if (!await hasCocoaPods) - return false; - try { - final Version installedVersion = new Version.parse(await cocoaPodsVersionText); - return installedVersion >= new Version.parse(cocoaPodsMinimumVersion); - } on FormatException { - return false; - } - } - - /// Whether CocoaPods ran 'pod setup' once where the costly pods' specs are cloned. - Future<bool> get isCocoaPodsInitialized => fs.isDirectory(fs.path.join(homeDirPath, '.cocoapods', 'repos', 'master')); - @override Future<ValidationResult> validate() async { final List<ValidationMessage> messages = <ValidationMessage>[]; @@ -187,9 +166,9 @@ } } - if (await isCocoaPodsInstalledAndMeetsVersionCheck) { - if (await isCocoaPodsInitialized) { - messages.add(new ValidationMessage('CocoaPods version ${await cocoaPodsVersionText}')); + if (await cocoaPods.isCocoaPodsInstalledAndMeetsVersionCheck) { + if (await cocoaPods.isCocoaPodsInitialized) { + messages.add(new ValidationMessage('CocoaPods version ${await cocoaPods.cocoaPodsVersionText}')); } else { brewStatus = ValidationType.partial; messages.add(new ValidationMessage.error( @@ -202,7 +181,7 @@ } } else { brewStatus = ValidationType.partial; - if (!await hasCocoaPods) { + if (!await cocoaPods.hasCocoaPods) { messages.add(new ValidationMessage.error( 'CocoaPods not installed.\n' '$noCocoaPodsConsequence\n' @@ -211,7 +190,7 @@ )); } else { messages.add(new ValidationMessage.error( - 'CocoaPods out of date ($cocoaPodsMinimumVersion is required).\n' + 'CocoaPods out of date ($cocoaPods.cocoaPodsMinimumVersion is required).\n' '$noCocoaPodsConsequence\n' 'To upgrade:\n' '$cocoaPodsUpgradeInstructions'
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart index a4ea001..8fb6ef1 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -21,8 +21,8 @@ import '../globals.dart'; import '../plugins.dart'; import '../services.dart'; +import 'cocoapods.dart'; import 'code_signing.dart'; -import 'ios_workflow.dart'; import 'xcodeproj.dart'; const int kXcodeRequiredVersionMajor = 8; @@ -215,7 +215,11 @@ final bool hasFlutterPlugins = injectPlugins(); if (hasFlutterPlugins) - await _runPodInstall(appDirectory, flutterFrameworkDir(mode)); + await cocoaPods.processPods( + appIosDir: appDirectory, + iosEngineDir: flutterFrameworkDir(mode), + isSwift: app.isSwift, + ); updateXcodeGeneratedProperties( projectPath: fs.currentDirectory.path, @@ -396,67 +400,6 @@ return true; } -final String noCocoaPodsConsequence = ''' - CocoaPods is used to retrieve the iOS platform side's plugin code that responds to your plugin usage on the Dart side. - Without resolving iOS dependencies with CocoaPods, plugins will not work on iOS. - For more info, see https://flutter.io/platform-plugins'''; - -final String cocoaPodsInstallInstructions = ''' - brew update - brew install cocoapods - pod setup'''; - -final String cocoaPodsUpgradeInstructions = ''' - brew update - brew upgrade cocoapods - pod setup'''; - -Future<Null> _runPodInstall(Directory bundle, String engineDirectory) async { - if (fs.file(fs.path.join(bundle.path, 'Podfile')).existsSync()) { - if (!await iosWorkflow.isCocoaPodsInstalledAndMeetsVersionCheck) { - final String minimumVersion = iosWorkflow.cocoaPodsMinimumVersion; - printError( - 'Warning: CocoaPods version $minimumVersion or greater not installed. Skipping pod install.\n' - '$noCocoaPodsConsequence\n' - 'To install:\n' - '$cocoaPodsInstallInstructions\n', - emphasis: true, - ); - return; - } - if (!await iosWorkflow.isCocoaPodsInitialized) { - printError( - 'Warning: CocoaPods installed but not initialized. Skipping pod install.\n' - '$noCocoaPodsConsequence\n' - 'To initialize CocoaPods, run:\n' - ' pod setup\n' - 'once to finalize CocoaPods\' installation.', - emphasis: true, - ); - return; - } - final Status status = logger.startProgress('Running pod install...', expectSlowOperation: true); - final ProcessResult result = await processManager.run( - <String>['pod', 'install', '--verbose'], - workingDirectory: bundle.path, - environment: <String, String>{'FLUTTER_FRAMEWORK_DIR': engineDirectory}, - ); - status.stop(); - if (logger.isVerbose || result.exitCode != 0) { - if (result.stdout.isNotEmpty) { - printStatus('CocoaPods\' output:\n↳'); - printStatus(result.stdout, indent: 4); - } - if (result.stderr.isNotEmpty) { - printStatus('Error output from CocoaPods:\n↳'); - printStatus(result.stderr, indent: 4); - } - } - if (result.exitCode != 0) - throwToolExit('Error running pod install'); - } -} - Future<Null> _addServicesToBundle(Directory bundle) async { final List<Map<String, String>> services = <Map<String, String>>[]; printTrace("Trying to resolve native pub services.");
diff --git a/packages/flutter_tools/templates/create/ios-objc.tmpl/Podfile b/packages/flutter_tools/templates/cocoapods/Podfile-objc similarity index 100% rename from packages/flutter_tools/templates/create/ios-objc.tmpl/Podfile rename to packages/flutter_tools/templates/cocoapods/Podfile-objc
diff --git a/packages/flutter_tools/templates/create/ios-swift.tmpl/Podfile b/packages/flutter_tools/templates/cocoapods/Podfile-swift similarity index 100% rename from packages/flutter_tools/templates/create/ios-swift.tmpl/Podfile rename to packages/flutter_tools/templates/cocoapods/Podfile-swift
diff --git a/packages/flutter_tools/test/application_package_test.dart b/packages/flutter_tools/test/application_package_test.dart index 12aa8ba..b46a58a 100644 --- a/packages/flutter_tools/test/application_package_test.dart +++ b/packages/flutter_tools/test/application_package_test.dart
@@ -16,6 +16,17 @@ expect(data.launchableActivityName, 'io.flutter.app.FlutterActivity'); }); }); + + group('BuildableIOSApp', () { + testUsingContext('check isSwift', () { + final BuildableIOSApp buildableIOSApp = new BuildableIOSApp( + projectBundleId: 'blah', + appDirectory: 'not/important', + buildSettings: _swiftBuildSettings, + ); + expect(buildableIOSApp.isSwift, true); + }); + }); } final String _aaptData = ''' @@ -44,3 +55,16 @@ densities: '160' '240' '320' '480' '640' native-code: 'armeabi-v7a' '''; + +final Map<String, String> _swiftBuildSettings = <String, String>{ + 'ARCHS': 'arm64', + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'CLANG_ENABLE_MODULES': 'YES', + 'ENABLE_BITCODE': 'NO', + 'INFOPLIST_FILE': 'Runner/Info.plist', + 'PRODUCT_BUNDLE_IDENTIFIER': 'com.yourcompany.test', + 'PRODUCT_NAME': 'blah', + 'SWIFT_OBJC_BRIDGING_HEADER': 'Runner/Runner-Bridging-Header.h', + 'SWIFT_OPTIMIZATION_LEVEL': '-Onone', + 'SWIFT_VERSION': '3.0', +};
diff --git a/packages/flutter_tools/test/ios/cocoapods_test.dart b/packages/flutter_tools/test/ios/cocoapods_test.dart new file mode 100644 index 0000000..615b052 --- /dev/null +++ b/packages/flutter_tools/test/ios/cocoapods_test.dart
@@ -0,0 +1,158 @@ +// Copyright 2017 The Chromium 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/file.dart'; +import 'package:file/memory.dart'; +import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/ios/cocoapods.dart'; +import 'package:flutter_tools/src/cache.dart'; +import 'package:mockito/mockito.dart'; +import 'package:process/process.dart'; +import 'package:test/test.dart'; + +import '../src/context.dart'; + +void main() { + FileSystem fs; + ProcessManager mockProcessManager; + Directory projectUnderTest; + CocoaPods cocoaPodsUnderTest; + + setUp(() { + Cache.flutterRoot = 'flutter'; + fs = new MemoryFileSystem(); + mockProcessManager = new MockProcessManager(); + projectUnderTest = fs.directory(fs.path.join('project', 'ios'))..createSync(recursive: true); + fs.file(fs.path.join( + Cache.flutterRoot, 'packages', 'flutter_tools', 'templates', 'cocoapods', 'Podfile-objc' + )) + ..createSync(recursive: true) + ..writeAsStringSync('Objective-C podfile template'); + fs.file(fs.path.join( + Cache.flutterRoot, 'packages', 'flutter_tools', 'templates', 'cocoapods', 'Podfile-swift' + )) + ..createSync(recursive: true) + ..writeAsStringSync('Swift podfile template'); + cocoaPodsUnderTest = const TestCocoaPods(); + + when(mockProcessManager.run( + <String>['pod', 'install', '--verbose'], + workingDirectory: 'project/ios', + environment: <String, String>{'FLUTTER_FRAMEWORK_DIR': 'engine/path'}, + )).thenReturn(exitsHappy); + }); + + testUsingContext( + 'create objective-c Podfile when not present', + () async { + await cocoaPodsUnderTest.processPods( + appIosDir: projectUnderTest, + iosEngineDir: 'engine/path', + ); + expect(fs.file(fs.path.join('project', 'ios', 'Podfile')).readAsStringSync() , 'Objective-C podfile template'); + verify(mockProcessManager.run( + <String>['pod', 'install', '--verbose'], + workingDirectory: 'project/ios', + environment: <String, String>{'FLUTTER_FRAMEWORK_DIR': 'engine/path'}, + )); + }, + overrides: <Type, Generator>{ + FileSystem: () => fs, + ProcessManager: () => mockProcessManager, + }, + ); + + testUsingContext( + 'create swift Podfile if swift', + () async { + await cocoaPodsUnderTest.processPods( + appIosDir: projectUnderTest, + iosEngineDir: 'engine/path', + isSwift: true, + ); + expect(fs.file(fs.path.join('project', 'ios', 'Podfile')).readAsStringSync() , 'Swift podfile template'); + verify(mockProcessManager.run( + <String>['pod', 'install', '--verbose'], + workingDirectory: 'project/ios', + environment: <String, String>{'FLUTTER_FRAMEWORK_DIR': 'engine/path'}, + )); + }, + overrides: <Type, Generator>{ + FileSystem: () => fs, + ProcessManager: () => mockProcessManager, + }, + ); + + testUsingContext( + 'do not recreate Podfile when present', + () async { + fs.file(fs.path.join('project', 'ios', 'Podfile')) + ..createSync() + ..writeAsString('Existing Podfile'); + await cocoaPodsUnderTest.processPods( + appIosDir: projectUnderTest, + iosEngineDir: 'engine/path', + ); expect(fs.file(fs.path.join('project', 'ios', 'Podfile')).readAsStringSync() , 'Existing Podfile'); + verify(mockProcessManager.run( + <String>['pod', 'install', '--verbose'], + workingDirectory: 'project/ios', + environment: <String, String>{'FLUTTER_FRAMEWORK_DIR': 'engine/path'}, + )); + }, + overrides: <Type, Generator>{ + FileSystem: () => fs, + ProcessManager: () => mockProcessManager, + }, + ); + + testUsingContext( + 'missing CocoaPods throws', + () async { + cocoaPodsUnderTest = const TestCocoaPods(false); + try { + await cocoaPodsUnderTest.processPods( + appIosDir: projectUnderTest, + iosEngineDir: 'engine/path', + ); + fail('Expected tool error'); + } catch (ToolExit) { + verifyNever(mockProcessManager.run( + <String>['pod', 'install', '--verbose'], + workingDirectory: 'project/ios', + environment: <String, String>{'FLUTTER_FRAMEWORK_DIR': 'engine/path'}, + )); + } + }, + overrides: <Type, Generator>{ + FileSystem: () => fs, + ProcessManager: () => mockProcessManager, + }, + ); +} + +class MockProcessManager extends Mock implements ProcessManager {} + +class TestCocoaPods extends CocoaPods { + const TestCocoaPods([this._hasCocoaPods = true]); + + final bool _hasCocoaPods; + + @override + Future<bool> get hasCocoaPods => new Future<bool>.value(_hasCocoaPods); + + @override + Future<String> get cocoaPodsVersionText async => new Future<String>.value('1.5.0'); + + @override + Future<bool> get isCocoaPodsInitialized => new Future<bool>.value(true); +} + +final ProcessResult exitsHappy = new ProcessResult( + 1, // pid + 0, // exitCode + '', // stdout + '', // stderr +); \ No newline at end of file
diff --git a/packages/flutter_tools/test/ios/ios_workflow_test.dart b/packages/flutter_tools/test/ios/ios_workflow_test.dart index cf92c7e..5cb1902 100644 --- a/packages/flutter_tools/test/ios/ios_workflow_test.dart +++ b/packages/flutter_tools/test/ios/ios_workflow_test.dart
@@ -9,6 +9,7 @@ import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/doctor.dart'; +import 'package:flutter_tools/src/ios/cocoapods.dart'; import 'package:flutter_tools/src/ios/ios_workflow.dart'; import 'package:flutter_tools/src/ios/mac.dart'; import 'package:mockito/mockito.dart'; @@ -22,13 +23,18 @@ MockIMobileDevice iMobileDevice; MockXcode xcode; MockProcessManager processManager; + MockCocoaPods cocoaPods; FileSystem fs; setUp(() { iMobileDevice = new MockIMobileDevice(); xcode = new MockXcode(); processManager = new MockProcessManager(); + cocoaPods = new MockCocoaPods(); fs = new MemoryFileSystem(); + + when(cocoaPods.isCocoaPodsInstalledAndMeetsVersionCheck).thenReturn(true); + when(cocoaPods.isCocoaPodsInitialized).thenReturn(true); }); testUsingContext('Emit missing status when nothing is installed', () async { @@ -44,6 +50,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when Xcode is not installed', () async { @@ -55,6 +62,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when Xcode is partially installed', () async { @@ -66,6 +74,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when Xcode version too low', () async { @@ -80,6 +89,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when Xcode EULA not signed', () async { @@ -94,6 +104,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when Mac dev mode was never enabled', () async { @@ -108,6 +119,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when python six not installed', () async { @@ -122,6 +134,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when homebrew not installed', () async { @@ -136,6 +149,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when libimobiledevice is not installed', () async { @@ -150,6 +164,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => new MockIMobileDevice(isWorking: false), Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when ios-deploy is not installed', () async { @@ -164,6 +179,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when ios-deploy version is too low', () async { @@ -178,6 +194,7 @@ }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when CocoaPods is not installed', () async { @@ -186,12 +203,15 @@ .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); - final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(hasCocoaPods: false); + when(cocoaPods.isCocoaPodsInstalledAndMeetsVersionCheck).thenReturn(false); + when(cocoaPods.hasCocoaPods).thenReturn(false); + final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(); final ValidationResult result = await workflow.validate(); expect(result.type, ValidationType.partial); }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when CocoaPods version is too low', () async { @@ -200,12 +220,16 @@ .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); - final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(cocoaPodsVersionText: '0.39.0'); + when(cocoaPods.isCocoaPodsInstalledAndMeetsVersionCheck).thenReturn(false); + when(cocoaPods.hasCocoaPods).thenReturn(true); + when(cocoaPods.cocoaPodsVersionText).thenReturn('0.39.0'); + final IOSWorkflowTestTarget workflow = new IOSWorkflowTestTarget(); final ValidationResult result = await workflow.validate(); expect(result.type, ValidationType.partial); }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, }); testUsingContext('Emits partial status when CocoaPods is not initialized', () async { @@ -214,6 +238,9 @@ .thenReturn('Xcode 8.2.1\nBuild version 8C1002\n'); when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(xcode.eulaSigned).thenReturn(true); + when(cocoaPods.isCocoaPodsInstalledAndMeetsVersionCheck).thenReturn(false); + when(cocoaPods.hasCocoaPods).thenReturn(true); + when(cocoaPods.isCocoaPodsInitialized).thenReturn(false); final ValidationResult result = await new IOSWorkflowTestTarget().validate(); expect(result.type, ValidationType.partial); @@ -221,6 +248,7 @@ FileSystem: () => fs, IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, ProcessManager: () => processManager, }); @@ -239,6 +267,7 @@ FileSystem: () => fs, IMobileDevice: () => iMobileDevice, Xcode: () => xcode, + CocoaPods: () => cocoaPods, ProcessManager: () => processManager, }); }); @@ -260,6 +289,7 @@ class MockXcode extends Mock implements Xcode {} class MockProcessManager extends Mock implements ProcessManager {} +class MockCocoaPods extends Mock implements CocoaPods {} class IOSWorkflowTestTarget extends IOSWorkflow { IOSWorkflowTestTarget({ @@ -268,14 +298,10 @@ bool hasIosDeploy: true, String iosDeployVersionText: '1.9.0', bool hasIDeviceInstaller: true, - bool hasCocoaPods: true, - String cocoaPodsVersionText: '1.2.0', String macDevMode: 'Developer mode is already enabled.', }) : hasIosDeploy = new Future<bool>.value(hasIosDeploy), iosDeployVersionText = new Future<String>.value(iosDeployVersionText), hasIDeviceInstaller = new Future<bool>.value(hasIDeviceInstaller), - hasCocoaPods = new Future<bool>.value(hasCocoaPods), - cocoaPodsVersionText = new Future<String>.value(cocoaPodsVersionText), macDevMode = new Future<String>.value(macDevMode); @override @@ -294,11 +320,5 @@ final Future<bool> hasIDeviceInstaller; @override - final Future<bool> hasCocoaPods; - - @override - final Future<String> cocoaPodsVersionText; - - @override final Future<String> macDevMode; }