Unverified Commit 86389be6 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Convert CocoaPods tests to testWithoutContext (#53291)

parent ff20bb8c
......@@ -104,7 +104,14 @@ Future<T> runInContext<T>(
operatingSystemUtils: globals.os,
platform: globals.platform,
),
CocoaPods: () => CocoaPods(),
CocoaPods: () => CocoaPods(
fileSystem: globals.fs,
processManager: globals.processManager,
logger: globals.logger,
platform: globals.platform,
xcodeProjectInterpreter: globals.xcodeProjectInterpreter,
timeoutConfiguration: timeoutConfiguration,
),
CocoaPodsValidator: () => CocoaPodsValidator(
globals.cocoaPods,
globals.userMessages,
......
......@@ -6,6 +6,8 @@ import 'dart:async';
import 'package:file/file.dart';
import 'package:meta/meta.dart';
import 'package:platform/platform.dart';
import 'package:process/process.dart';
import '../base/common.dart';
import '../base/file_system.dart';
......@@ -14,7 +16,7 @@ import '../base/logger.dart';
import '../base/process.dart';
import '../base/version.dart';
import '../cache.dart';
import '../globals.dart' as globals;
import '../ios/xcodeproj.dart';
import '../project.dart';
const String noCocoaPodsConsequence = '''
......@@ -62,16 +64,41 @@ enum CocoaPodsStatus {
}
class CocoaPods {
CocoaPods({
@required FileSystem fileSystem,
@required ProcessManager processManager,
@required XcodeProjectInterpreter xcodeProjectInterpreter,
@required Logger logger,
@required Platform platform,
@required TimeoutConfiguration timeoutConfiguration,
}) : _fileSystem = fileSystem,
_processManager = processManager,
_xcodeProjectInterpreter = xcodeProjectInterpreter,
_logger = logger,
_platform = platform,
_processUtils = ProcessUtils(processManager: processManager, logger: logger),
_fileSystemUtils = FileSystemUtils(fileSystem: fileSystem, platform: platform),
_timeoutConfiguration = timeoutConfiguration;
final FileSystem _fileSystem;
final ProcessManager _processManager;
final FileSystemUtils _fileSystemUtils;
final ProcessUtils _processUtils;
final XcodeProjectInterpreter _xcodeProjectInterpreter;
final Logger _logger;
final Platform _platform;
final TimeoutConfiguration _timeoutConfiguration;
Future<String> _versionText;
String get cocoaPodsMinimumVersion => '1.6.0';
String get cocoaPodsRecommendedVersion => '1.8.0';
Future<bool> get isInstalled =>
globals.processUtils.exitsHappy(<String>['which', 'pod']);
_processUtils.exitsHappy(<String>['which', 'pod']);
Future<String> get cocoaPodsVersionText {
_versionText ??= globals.processUtils.run(
_versionText ??= _processUtils.run(
<String>['pod', '--version'],
environment: <String, String>{
'LANG': 'en_US.UTF-8',
......@@ -124,9 +151,9 @@ class CocoaPods {
if (installedVersion != null && installedVersion >= Version.parse('1.8.0')) {
return true;
}
final String cocoapodsReposDir = globals.platform.environment['CP_REPOS_DIR']
?? globals.fs.path.join(globals.fsUtils.homeDirPath, '.cocoapods', 'repos');
return globals.fs.isDirectory(globals.fs.path.join(cocoapodsReposDir, 'master'));
final String cocoapodsReposDir = _platform.environment['CP_REPOS_DIR']
?? _fileSystem.path.join(_fileSystemUtils.homeDirPath, '.cocoapods', 'repos');
return _fileSystem.isDirectory(_fileSystem.path.join(cocoapodsReposDir, 'master'));
}
Future<bool> processPods({
......@@ -155,7 +182,7 @@ class CocoaPods {
final CocoaPodsStatus installation = await evaluateCocoaPodsInstallation;
switch (installation) {
case CocoaPodsStatus.notInstalled:
globals.printError(
_logger.printError(
'Warning: CocoaPods not installed. Skipping pod install.\n'
'$noCocoaPodsConsequence\n'
'To install:\n'
......@@ -164,7 +191,7 @@ class CocoaPods {
);
return false;
case CocoaPodsStatus.brokenInstall:
globals.printError(
_logger.printError(
'Warning: CocoaPods is installed but broken. Skipping pod install.\n'
'$brokenCocoaPodsConsequence\n'
'To re-install:\n'
......@@ -173,7 +200,7 @@ class CocoaPods {
);
return false;
case CocoaPodsStatus.unknownVersion:
globals.printError(
_logger.printError(
'Warning: Unknown CocoaPods version installed.\n'
'$unknownCocoaPodsConsequence\n'
'To upgrade:\n'
......@@ -182,7 +209,7 @@ class CocoaPods {
);
break;
case CocoaPodsStatus.belowMinimumVersion:
globals.printError(
_logger.printError(
'Warning: CocoaPods minimum required version $cocoaPodsMinimumVersion or greater not installed. Skipping pod install.\n'
'$noCocoaPodsConsequence\n'
'To upgrade:\n'
......@@ -191,7 +218,7 @@ class CocoaPods {
);
return false;
case CocoaPodsStatus.belowRecommendedVersion:
globals.printError(
_logger.printError(
'Warning: CocoaPods recommended version $cocoaPodsRecommendedVersion or greater not installed.\n'
'Pods handling may fail on some projects involving plugins.\n'
'To upgrade:\n'
......@@ -203,7 +230,7 @@ class CocoaPods {
break;
}
if (!await isCocoaPodsInitialized) {
globals.printError(
_logger.printError(
'Warning: CocoaPods installed but not initialized. Skipping pod install.\n'
'$noCocoaPodsConsequence\n'
'To initialize CocoaPods, run:\n'
......@@ -221,7 +248,7 @@ class CocoaPods {
/// contains a suitable `Podfile` and that its `Flutter/Xxx.xcconfig` files
/// include pods configuration.
Future<void> setupPodfile(XcodeBasedProject xcodeProject) async {
if (!globals.xcodeProjectInterpreter.isInstalled) {
if (!_xcodeProjectInterpreter.isInstalled) {
// Don't do anything for iOS when host platform doesn't support it.
return;
}
......@@ -238,13 +265,13 @@ class CocoaPods {
if (xcodeProject is MacOSProject) {
podfileTemplateName = 'Podfile-macos';
} else {
final bool isSwift = (await globals.xcodeProjectInterpreter.getBuildSettings(
final bool isSwift = (await _xcodeProjectInterpreter.getBuildSettings(
runnerProject.path,
'Runner',
)).containsKey('SWIFT_VERSION');
podfileTemplateName = isSwift ? 'Podfile-ios-swift' : 'Podfile-ios-objc';
}
final File podfileTemplate = globals.fs.file(globals.fs.path.join(
final File podfileTemplate = _fileSystem.file(_fileSystem.path.join(
Cache.flutterRoot,
'packages',
'flutter_tools',
......@@ -305,10 +332,10 @@ class CocoaPods {
}
Future<void> _runPodInstall(XcodeBasedProject xcodeProject, String engineDirectory) async {
final Status status = globals.logger.startProgress('Running pod install...', timeout: timeoutConfiguration.slowOperation);
final ProcessResult result = await globals.processManager.run(
final Status status = _logger.startProgress('Running pod install...', timeout: _timeoutConfiguration.slowOperation);
final ProcessResult result = await _processManager.run(
<String>['pod', 'install', '--verbose'],
workingDirectory: globals.fs.path.dirname(xcodeProject.podfile.path),
workingDirectory: _fileSystem.path.dirname(xcodeProject.podfile.path),
environment: <String, String>{
'FLUTTER_FRAMEWORK_DIR': engineDirectory,
// See https://github.com/flutter/flutter/issues/10873.
......@@ -318,16 +345,16 @@ class CocoaPods {
},
);
status.stop();
if (globals.logger.isVerbose || result.exitCode != 0) {
if (_logger.isVerbose || result.exitCode != 0) {
final String stdout = result.stdout as String;
if (stdout.isNotEmpty) {
globals.printStatus("CocoaPods' output:\n↳");
globals.printStatus(stdout, indent: 4);
_logger.printStatus("CocoaPods' output:\n↳");
_logger.printStatus(stdout, indent: 4);
}
final String stderr = result.stderr as String;
if (stderr.isNotEmpty) {
globals.printStatus('Error output from CocoaPods:\n↳');
globals.printStatus(stderr, indent: 4);
_logger.printStatus('Error output from CocoaPods:\n↳');
_logger.printStatus(stderr, indent: 4);
}
}
if (result.exitCode != 0) {
......@@ -340,7 +367,7 @@ class CocoaPods {
void _diagnosePodInstallFailure(ProcessResult result) {
final dynamic stdout = result.stdout;
if (stdout is String && stdout.contains('out-of-date source repos')) {
globals.printError(
_logger.printError(
"Error: CocoaPods's specs repository is too out-of-date to satisfy dependencies.\n"
'To update the CocoaPods specs, run:\n'
' pod repo update\n',
......@@ -360,12 +387,12 @@ class CocoaPods {
if (xcodeProject is! IosProject) {
return;
}
final Link flutterSymlink = globals.fs.link(globals.fs.path.join(
final Link flutterSymlink = _fileSystem.link(_fileSystem.path.join(
xcodeProject.symlinks.path,
'flutter',
));
if (flutterSymlink.existsSync()) {
globals.printError(
_logger.printError(
'Warning: Podfile is out of date\n'
'$outOfDatePodfileConsequence\n'
'To regenerate the Podfile, run:\n'
......
......@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:platform/platform.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
......@@ -31,6 +32,7 @@ void main() {
FlutterProject projectUnderTest;
CocoaPods cocoaPodsUnderTest;
InvokeProcess resultOfPodVersion;
BufferLogger logger;
void pretendPodVersionFails() {
resultOfPodVersion = () async => exitsWithError();
......@@ -64,10 +66,18 @@ void main() {
Cache.flutterRoot = 'flutter';
fs = MemoryFileSystem();
mockProcessManager = MockProcessManager();
logger = BufferLogger.test();
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
projectUnderTest = FlutterProject.fromDirectory(fs.directory('project'));
projectUnderTest.ios.xcodeProject.createSync(recursive: true);
cocoaPodsUnderTest = CocoaPods();
cocoaPodsUnderTest = CocoaPods(
fileSystem: fs,
processManager: mockProcessManager,
logger: logger,
platform: FakePlatform(),
xcodeProjectInterpreter: mockXcodeProjectInterpreter,
timeoutConfiguration: const TimeoutConfiguration(),
);
pretendPodVersionIs('1.8.0');
fs.file(fs.path.join(
Cache.flutterRoot, 'packages', 'flutter_tools', 'templates', 'cocoapods', 'Podfile-ios-objc',
......@@ -139,92 +149,74 @@ void main() {
when(mockProcessManager.canRun(any)).thenReturn(true);
});
testUsingContext('detects not installed, if pod exec does not exist', () async {
testWithoutContext('detects not installed, if pod exec does not exist', () async {
pretendPodIsNotInstalled();
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.notInstalled);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('detects not installed, if pod is installed but version fails', () async {
testWithoutContext('detects not installed, if pod is installed but version fails', () async {
pretendPodIsInstalled();
pretendPodVersionFails();
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.brokenInstall);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('detects installed', () async {
testWithoutContext('detects installed', () async {
pretendPodIsInstalled();
pretendPodVersionIs('0.0.1');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, isNot(CocoaPodsStatus.notInstalled));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('detects unknown version', () async {
testWithoutContext('detects unknown version', () async {
pretendPodIsInstalled();
pretendPodVersionIs('Plugin loaded.\n1.5.3');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.unknownVersion);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('detects below minimum version', () async {
testWithoutContext('detects below minimum version', () async {
pretendPodIsInstalled();
pretendPodVersionIs('1.5.0');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.belowMinimumVersion);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('detects below recommended version', () async {
testWithoutContext('detects below recommended version', () async {
pretendPodIsInstalled();
pretendPodVersionIs('1.6.0');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.belowRecommendedVersion);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('detects at recommended version', () async {
testWithoutContext('detects at recommended version', () async {
pretendPodIsInstalled();
pretendPodVersionIs('1.8.0');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.recommended);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('detects above recommended version', () async {
testWithoutContext('detects above recommended version', () async {
pretendPodIsInstalled();
pretendPodVersionIs('1.8.1');
expect(await cocoaPodsUnderTest.evaluateCocoaPodsInstallation, CocoaPodsStatus.recommended);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('detects initialized over 1.8.0', () async {
testWithoutContext('detects initialized over 1.8.0', () async {
pretendPodIsInstalled();
pretendPodVersionIs('1.8.0');
expect(await cocoaPodsUnderTest.isCocoaPodsInitialized, isTrue);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(),
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
});
group('Setup Podfile', () {
testUsingContext('creates objective-c Podfile when not present', () async {
setUp(() {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
});
testWithoutContext('creates objective-c Podfile when not present', () async {
when(mockXcodeProjectInterpreter.getBuildSettings(any, any))
.thenAnswer((_) async => <String, String>{});
await cocoaPodsUnderTest.setupPodfile(projectUnderTest.ios);
expect(projectUnderTest.ios.podfile.readAsStringSync(), 'Objective-C iOS podfile template');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('creates swift Podfile if swift', () async {
when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.getBuildSettings(any, any))
.thenAnswer((_) async => <String, String>{
'SWIFT_VERSION': '5.0',
......@@ -240,14 +232,11 @@ void main() {
XcodeProjectInterpreter: () => mockXcodeProjectInterpreter,
});
testUsingContext('creates macOS Podfile when not present', () async {
testWithoutContext('creates macOS Podfile when not present', () async {
projectUnderTest.macos.xcodeProject.createSync(recursive: true);
await cocoaPodsUnderTest.setupPodfile(projectUnderTest.macos);
expect(projectUnderTest.macos.podfile.readAsStringSync(), 'macOS podfile template');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('does not recreate Podfile when already present', () async {
......@@ -337,7 +326,7 @@ void main() {
when(mockProcessManager.canRun(any)).thenReturn(true);
});
testUsingContext('throwsToolExit if CocoaPods is not installed', () async {
testWithoutContext('throwsToolExit if CocoaPods is not installed', () async {
pretendPodIsNotInstalled();
projectUnderTest.ios.podfile.createSync();
final Function invokeProcessPods = () async => await cocoaPodsUnderTest.processPods(
......@@ -350,12 +339,9 @@ void main() {
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('throwsToolExit if CocoaPods install is broken', () async {
testWithoutContext('throwsToolExit if CocoaPods install is broken', () async {
pretendPodIsBroken();
projectUnderTest.ios.podfile.createSync();
final Function invokeProcessPods = () async => await cocoaPodsUnderTest.processPods(
......@@ -368,12 +354,9 @@ void main() {
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('prints warning, if Podfile is out of date', () async {
testWithoutContext('prints warning, if Podfile is out of date', () async {
pretendPodIsInstalled();
fs.file(fs.path.join('project', 'ios', 'Podfile'))
......@@ -388,13 +371,10 @@ void main() {
xcodeProject: projectUnderTest.ios,
engineDir: 'engine/path',
);
expect(testLogger.errorText, contains('Warning: Podfile is out of date'));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
expect(logger.errorText, contains('Warning: Podfile is out of date'));
});
testUsingContext('throws, if Podfile is missing.', () async {
testWithoutContext('throws, if Podfile is missing.', () async {
pretendPodIsInstalled();
try {
await cocoaPodsUnderTest.processPods(
......@@ -410,12 +390,9 @@ void main() {
environment: anyNamed('environment'),
));
}
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('throws, if specs repo is outdated.', () async {
testWithoutContext('throws, if specs repo is outdated.', () async {
pretendPodIsInstalled();
fs.file(fs.path.join('project', 'ios', 'Podfile'))
..createSync()
......@@ -454,16 +431,13 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
} on Exception catch (e) {
expect(e, isA<ToolExit>());
expect(
testLogger.errorText,
logger.errorText,
contains("CocoaPods's specs repository is too out-of-date to satisfy dependencies"),
);
}
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('run pod install, if Podfile.lock is missing', () async {
testWithoutContext('run pod install, if Podfile.lock is missing', () async {
pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
......@@ -482,12 +456,9 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
workingDirectory: 'project/ios',
environment: <String, String>{'FLUTTER_FRAMEWORK_DIR': 'engine/path', 'COCOAPODS_DISABLE_STATS': 'true', 'LANG': 'en_US.UTF-8'},
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('runs pod install, if Manifest.lock is missing', () async {
testWithoutContext('runs pod install, if Manifest.lock is missing', () async {
pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
......@@ -510,12 +481,9 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
'LANG': 'en_US.UTF-8',
},
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('runs pod install, if Manifest.lock different from Podspec.lock', () async {
testWithoutContext('runs pod install, if Manifest.lock different from Podspec.lock', () async {
pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
......@@ -541,12 +509,9 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
'LANG': 'en_US.UTF-8',
},
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('runs pod install, if flutter framework changed', () async {
testWithoutContext('runs pod install, if flutter framework changed', () async {
pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
......@@ -572,12 +537,9 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
'LANG': 'en_US.UTF-8',
},
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('runs pod install, if Podfile.lock is older than Podfile', () async {
testWithoutContext('runs pod install, if Podfile.lock is older than Podfile', () async {
pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
......@@ -605,12 +567,9 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
'LANG': 'en_US.UTF-8',
},
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('skips pod install, if nothing changed', () async {
testWithoutContext('skips pod install, if nothing changed', () async {
pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
......@@ -632,12 +591,9 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
));
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
testUsingContext('a failed pod install deletes Pods/Manifest.lock', () async {
testWithoutContext('a failed pod install deletes Pods/Manifest.lock', () async {
pretendPodIsInstalled();
projectUnderTest.ios.podfile
..createSync()
......@@ -671,9 +627,6 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
} on ToolExit {
expect(projectUnderTest.ios.podManifestLock.existsSync(), isFalse);
}
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
});
});
......@@ -692,7 +645,7 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
};
});
testUsingContext('succeeds, if specs repo is in CP_REPOS_DIR.', () async {
testWithoutContext('succeeds, if specs repo is in CP_REPOS_DIR.', () async {
pretendPodIsInstalled();
fs.file(fs.path.join('project', 'ios', 'Podfile'))
..createSync()
......@@ -707,10 +660,6 @@ Note: as of CocoaPods 1.0, `pod repo update` does not happen on `pod install` by
engineDir: 'engine/path',
);
expect(success, true);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => mockProcessManager,
Platform: () => FakePlatform(environment: environment),
});
});
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment