Run static analyzer during xctest (#3667)

This commit is contained in:
Jenn Magder
2021-03-02 19:27:48 -08:00
committed by GitHub
parent e1b3b296f2
commit 3ea78093bf
4 changed files with 61 additions and 256 deletions

View File

@ -14,8 +14,7 @@ import 'common.dart';
typedef void Print(Object object);
/// Lint the CocoaPod podspecs, run the static analyzer on iOS/macOS plugin
/// platform code, and run unit tests.
/// Lint the CocoaPod podspecs and run unit tests.
///
/// See https://guides.cocoapods.org/terminal/commands.html#pod_lib_lint.
class LintPodspecsCommand extends PluginCommand {
@ -35,10 +34,6 @@ class LintPodspecsCommand extends PluginCommand {
help:
'Do not pass --allow-warnings flag to "pod lib lint" for podspecs with this basename (example: plugins with known warnings)',
valueHelp: 'podspec_file_name');
argParser.addMultiOption('no-analyze',
help:
'Do not pass --analyze flag to "pod lib lint" for podspecs with this basename (example: plugins with known analyzer warnings)',
valueHelp: 'podspec_file_name');
}
@override
@ -102,25 +97,17 @@ class LintPodspecsCommand extends PluginCommand {
Future<bool> _lintPodspec(File podspec) async {
// Do not run the static analyzer on plugins with known analyzer issues.
final String podspecPath = podspec.path;
final bool runAnalyzer = !argResults['no-analyze']
.contains(p.basenameWithoutExtension(podspecPath));
final String podspecBasename = p.basename(podspecPath);
if (runAnalyzer) {
_print('Linting and analyzing $podspecBasename');
} else {
_print('Linting $podspecBasename');
}
_print('Linting $podspecBasename');
// Lint plugin as framework (use_frameworks!).
final ProcessResult frameworkResult = await _runPodLint(podspecPath,
runAnalyzer: runAnalyzer, libraryLint: true);
final ProcessResult frameworkResult = await _runPodLint(podspecPath, libraryLint: true);
_print(frameworkResult.stdout);
_print(frameworkResult.stderr);
// Lint plugin as library.
final ProcessResult libraryResult = await _runPodLint(podspecPath,
runAnalyzer: runAnalyzer, libraryLint: false);
final ProcessResult libraryResult = await _runPodLint(podspecPath, libraryLint: false);
_print(libraryResult.stdout);
_print(libraryResult.stderr);
@ -128,7 +115,7 @@ class LintPodspecsCommand extends PluginCommand {
}
Future<ProcessResult> _runPodLint(String podspecPath,
{bool runAnalyzer, bool libraryLint}) async {
{bool libraryLint}) async {
final bool allowWarnings = argResults['ignore-warnings']
.contains(p.basenameWithoutExtension(podspecPath));
final List<String> arguments = <String>[
@ -136,10 +123,10 @@ class LintPodspecsCommand extends PluginCommand {
'lint',
podspecPath,
if (allowWarnings) '--allow-warnings',
if (runAnalyzer) '--analyze',
if (libraryLint) '--use-libraries'
];
_print('Running "pod ${arguments.join(' ')}"');
return processRunner.run('pod', arguments,
workingDir: packagesDir, stdoutEncoding: utf8, stderrEncoding: utf8);
}

View File

@ -12,17 +12,15 @@ import 'package:path/path.dart' as p;
import 'common.dart';
const String _kiOSDestination = 'ios-destination';
const String _kTarget = 'target';
const String _kSkip = 'skip';
const String _kXcodeBuildCommand = 'xcodebuild';
const String _kXCRunCommand = 'xcrun';
const String _kFoundNoSimulatorsMessage =
'Cannot find any available simulators, tests failed';
/// The command to run iOS' XCTests in plugins, this should work for both XCUnitTest and XCUITest targets.
/// The tests target have to be added to the xcode project of the example app. Usually at "example/ios/Runner.xcodeproj".
/// The command takes a "-target" argument which has to match the target of the test target.
/// For information on how to add test target in an xcode project, see https://developer.apple.com/library/archive/documentation/ToolsLanguages/Conceptual/Xcode_Overview/UnitTesting.html
/// The command to run iOS XCTests in plugins, this should work for both XCUnitTest and XCUITest targets.
/// The tests target have to be added to the xcode project of the example app. Usually at "example/ios/Runner.xcworkspace".
/// The static analyzer is also run.
class XCTestCommand extends PluginCommand {
XCTestCommand(
Directory packagesDir,
@ -36,10 +34,6 @@ class XCTestCommand extends PluginCommand {
'this is passed to the `-destination` argument in xcodebuild command.\n'
'See https://developer.apple.com/library/archive/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-UNIT for details on how to specify the destination.',
);
argParser.addOption(_kTarget,
help: 'The test target.\n'
'This is the xcode project test target. This is passed to the `-scheme` argument in the xcodebuild command. \n'
'See https://developer.apple.com/library/archive/technotes/tn2339/_index.html#//apple_ref/doc/uid/DTS40014588-CH1-UNIT for details on how to specify the scheme');
argParser.addMultiOption(_kSkip,
help: 'Plugins to skip while running this command. \n');
}
@ -49,17 +43,10 @@ class XCTestCommand extends PluginCommand {
@override
final String description = 'Runs the xctests in the iOS example apps.\n\n'
'This command requires "flutter" to be in your path.';
'This command requires "flutter" and "xcrun" to be in your path.';
@override
Future<Null> run() async {
if (argResults[_kTarget] == null) {
// TODO(cyanglaz): Automatically find all the available testing schemes if this argument is not specified.
// https://github.com/flutter/flutter/issues/68419
print('--$_kTarget must be specified');
throw ToolExit(1);
}
String destination = argResults[_kiOSDestination];
if (destination == null) {
String simulatorId = await _findAvailableIphoneSimulator();
@ -72,7 +59,6 @@ class XCTestCommand extends PluginCommand {
checkSharding();
final String target = argResults[_kTarget];
final List<String> skipped = argResults[_kSkip];
List<String> failingPackages = <String>[];
@ -92,57 +78,14 @@ class XCTestCommand extends PluginCommand {
continue;
}
for (Directory example in getExamplesForPlugin(plugin)) {
// Look for the test scheme in the example app.
print('Look for target named: $_kTarget ...');
final List<String> findSchemeArgs = <String>[
'-project',
'ios/Runner.xcodeproj',
'-list',
'-json'
];
final String completeFindSchemeCommand =
'$_kXcodeBuildCommand ${findSchemeArgs.join(' ')}';
print(completeFindSchemeCommand);
final io.ProcessResult xcodeprojListResult = await processRunner
.run(_kXcodeBuildCommand, findSchemeArgs, workingDir: example);
if (xcodeprojListResult.exitCode != 0) {
print('Error occurred while running "$completeFindSchemeCommand":\n'
'${xcodeprojListResult.stderr}');
failingPackages.add(packageName);
print('\n\n');
continue;
// Running tests and static analyzer.
print('Running tests and analyzer for $packageName ...');
int exitCode = await _runTests(true, destination, example);
// 66 = there is no test target (this fails fast). Try again with just the analyzer.
if (exitCode == 66) {
print('Tests not found for $packageName, running analyzer only...');
exitCode = await _runTests(false, destination, example);
}
final String xcodeprojListOutput = xcodeprojListResult.stdout;
Map<String, dynamic> xcodeprojListOutputJson =
jsonDecode(xcodeprojListOutput);
if (!xcodeprojListOutputJson['project']['targets'].contains(target)) {
failingPackages.add(packageName);
print('$target not configured for $packageName, test failed.');
print(
'Please check the scheme for the test target if it matches the name $target.\n'
'If this plugin does not have an XCTest target, use the $_kSkip flag in the $name command to skip the plugin.');
print('\n\n');
continue;
}
// Found the scheme, running tests
print('Running XCTests:$target for $packageName ...');
final List<String> xctestArgs = <String>[
'test',
'-workspace',
'ios/Runner.xcworkspace',
'-scheme',
target,
'-destination',
destination,
'CODE_SIGN_IDENTITY=""',
'CODE_SIGNING_REQUIRED=NO'
];
final String completeTestCommand =
'$_kXcodeBuildCommand ${xctestArgs.join(' ')}';
print(completeTestCommand);
final int exitCode = await processRunner
.runAndStream(_kXcodeBuildCommand, xctestArgs, workingDir: example);
if (exitCode == 0) {
print('Successfully ran xctest for $packageName');
} else {
@ -164,6 +107,31 @@ class XCTestCommand extends PluginCommand {
}
}
Future<int> _runTests(bool runTests, String destination, Directory example) {
final List<String> xctestArgs = <String>[
_kXcodeBuildCommand,
if (runTests)
'test',
'analyze',
'-workspace',
'ios/Runner.xcworkspace',
'-configuration',
'Debug',
'-scheme',
'Runner',
'-destination',
destination,
'CODE_SIGN_IDENTITY=""',
'CODE_SIGNING_REQUIRED=NO',
'GCC_TREAT_WARNINGS_AS_ERRORS=YES',
];
final String completeTestCommand =
'$_kXCRunCommand ${xctestArgs.join(' ')}';
print(completeTestCommand);
return processRunner
.runAndStream(_kXCRunCommand, xctestArgs, workingDir: example, exitOnError: false);
}
Future<String> _findAvailableIphoneSimulator() async {
// Find the first available destination if not specified.
final List<String> findSimulatorsArguments = <String>[