Catch exceptions thrown by runChecked* when possible (#36109)

This commit is contained in:
Emmanuel Garcia 2019-07-18 10:45:37 -07:00 committed by GitHub
parent 7b0cc5051b
commit 5a34e7981e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 242 additions and 51 deletions

View file

@ -378,10 +378,14 @@ class AndroidDevice extends Device {
printError('$installResult');
return false;
}
await runAdbCheckedAsync(<String>[
'shell', 'echo', '-n', _getSourceSha1(app), '>', _getDeviceSha1Path(app),
]);
try {
await runAdbCheckedAsync(<String>[
'shell', 'echo', '-n', _getSourceSha1(app), '>', _getDeviceSha1Path(app),
]);
} on ProcessException catch (error) {
printError('adb shell failed to write the SHA hash: $error.');
return false;
}
return true;
}
@ -390,7 +394,13 @@ class AndroidDevice extends Device {
if (!await _checkForSupportedAdbVersion() || !await _checkForSupportedAndroidVersion())
return false;
final String uninstallOut = (await runCheckedAsync(adbCommandForDevice(<String>['uninstall', app.id]))).stdout;
String uninstallOut;
try {
uninstallOut = (await runCheckedAsync(adbCommandForDevice(<String>['uninstall', app.id]))).stdout;
} catch (error) {
printError('adb uninstall failed: $error');
return false;
}
final RegExp failureExp = RegExp(r'^Failure.*$', multiLine: true);
final String failure = failureExp.stringMatch(uninstallOut);
if (failure != null) {
@ -615,13 +625,18 @@ class AndroidDevice extends Device {
static final RegExp _timeRegExp = RegExp(r'^\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}', multiLine: true);
/// Return the most recent timestamp in the Android log or null if there is
/// Return the most recent timestamp in the Android log or [null] if there is
/// no available timestamp. The format can be passed to logcat's -T option.
String get lastLogcatTimestamp {
final String output = runAdbCheckedSync(<String>[
'shell', '-x', 'logcat', '-v', 'time', '-t', '1',
]);
String output;
try {
output = runAdbCheckedSync(<String>[
'shell', '-x', 'logcat', '-v', 'time', '-t', '1',
]);
} catch (error) {
printError('Failed to extract the most recent timestamp from the Android log: $error.');
return null;
}
final Match timeMatch = _timeRegExp.firstMatch(output);
return timeMatch?.group(0);
}
@ -940,9 +955,15 @@ class _AndroidDevicePortForwarder extends DevicePortForwarder {
List<ForwardedPort> get forwardedPorts {
final List<ForwardedPort> ports = <ForwardedPort>[];
final String stdout = runCheckedSync(device.adbCommandForDevice(
<String>['forward', '--list']
));
String stdout;
try {
stdout = runCheckedSync(device.adbCommandForDevice(
<String>['forward', '--list']
));
} catch (error) {
printError('Failed to list forwarded ports: $error.');
return ports;
}
final List<String> lines = LineSplitter.split(stdout).toList();
for (String line in lines) {

View file

@ -114,16 +114,21 @@ class AndroidApk extends ApplicationPackage {
return null;
}
final List<String> aaptArgs = <String>[
aaptPath,
'dump',
'xmltree',
apk.path,
'AndroidManifest.xml',
];
String apptStdout;
try {
apptStdout = runCheckedSync(<String>[
aaptPath,
'dump',
'xmltree',
apk.path,
'AndroidManifest.xml',
]);
} catch (error) {
printError('Failed to extract manifest from APK: $error.');
return null;
}
final ApkManifestData data = ApkManifestData
.parseFromXmlDump(runCheckedSync(aaptArgs));
final ApkManifestData data = ApkManifestData.parseFromXmlDump(apptStdout);
if (data == null) {
printError('Unable to read manifest info from ${apk.path}.');

View file

@ -8,6 +8,7 @@ import 'package:meta/meta.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/os.dart';
import '../base/process.dart';
import '../cache.dart';
@ -98,8 +99,14 @@ class UpgradeCommandRunner {
'git', 'status', '-s'
], workingDirectory: Cache.flutterRoot);
return result.stdout.trim().isNotEmpty;
} catch (e) {
throwToolExit('git status failed: $e');
} on ProcessException catch (error) {
throwToolExit(
'The tool could not verify the status of the current flutter checkout. '
'This might be due to git not being installed or an internal error.'
'If it is okay to ignore potential local changes, then re-run this'
'command with --force.'
'\nError: $error.'
);
}
return false;
}
@ -132,11 +139,17 @@ class UpgradeCommandRunner {
} else {
tag = 'v${gitTagVersion.x}.${gitTagVersion.y}.${gitTagVersion.z}';
}
final RunResult runResult = await runCheckedAsync(<String>[
'git', 'reset', '--hard', tag,
], workingDirectory: Cache.flutterRoot);
if (runResult.exitCode != 0) {
throwToolExit('Failed to restore branch from hotfix.');
try {
await runCheckedAsync(<String>[
'git', 'reset', '--hard', tag,
], workingDirectory: Cache.flutterRoot);
} on ProcessException catch (error) {
throwToolExit(
'Unable to upgrade Flutter: The tool could not update to the version $tag. '
'This may be due to git not being installed or an internal error.'
'Please ensure that git is installed on your computer and retry again.'
'\nError: $error.'
);
}
}

View file

@ -6,6 +6,7 @@ import 'dart:async';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../base/os.dart';
import '../base/process.dart';
import '../base/version.dart';
@ -34,10 +35,19 @@ class VersionCommand extends FlutterCommand {
Version minSupportedVersion = Version.parse('1.2.1');
Future<List<String>> getTags() async {
final RunResult runResult = await runCheckedAsync(
<String>['git', 'tag', '-l', 'v*', '--sort=-creatordate'],
workingDirectory: Cache.flutterRoot,
);
RunResult runResult;
try {
runResult = await runCheckedAsync(
<String>['git', 'tag', '-l', 'v*', '--sort=-creatordate'],
workingDirectory: Cache.flutterRoot,
);
} on ProcessException catch (error) {
throwToolExit(
'Unable to get the tags. '
'This might be due to git not being installed or an internal error'
'\nError: $error.'
);
}
return runResult.toString().split('\n');
}

View file

@ -120,7 +120,16 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
const List<String> findIdentityCommand =
<String>['security', 'find-identity', '-p', 'codesigning', '-v'];
final List<String> validCodeSigningIdentities = runCheckedSync(findIdentityCommand)
String findIdentityStdout;
try {
findIdentityStdout = runCheckedSync(findIdentityCommand);
} catch (error) {
printTrace('Unexpected failure from find-identity: $error.');
return null;
}
final List<String> validCodeSigningIdentities = findIdentityStdout
.split('\n')
.map<String>((String outputLine) {
return _securityFindIdentityDeveloperIdentityExtractionPattern
@ -148,12 +157,18 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
if (signingCertificateId == null)
return null;
final String signingCertificate = runCheckedSync(
<String>['security', 'find-certificate', '-c', signingCertificateId, '-p']
);
String signingCertificateStdout;
try {
signingCertificateStdout = runCheckedSync(
<String>['security', 'find-certificate', '-c', signingCertificateId, '-p']
);
} catch (error) {
printTrace('Couldn\'t find the certificate: $error.');
return null;
}
final Process opensslProcess = await runCommand(const <String>['openssl', 'x509', '-subject']);
await (opensslProcess.stdin..write(signingCertificate)).close();
await (opensslProcess.stdin..write(signingCertificateStdout)).close();
final String opensslOutput = await utf8.decodeStream(opensslProcess.stdout);
// Fire and forget discard of the stderr stream so we don't hold onto resources.

View file

@ -174,15 +174,20 @@ class XcodeProjectInterpreter {
}
Map<String, String> getBuildSettings(String projectPath, String target) {
final String out = runCheckedSync(<String>[
_executable,
'-project',
fs.path.absolute(projectPath),
'-target',
target,
'-showBuildSettings',
], workingDirectory: projectPath);
return parseXcodeBuildSettings(out);
try {
final String out = runCheckedSync(<String>[
_executable,
'-project',
fs.path.absolute(projectPath),
'-target',
target,
'-showBuildSettings',
], workingDirectory: projectPath);
return parseXcodeBuildSettings(out);
} catch(error) {
printTrace('Unexpected failure to get the build settings: $error.');
return const <String, String>{};
}
}
Future<XcodeProjectInfo> getInfo(String projectPath) async {

View file

@ -10,6 +10,7 @@ import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_console.dart';
import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/application_package.dart';
import 'package:flutter_tools/src/base/config.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
@ -365,7 +366,7 @@ flutter:
testUsingContext('returns the generated host port from stdout', () async {
when(mockProcessManager.run(argThat(contains('forward'))))
.thenAnswer((_) async => ProcessResult(0, 0, '456', ''));
.thenAnswer((_) async => ProcessResult(0, 0, '456', ''));
expect(await forwarder.forward(123), equals(456));
}, overrides: <Type, Generator>{
@ -374,7 +375,7 @@ flutter:
testUsingContext('returns the supplied host port when stdout is empty', () async {
when(mockProcessManager.run(argThat(contains('forward'))))
.thenAnswer((_) async => ProcessResult(0, 0, '', ''));
.thenAnswer((_) async => ProcessResult(0, 0, '', ''));
expect(await forwarder.forward(123, hostPort: 456), equals(456));
}, overrides: <Type, Generator>{
@ -383,7 +384,7 @@ flutter:
testUsingContext('returns the supplied host port when stdout is the host port', () async {
when(mockProcessManager.run(argThat(contains('forward'))))
.thenAnswer((_) async => ProcessResult(0, 0, '456', ''));
.thenAnswer((_) async => ProcessResult(0, 0, '456', ''));
expect(await forwarder.forward(123, hostPort: 456), equals(456));
}, overrides: <Type, Generator>{
@ -392,12 +393,34 @@ flutter:
testUsingContext('throws an error when stdout is not blank nor the host port', () async {
when(mockProcessManager.run(argThat(contains('forward'))))
.thenAnswer((_) async => ProcessResult(0, 0, '123456', ''));
.thenAnswer((_) async => ProcessResult(0, 0, '123456', ''));
expect(forwarder.forward(123, hostPort: 456), throwsA(isInstanceOf<ProcessException>()));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
testUsingContext('forwardedPorts returns empty list when forward failed', () {
when(mockProcessManager.runSync(argThat(contains('forward'))))
.thenReturn(ProcessResult(0, 1, '', ''));
expect(forwarder.forwardedPorts, equals(const <ForwardedPort>[]));
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
});
group('lastLogcatTimestamp', () {
final ProcessManager mockProcessManager = MockProcessManager();
final AndroidDevice device = AndroidDevice('1234');
testUsingContext('returns null if shell command failed', () async {
when(mockProcessManager.runSync(argThat(contains('logcat'))))
.thenReturn(ProcessResult(0, 1, '', ''));
expect(device.lastLogcatTimestamp, isNull);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
});
});
}
@ -601,3 +624,10 @@ class MockUnresponsiveAndroidConsoleSocket extends Mock implements Socket {
@override
void add(List<int> data) {}
}
class AndroidPackageTest extends ApplicationPackage {
AndroidPackageTest() : super(id: 'app-id');
@override
String get name => 'app-package';
}

View file

@ -129,6 +129,15 @@ void main() {
),
);
}, overrides: overrides);
testUsingContext('returns null when failed to extract manifest', () async {
final AndroidSdkVersion sdkVersion = MockitoAndroidSdkVersion();
when(sdk.latestVersion).thenReturn(sdkVersion);
when(mockProcessManager.runSync(argThat(contains('logcat'))))
.thenReturn(ProcessResult(0, 1, '', ''));
expect(AndroidApk.fromApk(null), isNull);
}, overrides: overrides);
});
group('ApkManifestData', () {

View file

@ -6,6 +6,7 @@ import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/version.dart';
@ -59,12 +60,29 @@ void main() {
}, overrides: <Type, Generator>{
ProcessManager: () => MockProcessManager(),
});
testUsingContext('exit tool if can\'t get the tags', () async {
final VersionCommand command = VersionCommand();
try {
await command.getTags();
fail('ToolExit expected');
} catch(e) {
expect(e, isInstanceOf<ToolExit>());
}
}, overrides: <Type, Generator>{
ProcessManager: () => MockProcessManager(failGitTag: true),
});
});
}
class MockProcessManager extends Mock implements ProcessManager {
MockProcessManager({ this.failGitTag = false });
String version = '';
bool failGitTag;
@override
Future<ProcessResult> run(
List<dynamic> command, {
@ -76,6 +94,9 @@ class MockProcessManager extends Mock implements ProcessManager {
Encoding stderrEncoding = systemEncoding,
}) async {
if (command[0] == 'git' && command[1] == 'tag') {
if (failGitTag) {
return ProcessResult(0, 1, '', '');
}
return ProcessResult(0, 0, 'v10.0.0\r\nv20.0.0', '');
}
if (command[0] == 'git' && command[1] == 'checkout') {

View file

@ -440,6 +440,62 @@ void main() {
Config: () => mockConfig,
AnsiTerminal: () => testTerminal,
});
testUsingContext('find-identity failure', () async {
when(mockProcessManager.runSync(<String>['which', 'security']))
.thenReturn(exitsHappy);
when(mockProcessManager.runSync(<String>['which', 'openssl']))
.thenReturn(exitsHappy);
when(mockProcessManager.runSync(
argThat(contains('find-identity')),
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
)).thenReturn(ProcessResult(0, 1, '', ''));
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
expect(signingConfigs, isNull);
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Config: () => mockConfig,
AnsiTerminal: () => testTerminal,
});
testUsingContext('find-certificate failure', () async {
when(mockProcessManager.runSync(<String>['which', 'security']))
.thenReturn(exitsHappy);
when(mockProcessManager.runSync(<String>['which', 'openssl']))
.thenReturn(exitsHappy);
when(mockProcessManager.runSync(
argThat(contains('find-identity')),
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
)).thenReturn(ProcessResult(
1, // pid
0, // exitCode
'''
1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)"
2) da4b9237bacccdf19c0760cab7aec4a8359010b0 "iPhone Developer: Profile 2 (2222BBBB22)"
3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)"
3 valid identities found''',
'',
));
mockTerminalStdInStream =
Stream<String>.fromFuture(Future<String>.value('3'));
when(mockProcessManager.runSync(
<String>['security', 'find-certificate', '-c', '3333CCCC33', '-p'],
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
)).thenReturn(ProcessResult(1, 1, '', '' ));
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
expect(signingConfigs, isNull);
},
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Config: () => mockConfig,
AnsiTerminal: () => testTerminal,
});
});
}

View file

@ -143,6 +143,12 @@ void main() {
.thenReturn(ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', ''));
expect(xcodeProjectInterpreter.isInstalled, isTrue);
});
testUsingOsxContext('build settings is empty when xcodebuild failed to get the build settings', () {
when(mockProcessManager.runSync(argThat(contains(xcodebuild))))
.thenReturn(ProcessResult(0, 1, '', ''));
expect(xcodeProjectInterpreter.getBuildSettings('', ''), const <String, String>{});
});
});
group('Xcode project properties', () {
test('properties from default project can be parsed', () {