Unverified Commit 778c2ce9 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Convert some code_signing_tests to testWithoutContext (#53142)

parent 47582223
......@@ -4,11 +4,14 @@
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:process/process.dart';
import 'package:quiver/strings.dart';
import '../application_package.dart';
import '../base/common.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/process.dart';
import '../convert.dart' show utf8;
import '../globals.dart' as globals;
......@@ -95,7 +98,9 @@ final RegExp _certificateOrganizationalUnitExtractionPattern = RegExp(r'OU=([a-z
/// Will return null if none are found, if the user cancels or if the Xcode
/// project has a development team set in the project's build settings.
Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
BuildableIOSApp iosApp,
@required BuildableIOSApp iosApp,
@required ProcessManager processManager,
@required Logger logger,
}) async {
final Map<String, String> buildSettings = await iosApp.project.buildSettings;
if (buildSettings == null) {
......@@ -105,7 +110,7 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
// If the user already has it set in the project build settings itself,
// continue with that.
if (isNotEmpty(buildSettings['DEVELOPMENT_TEAM'])) {
globals.printStatus(
logger.printStatus(
'Automatically signing iOS for device deployment using specified development '
'team in Xcode project: ${buildSettings['DEVELOPMENT_TEAM']}'
);
......@@ -118,6 +123,7 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
// If the user's environment is missing the tools needed to find and read
// certificates, abandon. Tools should be pre-equipped on macOS.
final ProcessUtils processUtils = ProcessUtils(processManager: processManager, logger: logger);
if (!await processUtils.exitsHappy(const <String>['which', 'security']) ||
!await processUtils.exitsHappy(const <String>['which', 'openssl'])) {
return null;
......@@ -133,7 +139,7 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
throwOnError: true,
)).stdout.trim();
} on ProcessException catch (error) {
globals.printTrace('Unexpected failure from find-identity: $error.');
logger.printTrace('Unexpected failure from find-identity: $error.');
return null;
}
......@@ -148,14 +154,14 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
.toSet() // Unique.
.toList();
final String signingIdentity = await _chooseSigningIdentity(validCodeSigningIdentities);
final String signingIdentity = await _chooseSigningIdentity(validCodeSigningIdentities, logger);
// If none are chosen, return null.
if (signingIdentity == null) {
return null;
}
globals.printStatus('Signing iOS app for device deployment using developer identity: "$signingIdentity"');
logger.printStatus('Signing iOS app for device deployment using developer identity: "$signingIdentity"');
final String signingCertificateId =
_securityFindIdentityCertificateCnExtractionPattern
......@@ -174,7 +180,7 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
throwOnError: true,
)).stdout.trim();
} on ProcessException catch (error) {
globals.printTrace("Couldn't find the certificate: $error.");
logger.printTrace("Couldn't find the certificate: $error.");
return null;
}
......@@ -198,10 +204,10 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
};
}
Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) async {
Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities, Logger logger) async {
// The user has no valid code signing identities.
if (validCodeSigningIdentities.isEmpty) {
globals.printError(noCertificatesInstruction, emphasis: true);
logger.printError(noCertificatesInstruction, emphasis: true);
throwToolExit('No development certificates available to code sign app for device deployment');
}
......@@ -214,10 +220,10 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a
if (savedCertChoice != null) {
if (validCodeSigningIdentities.contains(savedCertChoice)) {
globals.printStatus('Found saved certificate choice "$savedCertChoice". To clear, use "flutter config".');
logger.printStatus('Found saved certificate choice "$savedCertChoice". To clear, use "flutter config".');
return savedCertChoice;
} else {
globals.printError('Saved signing certificate "$savedCertChoice" is not a valid development certificate');
logger.printError('Saved signing certificate "$savedCertChoice" is not a valid development certificate');
}
}
......@@ -228,14 +234,14 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a
}
final int count = validCodeSigningIdentities.length;
globals.printStatus(
logger.printStatus(
'Multiple valid development certificates available (your choice will be saved):',
emphasis: true,
);
for (int i=0; i<count; i++) {
globals.printStatus(' ${i+1}) ${validCodeSigningIdentities[i]}', emphasis: true);
logger.printStatus(' ${i+1}) ${validCodeSigningIdentities[i]}', emphasis: true);
}
globals.printStatus(' a) Abort', emphasis: true);
logger.printStatus(' a) Abort', emphasis: true);
final String choice = await globals.terminal.promptForCharInput(
List<String>.generate(count, (int number) => '${number + 1}')
......@@ -243,14 +249,14 @@ Future<String> _chooseSigningIdentity(List<String> validCodeSigningIdentities) a
prompt: 'Please select a certificate for code signing',
displayAcceptedCharacters: true,
defaultChoiceIndex: 0, // Just pressing enter chooses the first one.
logger: globals.logger,
logger: logger,
);
if (choice == 'a') {
throwToolExit('Aborted. Code signing is required to build a deployable iOS app.');
} else {
final String selectedCert = validCodeSigningIdentities[int.parse(choice) - 1];
globals.printStatus('Certificate choice "$selectedCert" saved');
logger.printStatus('Certificate choice "$selectedCert" saved');
globals.config.setValue('ios-signing-cert', selectedCert);
return selectedCert;
}
......
......@@ -172,7 +172,11 @@ Future<XcodeBuildResult> buildXcodeProject({
Map<String, String> autoSigningConfigs;
if (codesign && buildForDevice) {
autoSigningConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
autoSigningConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: globals.processManager,
logger: globals.logger
);
}
final FlutterProject project = FlutterProject.current();
......
......@@ -5,10 +5,10 @@
import 'dart:async';
import 'dart:convert';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:mockito/mockito.dart';
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/config.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/terminal.dart';
......@@ -27,8 +27,10 @@ void main() {
IosProject mockIosProject;
BuildableIOSApp app;
AnsiTerminal testTerminal;
BufferLogger logger;
setUp(() async {
logger = BufferLogger.test();
mockProcessManager = MockProcessManager();
// Assume all binaries exist and are executable
when(mockProcessManager.canRun(any)).thenReturn(true);
......@@ -44,38 +46,48 @@ void main() {
app = await BuildableIOSApp.fromProject(mockIosProject);
});
testUsingContext('No auto-sign if Xcode project settings are not available', () async {
testWithoutContext('No auto-sign if Xcode project settings are not available', () async {
when(mockIosProject.buildSettings).thenReturn(null);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
expect(signingConfigs, isNull);
});
testUsingContext('No discovery if development team specified in Xcode project', () async {
testWithoutContext('No discovery if development team specified in Xcode project', () async {
when(mockIosProject.buildSettings).thenAnswer((_) {
return Future<Map<String, String>>.value(<String, String>{
'DEVELOPMENT_TEAM': 'abc',
});
});
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
expect(signingConfigs, isNull);
expect(testLogger.statusText, equals(
expect(logger.statusText, equals(
'Automatically signing iOS for device deployment using specified development team in Xcode project: abc\n'
));
}, overrides: <Type, Generator>{
OutputPreferences: () => OutputPreferences(wrapText: false),
});
testUsingContext('No auto-sign if security or openssl not available', () async {
testWithoutContext('No auto-sign if security or openssl not available', () async {
when(mockProcessManager.run(<String>['which', 'security']))
.thenAnswer((_) => Future<ProcessResult>.value(exitsFail));
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
expect(signingConfigs, isNull);
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('No valid code signing certificates shows instructions', () async {
when(mockIosProject.buildSettings).thenAnswer((_) {
return Future<Map<String, String>>.value(<String, String>{});
});
when(mockProcessManager.run(
<String>['which', 'security'],
workingDirectory: anyNamed('workingDirectory'),
......@@ -92,22 +104,17 @@ void main() {
workingDirectory: anyNamed('workingDirectory'),
)).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy));
Map<String, String> signingConfigs;
try {
signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
fail('No identity should throw tool error');
} on ToolExit {
expect(signingConfigs, isNull);
expect(testLogger.errorText, contains('No valid code signing certificates were found'));
}
expect(() async => await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
), throwsToolExit(message: 'No development certificates available to code sign app for device deployment'));
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
OutputPreferences: () => OutputPreferences(wrapText: false),
});
testUsingContext('Test single identity and certificate organization works', () async {
testWithoutContext('Test single identity and certificate organization works', () async {
when(mockProcessManager.run(
<String>['which', 'security'],
workingDirectory: anyNamed('workingDirectory'),
......@@ -161,20 +168,19 @@ void main() {
when(mockProcess.stderr).thenAnswer((Invocation invocation) => mockStdErr);
when(mockProcess.exitCode).thenAnswer((_) async => 0);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
expect(testLogger.statusText, contains('iPhone Developer: Profile 1 (1111AAAA11)'));
expect(testLogger.errorText, isEmpty);
expect(logger.statusText, contains('iPhone Developer: Profile 1 (1111AAAA11)'));
expect(logger.errorText, isEmpty);
verify(mockStdIn.write('This is a mock certificate'));
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '3333CCCC33'});
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
OutputPreferences: () => OutputPreferences(wrapText: false),
});
testUsingContext('Test single identity (Catalina format) and certificate organization works', () async {
testWithoutContext('Test single identity (Catalina format) and certificate organization works', () async {
when(mockProcessManager.run(
<String>['which', 'security'],
workingDirectory: anyNamed('workingDirectory'),
......@@ -230,20 +236,20 @@ void main() {
Map<String, String> signingConfigs;
try {
signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
} on Exception catch (e) {
// This should not throw
fail('Code signing threw: $e');
}
expect(testLogger.statusText, contains('Apple Development: Profile 1 (1111AAAA11)'));
expect(testLogger.errorText, isEmpty);
expect(logger.statusText, contains('Apple Development: Profile 1 (1111AAAA11)'));
expect(logger.errorText, isEmpty);
verify(mockStdIn.write('This is a mock certificate'));
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '3333CCCC33'});
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
OutputPreferences: () => OutputPreferences(wrapText: false),
});
testUsingContext('Test multiple identity and certificate organization works', () async {
......@@ -304,24 +310,27 @@ void main() {
when(mockOpenSslProcess.stderr).thenAnswer((Invocation invocation) => mockOpenSslStdErr);
when(mockOpenSslProcess.exitCode).thenAnswer((_) => Future<int>.value(0));
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
expect(
testLogger.statusText,
logger.statusText,
contains('Please select a certificate for code signing [<bold>1</bold>|2|3|a]: 3'),
);
expect(
testLogger.statusText,
logger.statusText,
contains('Signing iOS app for device deployment using developer identity: "iPhone Developer: Profile 3 (3333CCCC33)"'),
);
expect(testLogger.errorText, isEmpty);
expect(logger.errorText, isEmpty);
verify(mockOpenSslStdIn.write('This is a mock certificate'));
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '4444DDDD44'});
verify(globals.config.setValue('ios-signing-cert', 'iPhone Developer: Profile 3 (3333CCCC33)'));
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Config: () => mockConfig,
AnsiTerminal: () => testTerminal,
OutputPreferences: () => OutputPreferences(wrapText: false),
......@@ -386,18 +395,21 @@ void main() {
when(mockOpenSslProcess.stderr).thenAnswer((Invocation invocation) => mockOpenSslStdErr);
when(mockOpenSslProcess.exitCode).thenAnswer((_) => Future<int>.value(0));
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
expect(
testLogger.statusText,
logger.statusText,
contains('Signing iOS app for device deployment using developer identity: "iPhone Developer: Profile 1 (1111AAAA11)"'),
);
expect(testLogger.errorText, isEmpty);
expect(logger.errorText, isEmpty);
verify(mockOpenSslStdIn.write('This is a mock certificate'));
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '5555EEEE55'});
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Config: () => mockConfig,
AnsiTerminal: () => testTerminal,
OutputPreferences: () => OutputPreferences(wrapText: false),
......@@ -460,22 +472,25 @@ void main() {
when(mockOpenSslProcess.exitCode).thenAnswer((_) => Future<int>.value(0));
when<String>(mockConfig.getValue('ios-signing-cert') as String).thenReturn('iPhone Developer: Profile 3 (3333CCCC33)');
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
expect(
testLogger.statusText,
logger.statusText,
contains('Found saved certificate choice "iPhone Developer: Profile 3 (3333CCCC33)". To clear, use "flutter config"'),
);
expect(
testLogger.statusText,
logger.statusText,
contains('Signing iOS app for device deployment using developer identity: "iPhone Developer: Profile 3 (3333CCCC33)"'),
);
expect(testLogger.errorText, isEmpty);
expect(logger.errorText, isEmpty);
verify(mockOpenSslStdIn.write('This is a mock certificate'));
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '4444DDDD44'});
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Config: () => mockConfig,
OutputPreferences: () => OutputPreferences(wrapText: false),
});
......@@ -540,26 +555,29 @@ void main() {
when(mockOpenSslProcess.exitCode).thenAnswer((_) => Future<int>.value(0));
when<String>(mockConfig.getValue('ios-signing-cert') as String).thenReturn('iPhone Developer: Invalid Profile');
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
expect(
testLogger.errorText.replaceAll('\n', ' '),
logger.errorText.replaceAll('\n', ' '),
contains('Saved signing certificate "iPhone Developer: Invalid Profile" is not a valid development certificate'),
);
expect(
testLogger.statusText,
logger.statusText,
contains('Certificate choice "iPhone Developer: Profile 3 (3333CCCC33)"'),
);
expect(signingConfigs, <String, String>{'DEVELOPMENT_TEAM': '4444DDDD44'});
verify(globals.config.setValue('ios-signing-cert', 'iPhone Developer: Profile 3 (3333CCCC33)'));
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Config: () => mockConfig,
AnsiTerminal: () => testTerminal,
});
testUsingContext('find-identity failure', () async {
testWithoutContext('find-identity failure', () async {
when(mockProcessManager.run(
<String>['which', 'security'],
workingDirectory: anyNamed('workingDirectory'),
......@@ -578,13 +596,12 @@ void main() {
ProcessResult(0, 1, '', '')
));
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
expect(signingConfigs, isNull);
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Config: () => mockConfig,
AnsiTerminal: () => testTerminal,
});
testUsingContext('find-certificate failure', () async {
......@@ -622,11 +639,14 @@ void main() {
ProcessResult(1, 1, '', '' ))
);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(
iosApp: app,
processManager: mockProcessManager,
logger: logger,
);
expect(signingConfigs, isNull);
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Config: () => mockConfig,
AnsiTerminal: () => testTerminal,
});
......
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