[flutter_tool] process.dart cleanup (#39899)
diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart index 327197e..d42b4c6 100644 --- a/packages/flutter_tools/lib/src/android/android_device.dart +++ b/packages/flutter_tools/lib/src/android/android_device.dart
@@ -227,11 +227,14 @@ String workingDirectory, bool allowReentrantFlutter = false, Map<String, String> environment}) { - return runCheckedSync(adbCommandForDevice(params), workingDirectory: workingDirectory, - allowReentrantFlutter: allowReentrantFlutter, - environment: environment, - whiteListFailures: allowHeapCorruptionOnWindows - ); + return processUtils.runSync( + adbCommandForDevice(params), + throwOnError: true, + workingDirectory: workingDirectory, + allowReentrantFlutter: allowReentrantFlutter, + environment: environment, + whiteListFailures: allowHeapCorruptionOnWindows, + ).stdout.trim(); } Future<RunResult> runAdbCheckedAsync( @@ -239,9 +242,13 @@ String workingDirectory, bool allowReentrantFlutter = false, }) async { - return runCheckedAsync(adbCommandForDevice(params), workingDirectory: workingDirectory, - allowReentrantFlutter: allowReentrantFlutter, - whiteListFailures: allowHeapCorruptionOnWindows); + return processUtils.run( + adbCommandForDevice(params), + throwOnError: true, + workingDirectory: workingDirectory, + allowReentrantFlutter: allowReentrantFlutter, + whiteListFailures: allowHeapCorruptionOnWindows, + ); } bool _isValidAdbVersion(String adbVersion) { @@ -268,13 +275,18 @@ } Future<bool> _checkForSupportedAdbVersion() async { - if (androidSdk == null) + if (androidSdk == null) { return false; + } try { - final RunResult adbVersion = await runCheckedAsync(<String>[getAdbPath(androidSdk), 'version']); - if (_isValidAdbVersion(adbVersion.stdout)) + final RunResult adbVersion = await processUtils.run( + <String>[getAdbPath(androidSdk), 'version'], + throwOnError: true, + ); + if (_isValidAdbVersion(adbVersion.stdout)) { return true; + } printError('The ADB at "${getAdbPath(androidSdk)}" is too old; please install version 1.0.39 or later.'); } catch (error, trace) { printError('Error running ADB: $error', stackTrace: trace); @@ -289,7 +301,10 @@ // output lines like this, which we want to ignore: // adb server is out of date. killing.. // * daemon started successfully * - await runCheckedAsync(<String>[getAdbPath(androidSdk), 'start-server']); + await processUtils.run( + <String>[getAdbPath(androidSdk), 'start-server'], + throwOnError: true, + ); // Sample output: '22' final String sdkVersion = await _getProperty('ro.build.version.sdk'); @@ -320,7 +335,8 @@ } Future<String> _getDeviceApkSha1(ApplicationPackage app) async { - final RunResult result = await runAsync(adbCommandForDevice(<String>['shell', 'cat', _getDeviceSha1Path(app)])); + final RunResult result = await processUtils.run( + adbCommandForDevice(<String>['shell', 'cat', _getDeviceSha1Path(app)])); return result.stdout; } @@ -363,7 +379,8 @@ return false; final Status status = logger.startProgress('Installing ${fs.path.relative(apk.file.path)}...', timeout: timeoutConfiguration.slowOperation); - final RunResult installResult = await runAsync(adbCommandForDevice(<String>['install', '-t', '-r', apk.file.path])); + final RunResult installResult = await processUtils.run( + adbCommandForDevice(<String>['install', '-t', '-r', apk.file.path])); status.stop(); // Some versions of adb exit with exit code 0 even on failure :( // Parsing the output to check for failures. @@ -396,7 +413,11 @@ String uninstallOut; try { - uninstallOut = (await runCheckedAsync(adbCommandForDevice(<String>['uninstall', app.id]))).stdout; + final RunResult uninstallResult = await processUtils.run( + adbCommandForDevice(<String>['uninstall', app.id]), + throwOnError: true, + ); + uninstallOut = uninstallResult.stdout; } catch (error) { printError('adb uninstall failed: $error'); return false; @@ -603,13 +624,13 @@ @override Future<bool> stopApp(ApplicationPackage app) { final List<String> command = adbCommandForDevice(<String>['shell', 'am', 'force-stop', app.id]); - return runCommandAndStreamOutput(command).then<bool>( + return processUtils.stream(command).then<bool>( (int exitCode) => exitCode == 0 || allowHeapCorruptionOnWindows(exitCode)); } @override void clearLogs() { - runSync(adbCommandForDevice(<String>['logcat', '-c'])); + processUtils.runSync(adbCommandForDevice(<String>['logcat', '-c'])); } @override @@ -650,7 +671,10 @@ Future<void> takeScreenshot(File outputFile) async { const String remotePath = '/data/local/tmp/flutter_screenshot.png'; await runAdbCheckedAsync(<String>['shell', 'screencap', '-p', remotePath]); - await runCheckedAsync(adbCommandForDevice(<String>['pull', remotePath, outputFile.path])); + await processUtils.run( + adbCommandForDevice(<String>['pull', remotePath, outputFile.path]), + throwOnError: true, + ); await runAdbCheckedAsync(<String>['shell', 'rm', remotePath]); } @@ -675,7 +699,10 @@ return <AndroidDevice>[]; String text; try { - text = runSync(<String>[adbPath, 'devices', '-l']); + text = processUtils.runSync( + <String>[adbPath, 'devices', '-l'], + throwOnError: true, + ).stdout.trim(); } on ArgumentError catch (exception) { throwToolExit('Unable to find "adb", check your Android SDK installation and ' 'ANDROID_HOME environment variable: ${exception.message}'); @@ -694,7 +721,7 @@ if (adbPath == null) return <String>[]; - final RunResult result = await runAsync(<String>[adbPath, 'devices', '-l']); + final RunResult result = await processUtils.run(<String>[adbPath, 'devices', '-l']); if (result.exitCode != 0) { return <String>[]; } else { @@ -816,11 +843,12 @@ // Start the adb logcat process. final List<String> args = <String>['shell', '-x', 'logcat', '-v', 'time']; final String lastTimestamp = device.lastLogcatTimestamp; - if (lastTimestamp != null) + if (lastTimestamp != null) { _timeOrigin = _adbTimestampToDateTime(lastTimestamp); - else + } else { _timeOrigin = null; - runCommand(device.adbCommandForDevice(args)).then<void>((Process process) { + } + processUtils.start(device.adbCommandForDevice(args)).then<void>((Process process) { _process = process; // We expect logcat streams to occasionally contain invalid utf-8, // see: https://github.com/flutter/flutter/pull/8864. @@ -959,33 +987,37 @@ String stdout; try { - stdout = runCheckedSync(device.adbCommandForDevice( - <String>['forward', '--list'] - )); - } catch (error) { + stdout = processUtils.runSync( + device.adbCommandForDevice(<String>['forward', '--list']), + throwOnError: true, + ).stdout.trim(); + } on ProcessException catch (error) { printError('Failed to list forwarded ports: $error.'); return ports; } final List<String> lines = LineSplitter.split(stdout).toList(); for (String line in lines) { - if (line.startsWith(device.id)) { - final List<String> splitLine = line.split('tcp:'); - - // Sanity check splitLine. - if (splitLine.length != 3) - continue; - - // Attempt to extract ports. - final int hostPort = _extractPort(splitLine[1]); - final int devicePort = _extractPort(splitLine[2]); - - // Failed, skip. - if (hostPort == null || devicePort == null) - continue; - - ports.add(ForwardedPort(hostPort, devicePort)); + if (!line.startsWith(device.id)) { + continue; } + final List<String> splitLine = line.split('tcp:'); + + // Sanity check splitLine. + if (splitLine.length != 3) { + continue; + } + + // Attempt to extract ports. + final int hostPort = _extractPort(splitLine[1]); + final int devicePort = _extractPort(splitLine[2]); + + // Failed, skip. + if (hostPort == null || devicePort == null) { + continue; + } + + ports.add(ForwardedPort(hostPort, devicePort)); } return ports; @@ -994,23 +1026,35 @@ @override Future<int> forward(int devicePort, { int hostPort }) async { hostPort ??= 0; - final RunResult process = await runCheckedAsync(device.adbCommandForDevice( - <String>['forward', 'tcp:$hostPort', 'tcp:$devicePort'] - )); + final List<String> forwardCommand = <String>[ + 'forward', + 'tcp:$hostPort', + 'tcp:$devicePort', + ]; + final RunResult process = await processUtils.run( + device.adbCommandForDevice(forwardCommand), + throwOnError: true, + ); - if (process.stderr.isNotEmpty) + if (process.stderr.isNotEmpty) { process.throwException('adb returned error:\n${process.stderr}'); + } if (process.exitCode != 0) { - if (process.stdout.isNotEmpty) + if (process.stdout.isNotEmpty) { process.throwException('adb returned error:\n${process.stdout}'); + } process.throwException('adb failed without a message'); } if (hostPort == 0) { - if (process.stdout.isEmpty) + if (process.stdout.isEmpty) { process.throwException('adb did not report forwarded port'); - hostPort = int.tryParse(process.stdout) ?? (throw 'adb returned invalid port number:\n${process.stdout}'); + } + hostPort = int.tryParse(process.stdout); + if (hostPort == null) { + process.throwException('adb returned invalid port number:\n${process.stdout}'); + } } else { // stdout may be empty or the port we asked it to forward, though it's // not documented (or obvious) what triggers each case. @@ -1025,8 +1069,9 @@ // To cover all cases, we accept the output being either empty or exactly // the port number, but treat any other output as probably being an error // message. - if (process.stdout.isNotEmpty && process.stdout.trim() != '$hostPort') + if (process.stdout.isNotEmpty && process.stdout.trim() != '$hostPort') { process.throwException('adb returned error:\n${process.stdout}'); + } } return hostPort; @@ -1034,8 +1079,14 @@ @override Future<void> unforward(ForwardedPort forwardedPort) async { - await runCheckedAsync(device.adbCommandForDevice( - <String>['forward', '--remove', 'tcp:${forwardedPort.hostPort}'] - )); + final List<String> unforwardCommand = <String>[ + 'forward', + '--remove', + 'tcp:${forwardedPort.hostPort}', + ]; + await processUtils.run( + device.adbCommandForDevice(unforwardCommand), + throwOnError: true, + ); } }
diff --git a/packages/flutter_tools/lib/src/android/android_emulator.dart b/packages/flutter_tools/lib/src/android/android_emulator.dart index 65a2118..4b8f7e3 100644 --- a/packages/flutter_tools/lib/src/android/android_emulator.dart +++ b/packages/flutter_tools/lib/src/android/android_emulator.dart
@@ -9,8 +9,7 @@ import '../android/android_sdk.dart'; import '../android/android_workflow.dart'; import '../base/file_system.dart'; -import '../base/io.dart'; -import '../base/process_manager.dart'; +import '../base/process.dart'; import '../device.dart'; import '../emulator.dart'; import 'android_sdk.dart'; @@ -50,13 +49,10 @@ @override Future<void> launch() async { - final Future<void> launchResult = - processManager.run(<String>[getEmulatorPath(), '-avd', id]) - .then((ProcessResult runResult) { - if (runResult.exitCode != 0) { - throw '${runResult.stdout}\n${runResult.stderr}'.trimRight(); - } - }); + final Future<void> launchResult = processUtils.run( + <String>[getEmulatorPath(), '-avd', id], + throwOnError: true, + ); // The emulator continues running on a successful launch, so if it hasn't // quit within 3 seconds we assume that's a success and just return. This // means that on a slow machine, a failure that takes more than three @@ -75,7 +71,8 @@ return <AndroidEmulator>[]; } - final String listAvdsOutput = processManager.runSync(<String>[emulatorPath, '-list-avds']).stdout; + final String listAvdsOutput = processUtils.runSync( + <String>[emulatorPath, '-list-avds']).stdout.trim(); final List<AndroidEmulator> emulators = <AndroidEmulator>[]; if (listAvdsOutput != null) {
diff --git a/packages/flutter_tools/lib/src/android/android_sdk.dart b/packages/flutter_tools/lib/src/android/android_sdk.dart index 65f40aa..fe13948 100644 --- a/packages/flutter_tools/lib/src/android/android_sdk.dart +++ b/packages/flutter_tools/lib/src/android/android_sdk.dart
@@ -7,7 +7,6 @@ import '../base/common.dart'; import '../base/context.dart'; import '../base/file_system.dart'; -import '../base/io.dart' show ProcessResult; import '../base/os.dart'; import '../base/platform.dart'; import '../base/process.dart'; @@ -526,9 +525,9 @@ /// First try Java bundled with Android Studio, then sniff JAVA_HOME, then fallback to PATH. static String findJavaBinary() { - - if (android_studio.javaPath != null) + if (android_studio.javaPath != null) { return fs.path.join(android_studio.javaPath, 'bin', 'java'); + } final String javaHomeEnv = platform.environment[_javaHomeEnvironmentVariable]; if (javaHomeEnv != null) { @@ -540,7 +539,11 @@ // See: http://stackoverflow.com/questions/14292698/how-do-i-check-if-the-java-jdk-is-installed-on-mac. if (platform.isMacOS) { try { - final String javaHomeOutput = runCheckedSync(<String>['/usr/libexec/java_home'], hideStdout: true); + final String javaHomeOutput = processUtils.runSync( + <String>['/usr/libexec/java_home'], + throwOnError: true, + hideStdout: true, + ).stdout.trim(); if (javaHomeOutput != null) { final List<String> javaHomeOutputSplit = javaHomeOutput.split('\n'); if ((javaHomeOutputSplit != null) && (javaHomeOutputSplit.isNotEmpty)) { @@ -575,7 +578,10 @@ String get sdkManagerVersion { if (!processManager.canRun(sdkManagerPath)) throwToolExit('Android sdkmanager not found. Update to the latest Android SDK to resolve this.'); - final ProcessResult result = processManager.runSync(<String>[sdkManagerPath, '--version'], environment: sdkManagerEnv); + final RunResult result = processUtils.runSync( + <String>[sdkManagerPath, '--version'], + environment: sdkManagerEnv, + ); if (result.exitCode != 0) { printTrace('sdkmanager --version failed: exitCode: ${result.exitCode} stdout: ${result.stdout} stderr: ${result.stderr}'); return null;
diff --git a/packages/flutter_tools/lib/src/android/android_studio.dart b/packages/flutter_tools/lib/src/android/android_studio.dart index 2fae44a..82e6b4b 100644 --- a/packages/flutter_tools/lib/src/android/android_studio.dart +++ b/packages/flutter_tools/lib/src/android/android_studio.dart
@@ -5,8 +5,8 @@ import '../base/common.dart'; import '../base/context.dart'; import '../base/file_system.dart'; -import '../base/io.dart'; import '../base/platform.dart'; +import '../base/process.dart'; import '../base/process_manager.dart'; import '../base/version.dart'; import '../globals.dart'; @@ -290,7 +290,7 @@ if (!processManager.canRun(javaExecutable)) { _validationMessages.add('Unable to find bundled Java version.'); } else { - final ProcessResult result = processManager.runSync(<String>[javaExecutable, '-version']); + final RunResult result = processUtils.runSync(<String>[javaExecutable, '-version']); if (result.exitCode == 0) { final List<String> versionLines = result.stderr.split('\n'); final String javaVersion = versionLines.length >= 2 ? versionLines[1] : versionLines[0];
diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart index 8f0a852..7b6b3e6 100644 --- a/packages/flutter_tools/lib/src/android/android_workflow.dart +++ b/packages/flutter_tools/lib/src/android/android_workflow.dart
@@ -259,7 +259,7 @@ } try { - final Process process = await runCommand( + final Process process = await processUtils.start( <String>[androidSdk.sdkManagerPath, '--licenses'], environment: androidSdk.sdkManagerEnv, ); @@ -302,7 +302,7 @@ } try { - final Process process = await runCommand( + final Process process = await processUtils.start( <String>[androidSdk.sdkManagerPath, '--licenses'], environment: androidSdk.sdkManagerEnv, );
diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index d69b99f..2d6f93b 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart
@@ -145,8 +145,9 @@ final Status progress = logger.startProgress('Ensuring gradle dependencies are up to date...', timeout: timeoutConfiguration.slowOperation); final FlutterProject flutterProject = FlutterProject.current(); final String gradlew = await gradleUtils.getExecutable(flutterProject); - await runCheckedAsync( + await processUtils.run( <String>[gradlew, 'dependencies'], + throwOnError: true, workingDirectory: flutterProject.android.hostAppGradleRoot.path, environment: _gradleEnv, ); @@ -234,13 +235,15 @@ // Get the properties and tasks from Gradle, so we can determinate the `buildDir`, // flavors and build types defined in the project. If gradle fails, then check if the failure is due to t try { - final RunResult propertiesRunResult = await runCheckedAsync( + final RunResult propertiesRunResult = await processUtils.run( <String>[gradlew, isLibrary ? 'properties' : 'app:properties'], + throwOnError: true, workingDirectory: hostAppGradleRoot.path, environment: _gradleEnv, ); - final RunResult tasksRunResult = await runCheckedAsync( + final RunResult tasksRunResult = await processUtils.run( <String>[gradlew, isLibrary ? 'tasks': 'app:tasks', '--all', '--console=auto'], + throwOnError: true, workingDirectory: hostAppGradleRoot.path, environment: _gradleEnv, ); @@ -306,22 +309,29 @@ injectGradleWrapperIfNeeded(android); final String gradle = _locateGradlewExecutable(android); - if (gradle == null) + if (gradle == null) { + status.stop(); throwToolExit('Unable to locate gradlew script'); + } printTrace('Using gradle from $gradle.'); // Validates the Gradle executable by asking for its version. // Makes Gradle Wrapper download and install Gradle distribution, if needed. try { - await runCheckedAsync(<String>[gradle, '-v'], environment: _gradleEnv); - } catch (e) { - if (e is ProcessException && - e.toString().contains('java.io.FileNotFoundException: https://downloads.gradle.org') || - e.toString().contains('java.io.IOException: Unable to tunnel through proxy')) { + await processUtils.run( + <String>[gradle, '-v'], + throwOnError: true, + environment: _gradleEnv, + ); + } on ProcessException catch (e) { + final String error = e.toString(); + if (error.contains('java.io.FileNotFoundException: https://downloads.gradle.org') || + error.contains('java.io.IOException: Unable to tunnel through proxy')) { throwToolExit('$gradle threw an error while trying to update itself.\n$e'); } rethrow; + } finally { + status.stop(); } - status.stop(); return gradle; } @@ -595,7 +605,7 @@ int exitCode = 1; try { - exitCode = await runCommandAndStreamOutput( + exitCode = await processUtils.stream( command, workingDirectory: project.android.hostAppGradleRoot.path, allowReentrantFlutter: true, @@ -633,7 +643,7 @@ multilineOutput: true, ); final Stopwatch sw = Stopwatch()..start(); - final int exitCode = await runCommandAndStreamOutput( + final int exitCode = await processUtils.stream( <String>[fs.file(gradlew).absolute.path, 'build'], workingDirectory: project.android.hostAppGradleRoot.path, allowReentrantFlutter: true, @@ -756,7 +766,7 @@ final Stopwatch sw = Stopwatch()..start(); int exitCode = 1; try { - exitCode = await runCommandAndStreamOutput( + exitCode = await processUtils.stream( command, workingDirectory: flutterProject.android.hostAppGradleRoot.path, allowReentrantFlutter: true,
diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index 9da49e9..59c577f 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart
@@ -13,6 +13,7 @@ import 'base/common.dart'; import 'base/context.dart'; import 'base/file_system.dart'; +import 'base/io.dart'; import 'base/os.dart' show os; import 'base/process.dart'; import 'base/user_messages.dart'; @@ -115,14 +116,17 @@ String apptStdout; try { - apptStdout = runCheckedSync(<String>[ - aaptPath, - 'dump', - 'xmltree', - apk.path, - 'AndroidManifest.xml', - ]); - } catch (error) { + apptStdout = processUtils.runSync( + <String>[ + aaptPath, + 'dump', + 'xmltree', + apk.path, + 'AndroidManifest.xml', + ], + throwOnError: true, + ).stdout.trim(); + } on ProcessException catch (error) { printError('Failed to extract manifest from APK: $error.'); return null; }
diff --git a/packages/flutter_tools/lib/src/base/build.dart b/packages/flutter_tools/lib/src/base/build.dart index 256f3a4..3e8062d 100644 --- a/packages/flutter_tools/lib/src/base/build.dart +++ b/packages/flutter_tools/lib/src/base/build.dart
@@ -69,7 +69,10 @@ outputFilter = (String line) => line != kStripWarning ? line : null; } - return runCommandAndStreamOutput(<String>[snapshotterPath, ...args], mapFunction: outputFilter); + return processUtils.stream( + <String>[snapshotterPath, ...args], + mapFunction: outputFilter, + ); } }
diff --git a/packages/flutter_tools/lib/src/base/os.dart b/packages/flutter_tools/lib/src/base/os.dart index 7286e84..8e1d714 100644 --- a/packages/flutter_tools/lib/src/base/os.dart +++ b/packages/flutter_tools/lib/src/base/os.dart
@@ -154,30 +154,45 @@ @override void zip(Directory data, File zipFile) { - runSync(<String>['zip', '-r', '-q', zipFile.path, '.'], workingDirectory: data.path); + processUtils.runSync( + <String>['zip', '-r', '-q', zipFile.path, '.'], + workingDirectory: data.path, + throwOnError: true, + ); } // unzip -o -q zipfile -d dest @override void unzip(File file, Directory targetDirectory) { - runSync(<String>['unzip', '-o', '-q', file.path, '-d', targetDirectory.path]); + processUtils.runSync( + <String>['unzip', '-o', '-q', file.path, '-d', targetDirectory.path], + throwOnError: true, + ); } @override - bool verifyZip(File zipFile) => exitsHappy(<String>['zip', '-T', zipFile.path]); + bool verifyZip(File zipFile) => + processUtils.exitsHappySync(<String>['zip', '-T', zipFile.path]); // tar -xzf tarball -C dest @override void unpack(File gzippedTarFile, Directory targetDirectory) { - runSync(<String>['tar', '-xzf', gzippedTarFile.path, '-C', targetDirectory.path]); + processUtils.runSync( + <String>['tar', '-xzf', gzippedTarFile.path, '-C', targetDirectory.path], + throwOnError: true, + ); } @override - bool verifyGzip(File gzippedFile) => exitsHappy(<String>['gzip', '-t', gzippedFile.path]); + bool verifyGzip(File gzippedFile) => + processUtils.exitsHappySync(<String>['gzip', '-t', gzippedFile.path]); @override File makePipe(String path) { - runSync(<String>['mkfifo', path]); + processUtils.runSync( + <String>['mkfifo', path], + throwOnError: true, + ); return fs.file(path); } @@ -187,12 +202,12 @@ String get name { if (_name == null) { if (platform.isMacOS) { - final List<ProcessResult> results = <ProcessResult>[ - processManager.runSync(<String>['sw_vers', '-productName']), - processManager.runSync(<String>['sw_vers', '-productVersion']), - processManager.runSync(<String>['sw_vers', '-buildVersion']), + final List<RunResult> results = <RunResult>[ + processUtils.runSync(<String>['sw_vers', '-productName']), + processUtils.runSync(<String>['sw_vers', '-productVersion']), + processUtils.runSync(<String>['sw_vers', '-buildVersion']), ]; - if (results.every((ProcessResult result) => result.exitCode == 0)) { + if (results.every((RunResult result) => result.exitCode == 0)) { _name = '${results[0].stdout.trim()} ${results[1].stdout .trim()} ${results[2].stdout.trim()}'; }
diff --git a/packages/flutter_tools/lib/src/base/process.dart b/packages/flutter_tools/lib/src/base/process.dart index b5be56b..99229fd 100644 --- a/packages/flutter_tools/lib/src/base/process.dart +++ b/packages/flutter_tools/lib/src/base/process.dart
@@ -6,7 +6,7 @@ import '../convert.dart'; import '../globals.dart'; -import 'common.dart'; +import 'context.dart'; import 'file_system.dart'; import 'io.dart'; import 'process_manager.dart'; @@ -98,379 +98,6 @@ printTrace('Shutdown hooks complete'); } -Map<String, String> _environment(bool allowReentrantFlutter, [ Map<String, String> environment ]) { - if (allowReentrantFlutter) { - if (environment == null) - environment = <String, String>{'FLUTTER_ALREADY_LOCKED': 'true'}; - else - environment['FLUTTER_ALREADY_LOCKED'] = 'true'; - } - - return environment; -} - -/// This runs the command in the background from the specified working -/// directory. Completes when the process has been started. -Future<Process> runCommand( - List<String> cmd, { - String workingDirectory, - bool allowReentrantFlutter = false, - Map<String, String> environment, -}) { - _traceCommand(cmd, workingDirectory: workingDirectory); - return processManager.start( - cmd, - workingDirectory: workingDirectory, - environment: _environment(allowReentrantFlutter, environment), - ); -} - -/// This runs the command and streams stdout/stderr from the child process to -/// this process' stdout/stderr. Completes with the process's exit code. -/// -/// If [filter] is null, no lines are removed. -/// -/// If [filter] is non-null, all lines that do not match it are removed. If -/// [mapFunction] is present, all lines that match [filter] are also forwarded -/// to [mapFunction] for further processing. -Future<int> runCommandAndStreamOutput( - List<String> cmd, { - String workingDirectory, - bool allowReentrantFlutter = false, - String prefix = '', - bool trace = false, - RegExp filter, - StringConverter mapFunction, - Map<String, String> environment, -}) async { - final Process process = await runCommand( - cmd, - workingDirectory: workingDirectory, - allowReentrantFlutter: allowReentrantFlutter, - environment: environment, - ); - final StreamSubscription<String> stdoutSubscription = process.stdout - .transform<String>(utf8.decoder) - .transform<String>(const LineSplitter()) - .where((String line) => filter == null || filter.hasMatch(line)) - .listen((String line) { - if (mapFunction != null) - line = mapFunction(line); - if (line != null) { - final String message = '$prefix$line'; - if (trace) - printTrace(message); - else - printStatus(message, wrap: false); - } - }); - final StreamSubscription<String> stderrSubscription = process.stderr - .transform<String>(utf8.decoder) - .transform<String>(const LineSplitter()) - .where((String line) => filter == null || filter.hasMatch(line)) - .listen((String line) { - if (mapFunction != null) - line = mapFunction(line); - if (line != null) - printError('$prefix$line', wrap: false); - }); - - // Wait for stdout to be fully processed - // because process.exitCode may complete first causing flaky tests. - await waitGroup<void>(<Future<void>>[ - stdoutSubscription.asFuture<void>(), - stderrSubscription.asFuture<void>(), - ]); - - await waitGroup<void>(<Future<void>>[ - stdoutSubscription.cancel(), - stderrSubscription.cancel(), - ]); - - return await process.exitCode; -} - -/// Runs the [command] interactively, connecting the stdin/stdout/stderr -/// streams of this process to those of the child process. Completes with -/// the exit code of the child process. -Future<int> runInteractively( - List<String> command, { - String workingDirectory, - bool allowReentrantFlutter = false, - Map<String, String> environment, -}) async { - final Process process = await runCommand( - command, - workingDirectory: workingDirectory, - allowReentrantFlutter: allowReentrantFlutter, - environment: environment, - ); - // The real stdin will never finish streaming. Pipe until the child process - // finishes. - unawaited(process.stdin.addStream(stdin)); - // Wait for stdout and stderr to be fully processed, because process.exitCode - // may complete first. - await Future.wait<dynamic>(<Future<dynamic>>[ - stdout.addStream(process.stdout), - stderr.addStream(process.stderr), - ]); - return await process.exitCode; -} - -Future<Process> runDetached(List<String> cmd) { - _traceCommand(cmd); - final Future<Process> proc = processManager.start( - cmd, - mode: ProcessStartMode.detached, - ); - return proc; -} - -Future<RunResult> runAsync( - List<String> cmd, { - String workingDirectory, - bool allowReentrantFlutter = false, - Map<String, String> environment, - Duration timeout, - int timeoutRetries = 0, -}) async { - _traceCommand(cmd, workingDirectory: workingDirectory); - - // When there is no timeout, there's no need to kill a running process, so - // we can just use processManager.run(). - if (timeout == null) { - final ProcessResult results = await processManager.run( - cmd, - workingDirectory: workingDirectory, - environment: _environment(allowReentrantFlutter, environment), - ); - final RunResult runResults = RunResult(results, cmd); - printTrace(runResults.toString()); - return runResults; - } - - // When there is a timeout, we have to kill the running process, so we have - // to use processManager.start() through runCommand() above. - while (true) { - assert(timeoutRetries >= 0); - timeoutRetries = timeoutRetries - 1; - - final Process process = await runCommand( - cmd, - workingDirectory: workingDirectory, - allowReentrantFlutter: allowReentrantFlutter, - environment: environment, - ); - - final StringBuffer stdoutBuffer = StringBuffer(); - final StringBuffer stderrBuffer = StringBuffer(); - final Future<void> stdoutFuture = process.stdout - .transform<String>(const Utf8Decoder(reportErrors: false)) - .listen(stdoutBuffer.write) - .asFuture<void>(null); - final Future<void> stderrFuture = process.stderr - .transform<String>(const Utf8Decoder(reportErrors: false)) - .listen(stderrBuffer.write) - .asFuture<void>(null); - - int exitCode; - exitCode = await process.exitCode.timeout(timeout, onTimeout: () { - // The process timed out. Kill it. - processManager.killPid(process.pid); - return null; - }); - - String stdoutString; - String stderrString; - try { - Future<void> stdioFuture = - Future.wait<void>(<Future<void>>[stdoutFuture, stderrFuture]); - if (exitCode == null) { - // If we had to kill the process for a timeout, only wait a short time - // for the stdio streams to drain in case killing the process didn't - // work. - stdioFuture = stdioFuture.timeout(const Duration(seconds: 1)); - } - await stdioFuture; - } catch (_) { - // Ignore errors on the process' stdout and stderr streams. Just capture - // whatever we got, and use the exit code - } - stdoutString = stdoutBuffer.toString(); - stderrString = stderrBuffer.toString(); - - final ProcessResult result = ProcessResult( - process.pid, exitCode ?? -1, stdoutString, stderrString); - final RunResult runResult = RunResult(result, cmd); - - // If the process did not timeout. We are done. - if (exitCode != null) { - printTrace(runResult.toString()); - return runResult; - } - - // If we are out of timeoutRetries, throw a ProcessException. - if (timeoutRetries < 0) { - throw ProcessException(cmd[0], cmd.sublist(1), - 'Process "${cmd[0]}" timed out: $runResult', exitCode); - } - - // Log the timeout with a trace message in verbose mode. - printTrace('Process "${cmd[0]}" timed out. $timeoutRetries attempts left: $runResult'); - } - - // Unreachable. -} - -typedef RunResultChecker = bool Function(int); - -Future<RunResult> runCheckedAsync( - List<String> cmd, { - String workingDirectory, - bool allowReentrantFlutter = false, - Map<String, String> environment, - RunResultChecker whiteListFailures, - Duration timeout, - int timeoutRetries = 0, -}) async { - final RunResult result = await runAsync( - cmd, - workingDirectory: workingDirectory, - allowReentrantFlutter: allowReentrantFlutter, - environment: environment, - timeout: timeout, - timeoutRetries: timeoutRetries, - ); - if (result.exitCode != 0) { - if (whiteListFailures == null || !whiteListFailures(result.exitCode)) { - throw ProcessException(cmd[0], cmd.sublist(1), - 'Process "${cmd[0]}" exited abnormally:\n$result', result.exitCode); - } - } - return result; -} - -bool exitsHappy( - List<String> cli, { - Map<String, String> environment, -}) { - _traceCommand(cli); - try { - return processManager.runSync(cli, environment: environment).exitCode == 0; - } catch (error) { - printTrace('$cli failed with $error'); - return false; - } -} - -Future<bool> exitsHappyAsync( - List<String> cli, { - Map<String, String> environment, -}) async { - _traceCommand(cli); - try { - return (await processManager.run(cli, environment: environment)).exitCode == 0; - } catch (error) { - printTrace('$cli failed with $error'); - return false; - } -} - -/// Run cmd and return stdout. -/// -/// Throws an error if cmd exits with a non-zero value. -String runCheckedSync( - List<String> cmd, { - String workingDirectory, - bool allowReentrantFlutter = false, - bool hideStdout = false, - Map<String, String> environment, - RunResultChecker whiteListFailures, -}) { - return _runWithLoggingSync( - cmd, - workingDirectory: workingDirectory, - allowReentrantFlutter: allowReentrantFlutter, - hideStdout: hideStdout, - checked: true, - noisyErrors: true, - environment: environment, - whiteListFailures: whiteListFailures - ); -} - -/// Run cmd and return stdout. -String runSync( - List<String> cmd, { - String workingDirectory, - bool allowReentrantFlutter = false, -}) { - return _runWithLoggingSync( - cmd, - workingDirectory: workingDirectory, - allowReentrantFlutter: allowReentrantFlutter, - ); -} - -void _traceCommand(List<String> args, { String workingDirectory }) { - final String argsText = args.join(' '); - if (workingDirectory == null) { - printTrace('executing: $argsText'); - } else { - printTrace('executing: [$workingDirectory${fs.path.separator}] $argsText'); - } -} - -String _runWithLoggingSync( - List<String> cmd, { - bool checked = false, - bool noisyErrors = false, - bool throwStandardErrorOnError = false, - String workingDirectory, - bool allowReentrantFlutter = false, - bool hideStdout = false, - Map<String, String> environment, - RunResultChecker whiteListFailures, -}) { - _traceCommand(cmd, workingDirectory: workingDirectory); - final ProcessResult results = processManager.runSync( - cmd, - workingDirectory: workingDirectory, - environment: _environment(allowReentrantFlutter, environment), - ); - - printTrace('Exit code ${results.exitCode} from: ${cmd.join(' ')}'); - - bool failedExitCode = results.exitCode != 0; - if (whiteListFailures != null && failedExitCode) { - failedExitCode = !whiteListFailures(results.exitCode); - } - - if (results.stdout.isNotEmpty && !hideStdout) { - if (failedExitCode && noisyErrors) - printStatus(results.stdout.trim()); - else - printTrace(results.stdout.trim()); - } - - if (failedExitCode) { - if (results.stderr.isNotEmpty) { - if (noisyErrors) - printError(results.stderr.trim()); - else - printTrace(results.stderr.trim()); - } - - if (throwStandardErrorOnError) - throw results.stderr.trim(); - - if (checked) - throw 'Exit code ${results.exitCode} from: ${cmd.join(' ')}'; - } - - return results.stdout.trim(); -} - class ProcessExit implements Exception { ProcessExit(this.exitCode, {this.immediate = false}); @@ -516,3 +143,382 @@ ); } } + +typedef RunResultChecker = bool Function(int); + +ProcessUtils get processUtils => ProcessUtils.instance; + +abstract class ProcessUtils { + factory ProcessUtils() => _DefaultProcessUtils(); + + static ProcessUtils get instance => context.get<ProcessUtils>(); + + /// Spawns a child process to run the command [cmd]. + /// + /// When [throwOnError] is `true`, if the child process finishes with a non-zero + /// exit code, a [ProcessException] is thrown. + /// + /// If [throwOnError] is `true`, and [whiteListFailures] is supplied, + /// a [ProcessException] is only thrown on a non-zero exit code if + /// [whiteListFailures] returns false when passed the exit code. + /// + /// When [workingDirectory] is set, it is the working directory of the child + /// process. + /// + /// When [allowReentrantFlutter] is set to `true`, the child process is + /// permitted to call the Flutter tool. By default it is not. + /// + /// When [environment] is supplied, it is used as the environment for the child + /// process. + /// + /// When [timeout] is supplied, [runAsync] will kill the child process and + /// throw a [ProcessException] when it doesn't finish in time. + /// + /// If [timeout] is supplied, the command will be retried [timeoutRetries] times + /// if it times out. + Future<RunResult> run( + List<String> cmd, { + bool throwOnError = false, + RunResultChecker whiteListFailures, + String workingDirectory, + bool allowReentrantFlutter = false, + Map<String, String> environment, + Duration timeout, + int timeoutRetries = 0, + }); + + /// Run the command and block waiting for its result. + RunResult runSync( + List<String> cmd, { + bool throwOnError = false, + RunResultChecker whiteListFailures, + bool hideStdout = false, + String workingDirectory, + Map<String, String> environment, + bool allowReentrantFlutter = false, + }); + + /// This runs the command in the background from the specified working + /// directory. Completes when the process has been started. + Future<Process> start( + List<String> cmd, { + String workingDirectory, + bool allowReentrantFlutter = false, + Map<String, String> environment, + }); + + /// This runs the command and streams stdout/stderr from the child process to + /// this process' stdout/stderr. Completes with the process's exit code. + /// + /// If [filter] is null, no lines are removed. + /// + /// If [filter] is non-null, all lines that do not match it are removed. If + /// [mapFunction] is present, all lines that match [filter] are also forwarded + /// to [mapFunction] for further processing. + Future<int> stream( + List<String> cmd, { + String workingDirectory, + bool allowReentrantFlutter = false, + String prefix = '', + bool trace = false, + RegExp filter, + StringConverter mapFunction, + Map<String, String> environment, + }); + + bool exitsHappySync( + List<String> cli, { + Map<String, String> environment, + }); + + Future<bool> exitsHappy( + List<String> cli, { + Map<String, String> environment, + }); +} + +class _DefaultProcessUtils implements ProcessUtils { + @override + Future<RunResult> run( + List<String> cmd, { + bool throwOnError = false, + RunResultChecker whiteListFailures, + String workingDirectory, + bool allowReentrantFlutter = false, + Map<String, String> environment, + Duration timeout, + int timeoutRetries = 0, + }) async { + if (cmd == null || cmd.isEmpty) { + throw ArgumentError('cmd must be a non-empty list'); + } + if (timeoutRetries < 0) { + throw ArgumentError('timeoutRetries must be non-negative'); + } + _traceCommand(cmd, workingDirectory: workingDirectory); + + // When there is no timeout, there's no need to kill a running process, so + // we can just use processManager.run(). + if (timeout == null) { + final ProcessResult results = await processManager.run( + cmd, + workingDirectory: workingDirectory, + environment: _environment(allowReentrantFlutter, environment), + ); + final RunResult runResult = RunResult(results, cmd); + printTrace(runResult.toString()); + if (throwOnError && runResult.exitCode != 0 && + (whiteListFailures == null || !whiteListFailures(runResult.exitCode))) { + runResult.throwException('Process exited abnormally:\n$runResult'); + } + return runResult; + } + + // When there is a timeout, we have to kill the running process, so we have + // to use processManager.start() through _runCommand() above. + while (true) { + assert(timeoutRetries >= 0); + timeoutRetries = timeoutRetries - 1; + + final Process process = await start( + cmd, + workingDirectory: workingDirectory, + allowReentrantFlutter: allowReentrantFlutter, + environment: environment, + ); + + final StringBuffer stdoutBuffer = StringBuffer(); + final StringBuffer stderrBuffer = StringBuffer(); + final Future<void> stdoutFuture = process.stdout + .transform<String>(const Utf8Decoder(reportErrors: false)) + .listen(stdoutBuffer.write) + .asFuture<void>(null); + final Future<void> stderrFuture = process.stderr + .transform<String>(const Utf8Decoder(reportErrors: false)) + .listen(stderrBuffer.write) + .asFuture<void>(null); + + int exitCode; + exitCode = await process.exitCode.timeout(timeout, onTimeout: () { + // The process timed out. Kill it. + processManager.killPid(process.pid); + return null; + }); + + String stdoutString; + String stderrString; + try { + Future<void> stdioFuture = + Future.wait<void>(<Future<void>>[stdoutFuture, stderrFuture]); + if (exitCode == null) { + // If we had to kill the process for a timeout, only wait a short time + // for the stdio streams to drain in case killing the process didn't + // work. + stdioFuture = stdioFuture.timeout(const Duration(seconds: 1)); + } + await stdioFuture; + } catch (_) { + // Ignore errors on the process' stdout and stderr streams. Just capture + // whatever we got, and use the exit code + } + stdoutString = stdoutBuffer.toString(); + stderrString = stderrBuffer.toString(); + + final ProcessResult result = ProcessResult( + process.pid, exitCode ?? -1, stdoutString, stderrString); + final RunResult runResult = RunResult(result, cmd); + + // If the process did not timeout. We are done. + if (exitCode != null) { + printTrace(runResult.toString()); + if (throwOnError && runResult.exitCode != 0 && + (whiteListFailures == null || !whiteListFailures(exitCode))) { + runResult.throwException('Process exited abnormally:\n$runResult'); + } + return runResult; + } + + // If we are out of timeoutRetries, throw a ProcessException. + if (timeoutRetries < 0) { + runResult.throwException('Process timed out:\n$runResult'); + } + + // Log the timeout with a trace message in verbose mode. + printTrace('Process "${cmd[0]}" timed out. $timeoutRetries attempts left:\n' + '$runResult'); + } + + // Unreachable. + } + + @override + RunResult runSync( + List<String> cmd, { + bool throwOnError = false, + RunResultChecker whiteListFailures, + bool hideStdout = false, + String workingDirectory, + Map<String, String> environment, + bool allowReentrantFlutter = false, + }) { + _traceCommand(cmd, workingDirectory: workingDirectory); + final ProcessResult results = processManager.runSync( + cmd, + workingDirectory: workingDirectory, + environment: _environment(allowReentrantFlutter, environment), + ); + final RunResult runResult = RunResult(results, cmd); + + printTrace('Exit code ${runResult.exitCode} from: ${cmd.join(' ')}'); + + bool failedExitCode = runResult.exitCode != 0; + if (whiteListFailures != null && failedExitCode) { + failedExitCode = !whiteListFailures(runResult.exitCode); + } + + if (runResult.stdout.isNotEmpty && !hideStdout) { + if (failedExitCode && throwOnError) { + printStatus(runResult.stdout.trim()); + } else { + printTrace(runResult.stdout.trim()); + } + } + + if (runResult.stderr.isNotEmpty) { + if (failedExitCode && throwOnError) { + printError(runResult.stderr.trim()); + } else { + printTrace(runResult.stderr.trim()); + } + } + + if (failedExitCode && throwOnError) { + runResult.throwException('The command failed'); + } + + return runResult; + } + + @override + Future<Process> start( + List<String> cmd, { + String workingDirectory, + bool allowReentrantFlutter = false, + Map<String, String> environment, + }) { + _traceCommand(cmd, workingDirectory: workingDirectory); + return processManager.start( + cmd, + workingDirectory: workingDirectory, + environment: _environment(allowReentrantFlutter, environment), + ); + } + + @override + Future<int> stream( + List<String> cmd, { + String workingDirectory, + bool allowReentrantFlutter = false, + String prefix = '', + bool trace = false, + RegExp filter, + StringConverter mapFunction, + Map<String, String> environment, + }) async { + final Process process = await start( + cmd, + workingDirectory: workingDirectory, + allowReentrantFlutter: allowReentrantFlutter, + environment: environment, + ); + final StreamSubscription<String> stdoutSubscription = process.stdout + .transform<String>(utf8.decoder) + .transform<String>(const LineSplitter()) + .where((String line) => filter == null || filter.hasMatch(line)) + .listen((String line) { + if (mapFunction != null) + line = mapFunction(line); + if (line != null) { + final String message = '$prefix$line'; + if (trace) + printTrace(message); + else + printStatus(message, wrap: false); + } + }); + final StreamSubscription<String> stderrSubscription = process.stderr + .transform<String>(utf8.decoder) + .transform<String>(const LineSplitter()) + .where((String line) => filter == null || filter.hasMatch(line)) + .listen((String line) { + if (mapFunction != null) + line = mapFunction(line); + if (line != null) + printError('$prefix$line', wrap: false); + }); + + // Wait for stdout to be fully processed + // because process.exitCode may complete first causing flaky tests. + await waitGroup<void>(<Future<void>>[ + stdoutSubscription.asFuture<void>(), + stderrSubscription.asFuture<void>(), + ]); + + await waitGroup<void>(<Future<void>>[ + stdoutSubscription.cancel(), + stderrSubscription.cancel(), + ]); + + return await process.exitCode; + } + + @override + bool exitsHappySync( + List<String> cli, { + Map<String, String> environment, + }) { + _traceCommand(cli); + try { + return processManager.runSync(cli, environment: environment).exitCode == 0; + } catch (error) { + printTrace('$cli failed with $error'); + return false; + } + } + + @override + Future<bool> exitsHappy( + List<String> cli, { + Map<String, String> environment, + }) async { + _traceCommand(cli); + try { + return (await processManager.run(cli, environment: environment)).exitCode == 0; + } catch (error) { + printTrace('$cli failed with $error'); + return false; + } + } + + Map<String, String> _environment(bool allowReentrantFlutter, [ + Map<String, String> environment, + ]) { + if (allowReentrantFlutter) { + if (environment == null) + environment = <String, String>{'FLUTTER_ALREADY_LOCKED': 'true'}; + else + environment['FLUTTER_ALREADY_LOCKED'] = 'true'; + } + + return environment; + } + + void _traceCommand(List<String> args, { String workingDirectory }) { + final String argsText = args.join(' '); + if (workingDirectory == null) { + printTrace('executing: $argsText'); + } else { + printTrace('executing: [$workingDirectory${fs.path.separator}] $argsText'); + } + } +}
diff --git a/packages/flutter_tools/lib/src/commands/build_aot.dart b/packages/flutter_tools/lib/src/commands/build_aot.dart index 763a8b9..f572442 100644 --- a/packages/flutter_tools/lib/src/commands/build_aot.dart +++ b/packages/flutter_tools/lib/src/commands/build_aot.dart
@@ -9,6 +9,7 @@ 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/version.dart'; @@ -142,14 +143,18 @@ // Merge arch-specific App.frameworks into a multi-arch App.framework. if ((await Future.wait<int>(exitCodes.values)).every((int buildExitCode) => buildExitCode == 0)) { - final Iterable<String> dylibs = iosBuilds.values.map<String>((String outputDir) => fs.path.join(outputDir, 'App.framework', 'App')); + final Iterable<String> dylibs = iosBuilds.values.map<String>( + (String outputDir) => fs.path.join(outputDir, 'App.framework', 'App')); fs.directory(fs.path.join(outputPath, 'App.framework'))..createSync(); - await runCheckedAsync(<String>[ - 'lipo', - ...dylibs, - '-create', - '-output', fs.path.join(outputPath, 'App.framework', 'App'), - ]); + await processUtils.run( + <String>[ + 'lipo', + ...dylibs, + '-create', + '-output', fs.path.join(outputPath, 'App.framework', 'App'), + ], + throwOnError: true, + ); } else { status?.cancel(); exitCodes.forEach((DarwinArch iosArch, Future<int> exitCodeFuture) async { @@ -173,10 +178,10 @@ throwToolExit('Snapshotting exited with non-zero exit code: $snapshotExitCode'); } } - } on String catch (error) { - // Catch the String exceptions thrown from the `runCheckedSync` methods below. + } on ProcessException catch (error) { + // Catch the String exceptions thrown from the `runSync` methods below. status?.cancel(); - printError(error); + printError(error.toString()); return null; } status?.stop();
diff --git a/packages/flutter_tools/lib/src/commands/channel.dart b/packages/flutter_tools/lib/src/commands/channel.dart index d9672f6..bef80ac 100644 --- a/packages/flutter_tools/lib/src/commands/channel.dart +++ b/packages/flutter_tools/lib/src/commands/channel.dart
@@ -61,7 +61,7 @@ showAll = showAll || currentChannel != currentBranch; printStatus('Flutter channels:'); - final int result = await runCommandAndStreamOutput( + final int result = await processUtils.stream( <String>['git', 'branch', '-r'], workingDirectory: Cache.flutterRoot, mapFunction: (String line) { @@ -111,28 +111,28 @@ static Future<void> _checkout(String branchName) async { // Get latest refs from upstream. - int result = await runCommandAndStreamOutput( + int result = await processUtils.stream( <String>['git', 'fetch'], workingDirectory: Cache.flutterRoot, prefix: 'git: ', ); if (result == 0) { - result = await runCommandAndStreamOutput( + result = await processUtils.stream( <String>['git', 'show-ref', '--verify', '--quiet', 'refs/heads/$branchName'], workingDirectory: Cache.flutterRoot, prefix: 'git: ', ); if (result == 0) { // branch already exists, try just switching to it - result = await runCommandAndStreamOutput( + result = await processUtils.stream( <String>['git', 'checkout', branchName, '--'], workingDirectory: Cache.flutterRoot, prefix: 'git: ', ); } else { // branch does not exist, we have to create it - result = await runCommandAndStreamOutput( + result = await processUtils.stream( <String>['git', 'checkout', '--track', '-b', branchName, 'origin/$branchName'], workingDirectory: Cache.flutterRoot, prefix: 'git: ',
diff --git a/packages/flutter_tools/lib/src/commands/drive.dart b/packages/flutter_tools/lib/src/commands/drive.dart index 61f3128..e63377e 100644 --- a/packages/flutter_tools/lib/src/commands/drive.dart +++ b/packages/flutter_tools/lib/src/commands/drive.dart
@@ -292,7 +292,7 @@ PackageMap.globalPackagesPath = fs.path.normalize(fs.path.absolute(PackageMap.globalPackagesPath)); final String dartVmPath = fs.path.join(dartSdkPath, 'bin', 'dart'); - final int result = await runCommandAndStreamOutput( + final int result = await processUtils.stream( <String>[ dartVmPath, ...dartVmFlags,
diff --git a/packages/flutter_tools/lib/src/commands/format.dart b/packages/flutter_tools/lib/src/commands/format.dart index fd0b331..24a1511 100644 --- a/packages/flutter_tools/lib/src/commands/format.dart +++ b/packages/flutter_tools/lib/src/commands/format.dart
@@ -76,9 +76,10 @@ ...argResults.rest, ]; - final int result = await runCommandAndStreamOutput(command); - if (result != 0) + final int result = await processUtils.stream(command); + if (result != 0) { throwToolExit('Formatting failed: $result', exitCode: result); + } return null; }
diff --git a/packages/flutter_tools/lib/src/commands/unpack.dart b/packages/flutter_tools/lib/src/commands/unpack.dart index 7450bf9..caa4436 100644 --- a/packages/flutter_tools/lib/src/commands/unpack.dart +++ b/packages/flutter_tools/lib/src/commands/unpack.dart
@@ -5,8 +5,7 @@ import '../artifacts.dart'; import '../base/common.dart'; import '../base/file_system.dart'; -import '../base/io.dart'; -import '../base/process_manager.dart'; +import '../base/process.dart'; import '../build_info.dart'; import '../cache.dart'; import '../globals.dart'; @@ -284,7 +283,7 @@ _deleteFrameworkIfPresent( fs.path.join(targetDirectory, fs.path.basename(frameworkPath))); - final ProcessResult result = processManager + final RunResult result = processUtils .runSync(<String>['cp', '-R', frameworkPath, targetDirectory]); if (result.exitCode != 0) { throw Exception(
diff --git a/packages/flutter_tools/lib/src/commands/upgrade.dart b/packages/flutter_tools/lib/src/commands/upgrade.dart index 542ffba..cd31444 100644 --- a/packages/flutter_tools/lib/src/commands/upgrade.dart +++ b/packages/flutter_tools/lib/src/commands/upgrade.dart
@@ -121,7 +121,7 @@ } Future<void> flutterUpgradeContinue() async { - final int code = await runCommandAndStreamOutput( + final int code = await processUtils.stream( <String>[ fs.path.join('bin', 'flutter'), 'upgrade', @@ -146,9 +146,11 @@ Future<bool> hasUncomittedChanges() async { try { - final RunResult result = await runCheckedAsync(<String>[ - 'git', 'status', '-s' - ], workingDirectory: Cache.flutterRoot); + final RunResult result = await processUtils.run( + <String>['git', 'status', '-s'], + throwOnError: true, + workingDirectory: Cache.flutterRoot, + ); return result.stdout.trim().isNotEmpty; } on ProcessException catch (error) { throwToolExit( @@ -167,9 +169,11 @@ /// Exits tool if there is no upstream. Future<void> verifyUpstreamConfigured() async { try { - await runCheckedAsync(<String>[ - 'git', 'rev-parse', '@{u}', - ], workingDirectory: Cache.flutterRoot); + await processUtils.run( + <String>[ 'git', 'rev-parse', '@{u}'], + throwOnError: true, + workingDirectory: Cache.flutterRoot, + ); } catch (e) { throwToolExit( 'Unable to upgrade Flutter: no origin repository configured. ' @@ -191,9 +195,11 @@ tag = 'v${gitTagVersion.x}.${gitTagVersion.y}.${gitTagVersion.z}'; } try { - await runCheckedAsync(<String>[ - 'git', 'reset', '--hard', tag, - ], workingDirectory: Cache.flutterRoot); + await processUtils.run( + <String>['git', 'reset', '--hard', tag], + throwOnError: true, + workingDirectory: Cache.flutterRoot, + ); } on ProcessException catch (error) { throwToolExit( 'Unable to upgrade Flutter: The tool could not update to the version $tag. ' @@ -218,7 +224,7 @@ /// If there haven't been any hot fixes or local changes, this is equivalent /// to a fast-forward. Future<void> attemptFastForward() async { - final int code = await runCommandAndStreamOutput( + final int code = await processUtils.stream( <String>['git', 'pull', '--ff'], workingDirectory: Cache.flutterRoot, mapFunction: (String line) => matchesGitLine(line) ? null : line, @@ -236,7 +242,7 @@ Future<void> precacheArtifacts() async { printStatus(''); printStatus('Upgrading engine...'); - final int code = await runCommandAndStreamOutput( + final int code = await processUtils.stream( <String>[ fs.path.join('bin', 'flutter'), '--no-color', '--no-version-check', 'precache', ], @@ -263,7 +269,7 @@ Future<void> runDoctor() async { printStatus(''); printStatus('Running flutter doctor...'); - await runCommandAndStreamOutput( + await processUtils.stream( <String>[ fs.path.join('bin', 'flutter'), '--no-version-check', 'doctor', ],
diff --git a/packages/flutter_tools/lib/src/commands/version.dart b/packages/flutter_tools/lib/src/commands/version.dart index 0fb9e90..2d8fdce 100644 --- a/packages/flutter_tools/lib/src/commands/version.dart +++ b/packages/flutter_tools/lib/src/commands/version.dart
@@ -37,8 +37,9 @@ Future<List<String>> getTags() async { RunResult runResult; try { - runResult = await runCheckedAsync( + runResult = await processUtils.run( <String>['git', 'tag', '-l', 'v*', '--sort=-creatordate'], + throwOnError: true, workingDirectory: Cache.flutterRoot, ); } on ProcessException catch (error) { @@ -83,8 +84,9 @@ } try { - await runCheckedAsync( + await processUtils.run( <String>['git', 'checkout', 'v$version'], + throwOnError: true, workingDirectory: Cache.flutterRoot, ); } catch (e) { @@ -101,7 +103,7 @@ // if necessary. printStatus(''); printStatus('Downloading engine...'); - int code = await runCommandAndStreamOutput(<String>[ + int code = await processUtils.stream(<String>[ fs.path.join('bin', 'flutter'), '--no-color', 'precache', @@ -128,7 +130,7 @@ // Run a doctor check in case system requirements have changed. printStatus(''); printStatus('Running flutter doctor...'); - code = await runCommandAndStreamOutput( + code = await processUtils.stream( <String>[ fs.path.join('bin', 'flutter'), 'doctor',
diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart index c761abd..ca885e3 100644 --- a/packages/flutter_tools/lib/src/context_runner.dart +++ b/packages/flutter_tools/lib/src/context_runner.dart
@@ -19,6 +19,7 @@ import 'base/logger.dart'; import 'base/os.dart'; import 'base/platform.dart'; +import 'base/process.dart'; import 'base/time.dart'; import 'base/user_messages.dart'; import 'base/utils.dart'; @@ -101,6 +102,7 @@ Logger: () => platform.isWindows ? WindowsStdoutLogger() : StdoutLogger(), MacOSWorkflow: () => const MacOSWorkflow(), OperatingSystemUtils: () => OperatingSystemUtils(), + ProcessUtils: () => ProcessUtils(), SimControl: () => SimControl(), Stdio: () => const Stdio(), SystemClock: () => const SystemClock(),
diff --git a/packages/flutter_tools/lib/src/dart/pub.dart b/packages/flutter_tools/lib/src/dart/pub.dart index 696253f..2b25239 100644 --- a/packages/flutter_tools/lib/src/dart/pub.dart +++ b/packages/flutter_tools/lib/src/dart/pub.dart
@@ -8,6 +8,7 @@ import '../base/common.dart'; import '../base/file_system.dart'; +import '../base/io.dart' as io; import '../base/logger.dart'; import '../base/platform.dart'; import '../base/process.dart'; @@ -156,22 +157,25 @@ int code; while (true) { attempts += 1; - code = await runCommandAndStreamOutput( + code = await processUtils.stream( _pubCommand(arguments), workingDirectory: directory, mapFunction: filter, environment: _createPubEnvironment(context), ); - if (code != 69) // UNAVAILABLE in https://github.com/dart-lang/pub/blob/master/lib/src/exit_codes.dart + if (code != 69) { // UNAVAILABLE in https://github.com/dart-lang/pub/blob/master/lib/src/exit_codes.dart break; + } printStatus('$failureMessage ($code) -- attempting retry $attempts in $duration second${ duration == 1 ? "" : "s"}...'); await Future<void>.delayed(Duration(seconds: duration)); - if (duration < 64) + if (duration < 64) { duration *= 2; + } } assert(code != null); - if (code != 0) + if (code != 0) { throwToolExit('$failureMessage ($code)', exitCode: code); + } } /// Runs pub in 'interactive' mode, directly piping the stdin stream of this @@ -182,13 +186,26 @@ String directory, }) async { Cache.releaseLockEarly(); - final int code = await runInteractively( + final io.Process process = await processUtils.start( _pubCommand(arguments), workingDirectory: directory, environment: _createPubEnvironment(PubContext.interactive), ); - if (code != 0) + + // Pipe the Flutter tool stdin to the pub stdin. + unawaited(process.stdin.addStream(io.stdin)); + + // Pipe the put stdout and stderr to the tool stdout and stderr. + await Future.wait<dynamic>(<Future<dynamic>>[ + io.stdout.addStream(process.stdout), + io.stderr.addStream(process.stderr), + ]); + + // Wait for pub to exit. + final int code = await process.exitCode; + if (code != 0) { throwToolExit('pub finished with exit code $code', exitCode: code); + } } /// The command used for running pub.
diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart index b3b3c88..8e45bfb 100644 --- a/packages/flutter_tools/lib/src/doctor.dart +++ b/packages/flutter_tools/lib/src/doctor.dart
@@ -14,7 +14,7 @@ import 'base/logger.dart'; import 'base/os.dart'; import 'base/platform.dart'; -import 'base/process_manager.dart'; +import 'base/process.dart'; import 'base/terminal.dart'; import 'base/user_messages.dart'; import 'base/utils.dart'; @@ -607,7 +607,7 @@ bool _genSnapshotRuns(String genSnapshotPath) { const int kExpectedExitCode = 255; try { - return processManager.runSync(<String>[genSnapshotPath]).exitCode == kExpectedExitCode; + return processUtils.runSync(<String>[genSnapshotPath]).exitCode == kExpectedExitCode; } catch (error) { return false; }
diff --git a/packages/flutter_tools/lib/src/emulator.dart b/packages/flutter_tools/lib/src/emulator.dart index a80c86d..dcbb538 100644 --- a/packages/flutter_tools/lib/src/emulator.dart +++ b/packages/flutter_tools/lib/src/emulator.dart
@@ -8,8 +8,7 @@ import 'android/android_emulator.dart'; import 'android/android_sdk.dart'; import 'base/context.dart'; -import 'base/io.dart' show ProcessResult; -import 'base/process_manager.dart'; +import 'base/process.dart'; import 'device.dart'; import 'globals.dart'; import 'ios/ios_emulators.dart'; @@ -118,7 +117,7 @@ '-k', sdkId, '-d', device, ]; - final ProcessResult runResult = processManager.runSync(args, + final RunResult runResult = processUtils.runSync(args, environment: androidSdk?.sdkManagerEnv); return CreateEmulatorResult( name, @@ -139,10 +138,11 @@ 'device', '-c', ]; - final ProcessResult runResult = processManager.runSync(args, + final RunResult runResult = processUtils.runSync(args, environment: androidSdk?.sdkManagerEnv); - if (runResult.exitCode != 0) + if (runResult.exitCode != 0) { return null; + } final List<String> availableDevices = runResult.stdout .split('\n') @@ -165,7 +165,7 @@ 'avd', '-n', 'temp', ]; - final ProcessResult runResult = processManager.runSync(args, + final RunResult runResult = processUtils.runSync(args, environment: androidSdk?.sdkManagerEnv); // Get the list of IDs that match our criteria
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart index ce118e6..5a2db26 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart
@@ -32,7 +32,7 @@ 'list', '-full' ]; - final RunResult result = await runAsync(command); + final RunResult result = await processUtils.run(command); if (result.exitCode != 0) { printError('dev_finder failed: ${result.stderr}'); return null; @@ -57,7 +57,7 @@ '-device-limit', '1', deviceName ]; - final RunResult result = await runAsync(command); + final RunResult result = await processUtils.run(command); if (result.exitCode != 0) { printError('dev_finder failed: ${result.stderr}'); return null;
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart index d731321..b0412df 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
@@ -454,7 +454,7 @@ throwToolExit('Cannot interact with device. No ssh config.\n' 'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.'); } - return await runAsync(<String>[ + return await processUtils.run(<String>[ 'ssh', '-F', fuchsiaArtifacts.sshConfig.absolute.path,
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_kernel_compiler.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_kernel_compiler.dart index 93db6ae..cccc8bb 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_kernel_compiler.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_kernel_compiler.dart
@@ -85,7 +85,7 @@ fuchsiaArtifacts.kernelCompiler.path, ...flags, ]; - final Process process = await runCommand(command); + final Process process = await processUtils.start(command); final Status status = logger.startProgress( 'Building Fuchsia application...', timeout: null,
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_pm.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_pm.dart index 55907ff..03fca09 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_pm.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_pm.dart
@@ -118,7 +118,7 @@ '-l', '$host:$port', ]; - final Process process = await runCommand(command); + final Process process = await processUtils.start(command); process.stdout .transform(utf8.decoder) .transform(const LineSplitter()) @@ -152,7 +152,7 @@ throwToolExit('Fuchsia pm tool not found'); } final List<String> command = <String>[fuchsiaArtifacts.pm.path] + args; - final RunResult result = await runAsync(command); + final RunResult result = await processUtils.run(command); return result.exitCode == 0; } }
diff --git a/packages/flutter_tools/lib/src/ios/code_signing.dart b/packages/flutter_tools/lib/src/ios/code_signing.dart index eefe21d..b238435 100644 --- a/packages/flutter_tools/lib/src/ios/code_signing.dart +++ b/packages/flutter_tools/lib/src/ios/code_signing.dart
@@ -96,8 +96,9 @@ BuildableIOSApp iosApp, }) async { final Map<String, String> buildSettings = iosApp.project.buildSettings; - if (buildSettings == null) + if (buildSettings == null) { return null; + } // If the user already has it set in the project build settings itself, // continue with that. @@ -114,16 +115,21 @@ // If the user's environment is missing the tools needed to find and read // certificates, abandon. Tools should be pre-equipped on macOS. - if (!exitsHappy(const <String>['which', 'security']) || !exitsHappy(const <String>['which', 'openssl'])) + if (!await processUtils.exitsHappy(const <String>['which', 'security']) || + !await processUtils.exitsHappy(const <String>['which', 'openssl'])) { return null; + } const List<String> findIdentityCommand = <String>['security', 'find-identity', '-p', 'codesigning', '-v']; String findIdentityStdout; try { - findIdentityStdout = runCheckedSync(findIdentityCommand); - } catch (error) { + findIdentityStdout = (await processUtils.run( + findIdentityCommand, + throwOnError: true, + )).stdout.trim(); + } on ProcessException catch (error) { printTrace('Unexpected failure from find-identity: $error.'); return null; } @@ -142,8 +148,9 @@ final String signingIdentity = await _chooseSigningIdentity(validCodeSigningIdentities); // If none are chosen, return null. - if (signingIdentity == null) + if (signingIdentity == null) { return null; + } printStatus('Signing iOS app for device deployment using developer identity: "$signingIdentity"'); @@ -153,20 +160,23 @@ ?.group(1); // If `security`'s output format changes, we'd have to update the above regex. - if (signingCertificateId == null) + if (signingCertificateId == null) { return null; + } String signingCertificateStdout; try { - signingCertificateStdout = runCheckedSync( - <String>['security', 'find-certificate', '-c', signingCertificateId, '-p'] - ); - } catch (error) { + signingCertificateStdout = (await processUtils.run( + <String>['security', 'find-certificate', '-c', signingCertificateId, '-p'], + throwOnError: true, + )).stdout.trim(); + } on ProcessException catch (error) { printTrace('Couldn\'t find the certificate: $error.'); return null; } - final Process opensslProcess = await runCommand(const <String>['openssl', 'x509', '-subject']); + final Process opensslProcess = await processUtils.start( + const <String>['openssl', 'x509', '-subject']); await (opensslProcess.stdin..write(signingCertificateStdout)).close(); final String opensslOutput = await utf8.decodeStream(opensslProcess.stdout);
diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart index bc907ed..3d0f1f5 100644 --- a/packages/flutter_tools/lib/src/ios/devices.dart +++ b/packages/flutter_tools/lib/src/ios/devices.dart
@@ -64,7 +64,7 @@ iosDeployEnv['PATH'] = '/usr/bin:${iosDeployEnv['PATH']}'; iosDeployEnv.addEntries(<MapEntry<String, String>>[cache.dyLdLibEntry]); - return await runCommandAndStreamOutput( + return await processUtils.stream( launchCommand, mapFunction: _monitorInstallationFailure, trace: true, @@ -195,8 +195,9 @@ Future<bool> isAppInstalled(ApplicationPackage app) async { RunResult apps; try { - apps = await runCheckedAsync( + apps = await processUtils.run( <String>[_installerPath, '--list-apps'], + throwOnError: true, environment: Map<String, String>.fromEntries( <MapEntry<String, String>>[cache.dyLdLibEntry], ), @@ -220,8 +221,9 @@ } try { - await runCheckedAsync( + await processUtils.run( <String>[_installerPath, '-i', iosApp.deviceBundlePath], + throwOnError: true, environment: Map<String, String>.fromEntries( <MapEntry<String, String>>[cache.dyLdLibEntry], ), @@ -236,8 +238,9 @@ @override Future<bool> uninstallApp(ApplicationPackage app) async { try { - await runCheckedAsync( + await processUtils.run( <String>[_installerPath, '-U', app.id], + throwOnError: true, environment: Map<String, String>.fromEntries( <MapEntry<String, String>>[cache.dyLdLibEntry], ), @@ -610,7 +613,7 @@ while (!connected) { printTrace('attempting to forward device port $devicePort to host port $hostPort'); // Usage: iproxy LOCAL_TCP_PORT DEVICE_TCP_PORT UDID - process = await runCommand( + process = await processUtils.start( <String>[ device._iproxyPath, hostPort.toString(),
diff --git a/packages/flutter_tools/lib/src/ios/ios_emulators.dart b/packages/flutter_tools/lib/src/ios/ios_emulators.dart index eec2249..a3bd885 100644 --- a/packages/flutter_tools/lib/src/ios/ios_emulators.dart +++ b/packages/flutter_tools/lib/src/ios/ios_emulators.dart
@@ -47,7 +47,7 @@ .followedBy(<String>['-a', xcode.getSimulatorPath()]) .toList(); - final RunResult launchResult = await runAsync(args); + final RunResult launchResult = await processUtils.run(args); if (launchResult.exitCode != 0) { printError('$launchResult'); return false;
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart index 9a491a6..8749ec8 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -107,7 +107,7 @@ final String _idevicescreenshotPath; bool get isInstalled { - _isInstalled ??= exitsHappy( + _isInstalled ??= processUtils.exitsHappySync( <String>[ _ideviceIdPath, '-h' @@ -136,7 +136,7 @@ final Map<String, String> executionEnv = Map<String, String>.fromEntries( <MapEntry<String, String>>[cache.dyLdLibEntry] ); - final ProcessResult ideviceResult = (await runAsync( + final ProcessResult ideviceResult = (await processUtils.run( <String>[ _ideviceinfoPath, '-u', @@ -150,7 +150,7 @@ } // If no device is attached, we're unable to detect any problems. Assume all is well. - final ProcessResult result = (await runAsync( + final ProcessResult result = (await processUtils.run( <String>[ _ideviceIdPath, '-l', @@ -161,7 +161,7 @@ _isWorking = true; } else { // Check that we can look up the names of any attached devices. - _isWorking = await exitsHappyAsync( + _isWorking = await processUtils.exitsHappy( <String>[_idevicenamePath], environment: executionEnv, ); @@ -229,7 +229,7 @@ /// Starts `idevicesyslog` and returns the running process. Future<Process> startLogger(String deviceID) { - return runCommand( + return processUtils.start( <String>[ _idevicesyslogPath, '-u', @@ -243,11 +243,12 @@ /// Captures a screenshot to the specified outputFile. Future<void> takeScreenshot(File outputFile) { - return runCheckedAsync( + return processUtils.run( <String>[ _idevicescreenshotPath, outputFile.path ], + throwOnError: true, environment: Map<String, String>.fromEntries( <MapEntry<String, String>>[cache.dyLdLibEntry] ), @@ -318,8 +319,9 @@ } Map<String, String> autoSigningConfigs; - if (codesign && buildForDevice) + if (codesign && buildForDevice) { autoSigningConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app); + } // Before the build, all service definitions must be updated and the dylibs // copied over to a location that is suitable for Xcodebuild to find them. @@ -440,7 +442,7 @@ final Stopwatch sw = Stopwatch()..start(); initialBuildStatus = logger.startProgress('Running Xcode build...', timeout: timeoutConfiguration.fastOperation); - final RunResult buildResult = await runAsync( + final RunResult buildResult = await processUtils.run( buildCommands, workingDirectory: app.project.hostAppRoot.path, allowReentrantFlutter: true, @@ -476,8 +478,9 @@ const Duration showBuildSettingsTimeout = Duration(minutes: 1); Map<String, String> buildSettings; try { - final RunResult showBuildSettingsResult = await runCheckedAsync( + final RunResult showBuildSettingsResult = await processUtils.run( showBuildSettingsCommand, + throwOnError: true, workingDirectory: app.project.hostAppRoot.path, timeout: showBuildSettingsTimeout, timeoutRetries: 1, @@ -677,7 +680,10 @@ continue; } // Shell out so permissions on the dylib are preserved. - await runCheckedAsync(<String>['/bin/cp', dylib.path, frameworksDirectory.path]); + await processUtils.run( + <String>['/bin/cp', dylib.path, frameworksDirectory.path], + throwOnError: true, + ); } }
diff --git a/packages/flutter_tools/lib/src/ios/plist_parser.dart b/packages/flutter_tools/lib/src/ios/plist_parser.dart index 8e8beeb..f626fe6 100644 --- a/packages/flutter_tools/lib/src/ios/plist_parser.dart +++ b/packages/flutter_tools/lib/src/ios/plist_parser.dart
@@ -4,6 +4,7 @@ import '../base/context.dart'; import '../base/file_system.dart'; +import '../base/io.dart'; import '../base/process.dart'; import '../convert.dart'; import '../globals.dart'; @@ -38,9 +39,12 @@ final List<String> args = <String>[ executable, '-convert', 'json', '-o', '-', normalizedPlistPath, ]; - final String jsonContent = runCheckedSync(args); + final String jsonContent = processUtils.runSync( + args, + throwOnError: true, + ).stdout.trim(); return json.decode(jsonContent); - } catch (error) { + } on ProcessException catch (error) { printTrace('$error'); return const <String, dynamic>{}; }
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index edf150f..a8f912c 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -130,7 +130,7 @@ } Future<bool> isInstalled(String deviceId, String appId) { - return exitsHappyAsync(<String>[ + return processUtils.exitsHappy(<String>[ _xcrunPath, 'simctl', 'get_app_container', @@ -142,7 +142,10 @@ Future<RunResult> install(String deviceId, String appPath) { Future<RunResult> result; try { - result = runCheckedAsync(<String>[_xcrunPath, 'simctl', 'install', deviceId, appPath]); + result = processUtils.run( + <String>[_xcrunPath, 'simctl', 'install', deviceId, appPath], + throwOnError: true, + ); } on ProcessException catch (exception) { throwToolExit('Unable to install $appPath on $deviceId:\n$exception'); } @@ -152,7 +155,10 @@ Future<RunResult> uninstall(String deviceId, String appId) { Future<RunResult> result; try { - result = runCheckedAsync(<String>[_xcrunPath, 'simctl', 'uninstall', deviceId, appId]); + result = processUtils.run( + <String>[_xcrunPath, 'simctl', 'uninstall', deviceId, appId], + throwOnError: true, + ); } on ProcessException catch (exception) { throwToolExit('Unable to uninstall $appId from $deviceId:\n$exception'); } @@ -162,14 +168,17 @@ Future<RunResult> launch(String deviceId, String appIdentifier, [ List<String> launchArgs ]) { Future<RunResult> result; try { - result = runCheckedAsync(<String>[ - _xcrunPath, - 'simctl', - 'launch', - deviceId, - appIdentifier, - ...?launchArgs, - ]); + result = processUtils.run( + <String>[ + _xcrunPath, + 'simctl', + 'launch', + deviceId, + appIdentifier, + ...?launchArgs, + ], + throwOnError: true, + ); } on ProcessException catch (exception) { throwToolExit('Unable to launch $appIdentifier on $deviceId:\n$exception'); } @@ -178,7 +187,10 @@ Future<void> takeScreenshot(String deviceId, String outputPath) async { try { - await runCheckedAsync(<String>[_xcrunPath, 'simctl', 'io', deviceId, 'screenshot', outputPath]); + await processUtils.run( + <String>[_xcrunPath, 'simctl', 'io', deviceId, 'screenshot', outputPath], + throwOnError: true, + ); } on ProcessException catch (exception) { throwToolExit('Unable to take screenshot of $deviceId:\n$exception'); } @@ -518,20 +530,22 @@ /// Launches the device log reader process on the host. Future<Process> launchDeviceLogTool(IOSSimulator device) async { // Versions of iOS prior to iOS 11 log to the simulator syslog file. - if (await device.sdkMajorVersion < 11) - return runCommand(<String>['tail', '-n', '0', '-F', device.logFilePath]); + if (await device.sdkMajorVersion < 11) { + return processUtils.start(<String>['tail', '-n', '0', '-F', device.logFilePath]); + } // For iOS 11 and above, use /usr/bin/log to tail process logs. // Run in interactive mode (via script), otherwise /usr/bin/log buffers in 4k chunks. (radar: 34420207) - return runCommand(<String>[ + return processUtils.start(<String>[ 'script', '/dev/null', '/usr/bin/log', 'stream', '--style', 'syslog', '--predicate', 'processImagePath CONTAINS "${device.id}"', ]); } Future<Process> launchSystemLogTool(IOSSimulator device) async { // Versions of iOS prior to 11 tail the simulator syslog file. - if (await device.sdkMajorVersion < 11) - return runCommand(<String>['tail', '-n', '0', '-F', '/private/var/log/system.log']); + if (await device.sdkMajorVersion < 11) { + return processUtils.start(<String>['tail', '-n', '0', '-F', '/private/var/log/system.log']); + } // For iOS 11 and later, all relevant detail is in the device log. return null;
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart index 7a52b55..181905f 100644 --- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart +++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -14,7 +14,6 @@ import '../base/os.dart'; import '../base/platform.dart'; import '../base/process.dart'; -import '../base/process_manager.dart'; import '../base/utils.dart'; import '../build_info.dart'; import '../cache.dart'; @@ -211,7 +210,9 @@ return; } try { - final ProcessResult result = processManager.runSync(<String>[_executable, '-version']); + final RunResult result = processUtils.runSync( + <String>[_executable, '-version'], + ); if (result.exitCode != 0) { return; } @@ -255,16 +256,20 @@ /// version below. Map<String, String> getBuildSettings(String projectPath, String target) { try { - final String out = runCheckedSync(<String>[ - _executable, - '-project', - fs.path.absolute(projectPath), - '-target', - target, - '-showBuildSettings', - ], workingDirectory: projectPath); + final String out = processUtils.runSync( + <String>[ + _executable, + '-project', + fs.path.absolute(projectPath), + '-target', + target, + '-showBuildSettings', + ], + throwOnError: true, + workingDirectory: projectPath, + ).stdout.trim(); return parseXcodeBuildSettings(out); - } catch(error) { + } on ProcessException catch (error) { printTrace('Unexpected failure to get the build settings: $error.'); return const <String, String>{}; } @@ -291,8 +296,9 @@ // showBuildSettings is reported to ocassionally timeout. Here, we give it // a lot of wiggle room (locally on Flutter Gallery, this takes ~1s). // When there is a timeout, we retry once. - final RunResult result = await runCheckedAsync( + final RunResult result = await processUtils.run( showBuildSettingsCommand, + throwOnError: true, workingDirectory: projectPath, timeout: timeout, timeoutRetries: 1, @@ -313,7 +319,7 @@ } void cleanWorkspace(String workspacePath, String scheme) { - runSync(<String>[ + processUtils.runSync(<String>[ _executable, '-workspace', workspacePath, @@ -325,11 +331,15 @@ } Future<XcodeProjectInfo> getInfo(String projectPath, {String projectFilename}) async { - final RunResult result = await runCheckedAsync(<String>[ - _executable, - '-list', - if (projectFilename != null) ...<String>['-project', projectFilename], - ], workingDirectory: projectPath); + final RunResult result = await processUtils.run( + <String>[ + _executable, + '-list', + if (projectFilename != null) ...<String>['-project', projectFilename], + ], + throwOnError: true, + workingDirectory: projectPath, + ); return XcodeProjectInfo.fromXcodeBuildOutput(result.toString()); } }
diff --git a/packages/flutter_tools/lib/src/macos/cocoapods.dart b/packages/flutter_tools/lib/src/macos/cocoapods.dart index fac60ca..08e2057 100644 --- a/packages/flutter_tools/lib/src/macos/cocoapods.dart +++ b/packages/flutter_tools/lib/src/macos/cocoapods.dart
@@ -67,10 +67,11 @@ String get cocoaPodsMinimumVersion => '1.6.0'; String get cocoaPodsRecommendedVersion => '1.6.0'; - Future<bool> get isInstalled => exitsHappyAsync(<String>['which', 'pod']); + Future<bool> get isInstalled => + processUtils.exitsHappy(<String>['which', 'pod']); Future<String> get cocoaPodsVersionText { - _versionText ??= runAsync(<String>['pod', '--version']).then<String>((RunResult result) { + _versionText ??= processUtils.run(<String>['pod', '--version']).then<String>((RunResult result) { return result.exitCode == 0 ? result.stdout.trim() : null; }, onError: (dynamic _) => null); return _versionText;
diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart index 8888f8d..5ab06a0 100644 --- a/packages/flutter_tools/lib/src/macos/xcode.dart +++ b/packages/flutter_tools/lib/src/macos/xcode.dart
@@ -10,7 +10,6 @@ import '../base/io.dart'; import '../base/platform.dart'; import '../base/process.dart'; -import '../base/process_manager.dart'; import '../ios/xcodeproj.dart'; const int kXcodeRequiredVersionMajor = 9; @@ -25,7 +24,9 @@ String get xcodeSelectPath { if (_xcodeSelectPath == null) { try { - _xcodeSelectPath = processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']).stdout.trim(); + _xcodeSelectPath = processUtils.runSync( + <String>['/usr/bin/xcode-select', '--print-path'], + ).stdout.trim(); } on ProcessException { // Ignored, return null below. } on ArgumentError { @@ -52,13 +53,16 @@ bool get eulaSigned { if (_eulaSigned == null) { try { - final ProcessResult result = processManager.runSync(<String>['/usr/bin/xcrun', 'clang']); - if (result.stdout != null && result.stdout.contains('license')) + final RunResult result = processUtils.runSync( + <String>['/usr/bin/xcrun', 'clang'], + ); + if (result.stdout != null && result.stdout.contains('license')) { _eulaSigned = false; - else if (result.stderr != null && result.stderr.contains('license')) + } else if (result.stderr != null && result.stderr.contains('license')) { _eulaSigned = false; - else + } else { _eulaSigned = true; + } } on ProcessException { _eulaSigned = false; } @@ -74,7 +78,9 @@ try { // This command will error if additional components need to be installed in // xcode 9.2 and above. - final ProcessResult result = processManager.runSync(<String>['/usr/bin/xcrun', 'simctl', 'list']); + final RunResult result = processUtils.runSync( + <String>['/usr/bin/xcrun', 'simctl', 'list'], + ); _isSimctlInstalled = result.stderr == null || result.stderr == ''; } on ProcessException { _isSimctlInstalled = false; @@ -94,16 +100,23 @@ } Future<RunResult> cc(List<String> args) { - return runCheckedAsync(<String>['xcrun', 'cc', ...args]); + return processUtils.run( + <String>['xcrun', 'cc', ...args], + throwOnError: true, + ); } Future<RunResult> clang(List<String> args) { - return runCheckedAsync(<String>['xcrun', 'clang', ...args]); + return processUtils.run( + <String>['xcrun', 'clang', ...args], + throwOnError: true, + ); } Future<String> iPhoneSdkLocation() async { - final RunResult runResult = await runCheckedAsync( + final RunResult runResult = await processUtils.run( <String>['xcrun', '--sdk', 'iphoneos', '--show-sdk-path'], + throwOnError: true, ); if (runResult.exitCode != 0) { throwToolExit('Could not find iPhone SDK location: ${runResult.stderr}');
diff --git a/packages/flutter_tools/lib/src/test/coverage_collector.dart b/packages/flutter_tools/lib/src/test/coverage_collector.dart index 4a2b444..1ce8a9d 100644 --- a/packages/flutter_tools/lib/src/test/coverage_collector.dart +++ b/packages/flutter_tools/lib/src/test/coverage_collector.dart
@@ -11,7 +11,7 @@ import '../base/logger.dart'; import '../base/os.dart'; import '../base/platform.dart'; -import '../base/process_manager.dart'; +import '../base/process.dart'; import '../dart/package_map.dart'; import '../globals.dart'; import '../vmservice.dart'; @@ -150,7 +150,7 @@ final Directory tempDir = fs.systemTempDirectory.createTempSync('flutter_tools_test_coverage.'); try { final File sourceFile = coverageFile.copySync(fs.path.join(tempDir.path, 'lcov.source.info')); - final ProcessResult result = processManager.runSync(<String>[ + final RunResult result = processUtils.runSync(<String>[ 'lcov', '--add-tracefile', baseCoverageData, '--add-tracefile', sourceFile.path,
diff --git a/packages/flutter_tools/lib/src/version.dart b/packages/flutter_tools/lib/src/version.dart index e42cd6d..df4185e 100644 --- a/packages/flutter_tools/lib/src/version.dart +++ b/packages/flutter_tools/lib/src/version.dart
@@ -538,7 +538,10 @@ } String _runGit(String command) { - return runSync(command.split(' '), workingDirectory: Cache.flutterRoot); + return processUtils.runSync( + command.split(' '), + workingDirectory: Cache.flutterRoot, + ).stdout.trim(); } /// Runs [command] in the root of the Flutter installation and returns the
diff --git a/packages/flutter_tools/lib/src/windows/visual_studio.dart b/packages/flutter_tools/lib/src/windows/visual_studio.dart index b342cf4..31fd027 100644 --- a/packages/flutter_tools/lib/src/windows/visual_studio.dart +++ b/packages/flutter_tools/lib/src/windows/visual_studio.dart
@@ -6,7 +6,7 @@ import '../base/file_system.dart'; import '../base/io.dart'; import '../base/platform.dart'; -import '../base/process_manager.dart'; +import '../base/process.dart'; import '../convert.dart'; VisualStudio get visualStudio => context.get<VisualStudio>(); @@ -167,7 +167,7 @@ '-utf8', '-latest', ]; - final ProcessResult whereResult = processManager.runSync(<String>[ + final RunResult whereResult = processUtils.runSync(<String>[ _vswherePath, ...defaultArguments, ...?additionalArguments,
diff --git a/packages/flutter_tools/lib/src/windows/windows_device.dart b/packages/flutter_tools/lib/src/windows/windows_device.dart index 8a714a9..05944f9 100644 --- a/packages/flutter_tools/lib/src/windows/windows_device.dart +++ b/packages/flutter_tools/lib/src/windows/windows_device.dart
@@ -8,7 +8,7 @@ import '../base/io.dart'; import '../base/os.dart'; import '../base/platform.dart'; -import '../base/process_manager.dart'; +import '../base/process.dart'; import '../build_info.dart'; import '../desktop.dart'; import '../device.dart'; @@ -88,7 +88,7 @@ ); } await stopApp(package); - final Process process = await processManager.start(<String>[ + final Process process = await processUtils.start(<String>[ package.executable(debuggingOptions?.buildInfo?.mode) ]); if (debuggingOptions?.buildInfo?.isRelease == true) { @@ -114,7 +114,9 @@ if (process == null) { return false; } - final ProcessResult result = await processManager.run(<String>['Taskkill', '/PID', process.first, '/F']); + final RunResult result = await processUtils.run( + <String>['Taskkill', '/PID', process.first, '/F'], + ); return result.exitCode == 0; } @@ -163,7 +165,9 @@ @visibleForTesting List<String> runningProcess(String processName) { // TODO(jonahwilliams): find a way to do this without powershell. - final ProcessResult result = processManager.runSync(<String>['powershell', '-script="Get-CimInstance Win32_Process"']); + final RunResult result = processUtils.runSync( + <String>['powershell', '-script="Get-CimInstance Win32_Process"'], + ); if (result.exitCode != 0) { return null; }
diff --git a/packages/flutter_tools/test/general.shard/application_package_test.dart b/packages/flutter_tools/test/general.shard/application_package_test.dart index c2fbedb..a374142 100644 --- a/packages/flutter_tools/test/general.shard/application_package_test.dart +++ b/packages/flutter_tools/test/general.shard/application_package_test.dart
@@ -87,7 +87,7 @@ workingDirectory: anyNamed('workingDirectory'), environment: anyNamed('environment'), ), - ).thenReturn(ProcessResult(0, 0, _aaptDataWithDefaultEnabledAndMainLauncherActivity, null)); + ).thenReturn(ProcessResult(0, 0, _aaptDataWithDefaultEnabledAndMainLauncherActivity, '')); final ApplicationPackage applicationPackage = await ApplicationPackageFactory.instance.getPackageForPlatform( TargetPlatform.android_arm,
diff --git a/packages/flutter_tools/test/general.shard/base/process_test.dart b/packages/flutter_tools/test/general.shard/base/process_test.dart index 9c8981c..88e7dd2 100644 --- a/packages/flutter_tools/test/general.shard/base/process_test.dart +++ b/packages/flutter_tools/test/general.shard/base/process_test.dart
@@ -26,10 +26,11 @@ mockProcessManager = PlainMockProcessManager(); }); - testUsingContext('runCheckedAsync exceptions should be ProcessException objects', () async { - when(mockProcessManager.run(<String>['false'])) - .thenAnswer((Invocation invocation) => Future<ProcessResult>.value(ProcessResult(0, 1, '', ''))); - expect(() async => await runCheckedAsync(<String>['false']), throwsA(isInstanceOf<ProcessException>())); + testUsingContext('runAsync throwOnError: exceptions should be ProcessException objects', () async { + when(mockProcessManager.run(<String>['false'])).thenAnswer( + (Invocation invocation) => Future<ProcessResult>.value(ProcessResult(0, 1, '', ''))); + expect(() async => await processUtils.run(<String>['false'], throwOnError: true), + throwsA(isInstanceOf<ProcessException>())); }, overrides: <Type, Generator>{ProcessManager: () => mockProcessManager}); }); @@ -86,7 +87,7 @@ testUsingContext('Command output is not wrapped.', () async { final List<String> testString = <String>['0123456789' * 10]; mockProcessManager.processFactory = processMetaFactory(testString, stderr: testString); - await runCommandAndStreamOutput(<String>['command']); + await processUtils.stream(<String>['command']); expect(mockLogger.statusText, equals('${testString[0]}\n')); expect(mockLogger.errorText, equals('${testString[0]}\n')); }, overrides: <Type, Generator>{ @@ -97,22 +98,82 @@ }); }); - group('runAsync timeout and retry', () { + group('run', () { const Duration delay = Duration(seconds: 2); MockProcessManager flakyProcessManager; + ProcessManager mockProcessManager; setUp(() { // MockProcessManager has an implementation of start() that returns the // result of processFactory. flakyProcessManager = MockProcessManager(); + mockProcessManager = MockProcessManager(); }); - testUsingContext('flaky process fails without retry', () async { + testUsingContext(' succeeds on success', () async { + when(mockProcessManager.run(<String>['whoohoo'])).thenAnswer((_) { + return Future<ProcessResult>.value(ProcessResult(0, 0, '', '')); + }); + expect((await processUtils.run(<String>['whoohoo'])).exitCode, 0); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' fails on failure', () async { + when(mockProcessManager.run(<String>['boohoo'])).thenAnswer((_) { + return Future<ProcessResult>.value(ProcessResult(0, 1, '', '')); + }); + expect((await processUtils.run(<String>['boohoo'])).exitCode, 1); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' throws on failure with throwOnError', () async { + when(mockProcessManager.run(<String>['kaboom'])).thenAnswer((_) { + return Future<ProcessResult>.value(ProcessResult(0, 1, '', '')); + }); + expect(() => processUtils.run(<String>['kaboom'], throwOnError: true), + throwsA(isA<ProcessException>())); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' does not throw on failure with whitelist', () async { + when(mockProcessManager.run(<String>['kaboom'])).thenAnswer((_) { + return Future<ProcessResult>.value(ProcessResult(0, 1, '', '')); + }); + expect( + (await processUtils.run( + <String>['kaboom'], + throwOnError: true, + whiteListFailures: (int c) => c == 1, + )).exitCode, + 1); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' throws on failure when not in whitelist', () async { + when(mockProcessManager.run(<String>['kaboom'])).thenAnswer((_) { + return Future<ProcessResult>.value(ProcessResult(0, 2, '', '')); + }); + expect( + () => processUtils.run( + <String>['kaboom'], + throwOnError: true, + whiteListFailures: (int c) => c == 1, + ), + throwsA(isA<ProcessException>())); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' flaky process fails without retry', () async { flakyProcessManager.processFactory = flakyProcessFactory( flakes: 1, delay: delay, ); - final RunResult result = await runAsync( + final RunResult result = await processUtils.run( <String>['dummy'], timeout: delay + const Duration(seconds: 1), ); @@ -121,12 +182,12 @@ ProcessManager: () => flakyProcessManager, }); - testUsingContext('flaky process succeeds with retry', () async { + testUsingContext(' flaky process succeeds with retry', () async { flakyProcessManager.processFactory = flakyProcessFactory( flakes: 1, delay: delay, ); - final RunResult result = await runAsync( + final RunResult result = await processUtils.run( <String>['dummy'], timeout: delay - const Duration(milliseconds: 500), timeoutRetries: 1, @@ -136,7 +197,7 @@ ProcessManager: () => flakyProcessManager, }); - testUsingContext('flaky process generates ProcessException on timeout', () async { + testUsingContext(' flaky process generates ProcessException on timeout', () async { final Completer<List<int>> flakyStderr = Completer<List<int>>(); final Completer<List<int>> flakyStdout = Completer<List<int>>(); flakyProcessManager.processFactory = flakyProcessFactory( @@ -153,7 +214,7 @@ flakyStdout.complete(<int>[]); return true; }); - expect(() async => await runAsync( + expect(() => processUtils.run( <String>['dummy'], timeout: delay - const Duration(milliseconds: 500), timeoutRetries: 0, @@ -162,6 +223,159 @@ ProcessManager: () => flakyProcessManager, }); }); + + group('runSync', () { + ProcessManager mockProcessManager; + + setUp(() { + mockProcessManager = MockProcessManager(); + }); + + testUsingContext(' succeeds on success', () async { + when(mockProcessManager.runSync(<String>['whoohoo'])).thenReturn( + ProcessResult(0, 0, '', '') + ); + expect(processUtils.runSync(<String>['whoohoo']).exitCode, 0); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' fails on failure', () async { + when(mockProcessManager.runSync(<String>['boohoo'])).thenReturn( + ProcessResult(0, 1, '', '') + ); + expect(processUtils.runSync(<String>['boohoo']).exitCode, 1); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' throws on failure with throwOnError', () async { + when(mockProcessManager.runSync(<String>['kaboom'])).thenReturn( + ProcessResult(0, 1, '', '') + ); + expect(() => processUtils.runSync(<String>['kaboom'], throwOnError: true), + throwsA(isA<ProcessException>())); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' does not throw on failure with whitelist', () async { + when(mockProcessManager.runSync(<String>['kaboom'])).thenReturn( + ProcessResult(0, 1, '', '') + ); + expect( + processUtils.runSync( + <String>['kaboom'], + throwOnError: true, + whiteListFailures: (int c) => c == 1, + ).exitCode, + 1); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' throws on failure when not in whitelist', () async { + when(mockProcessManager.runSync(<String>['kaboom'])).thenReturn( + ProcessResult(0, 2, '', '') + ); + expect( + () => processUtils.runSync( + <String>['kaboom'], + throwOnError: true, + whiteListFailures: (int c) => c == 1, + ), + throwsA(isA<ProcessException>())); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' prints stdout and stderr to trace on success', () async { + when(mockProcessManager.runSync(<String>['whoohoo'])).thenReturn( + ProcessResult(0, 0, 'stdout', 'stderr') + ); + expect(processUtils.runSync(<String>['whoohoo']).exitCode, 0); + expect(testLogger.traceText, contains('stdout')); + expect(testLogger.traceText, contains('stderr')); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' prints stdout to status and stderr to error on failure with throwOnError', () async { + when(mockProcessManager.runSync(<String>['kaboom'])).thenReturn( + ProcessResult(0, 1, 'stdout', 'stderr') + ); + expect(() => processUtils.runSync(<String>['kaboom'], throwOnError: true), + throwsA(isA<ProcessException>())); + expect(testLogger.statusText, contains('stdout')); + expect(testLogger.errorText, contains('stderr')); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' does not print stdout with hideStdout', () async { + when(mockProcessManager.runSync(<String>['whoohoo'])).thenReturn( + ProcessResult(0, 0, 'stdout', 'stderr') + ); + expect(processUtils.runSync(<String>['whoohoo'], hideStdout: true).exitCode, 0); + expect(testLogger.traceText.contains('stdout'), isFalse); + expect(testLogger.traceText, contains('stderr')); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + }); + + group('exitsHappySync', () { + ProcessManager mockProcessManager; + + setUp(() { + mockProcessManager = MockProcessManager(); + }); + + testUsingContext(' succeeds on success', () async { + when(mockProcessManager.runSync(<String>['whoohoo'])).thenReturn( + ProcessResult(0, 0, '', '') + ); + expect(processUtils.exitsHappySync(<String>['whoohoo']), isTrue); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' fails on failure', () async { + when(mockProcessManager.runSync(<String>['boohoo'])).thenReturn( + ProcessResult(0, 1, '', '') + ); + expect(processUtils.exitsHappySync(<String>['boohoo']), isFalse); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + }); + + group('exitsHappy', () { + ProcessManager mockProcessManager; + + setUp(() { + mockProcessManager = MockProcessManager(); + }); + + testUsingContext(' succeeds on success', () async { + when(mockProcessManager.run(<String>['whoohoo'])).thenAnswer((_) { + return Future<ProcessResult>.value(ProcessResult(0, 0, '', '')); + }); + expect(await processUtils.exitsHappy(<String>['whoohoo']), isTrue); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + + testUsingContext(' fails on failure', () async { + when(mockProcessManager.run(<String>['boohoo'])).thenAnswer((_) { + return Future<ProcessResult>.value(ProcessResult(0, 1, '', '')); + }); + expect(await processUtils.exitsHappy(<String>['boohoo']), isFalse); + }, overrides: <Type, Generator>{ + ProcessManager: () => mockProcessManager, + }); + }); + } class PlainMockProcessManager extends Mock implements ProcessManager {}
diff --git a/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart b/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart index b709295..1d85a9a 100644 --- a/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/code_signing_test.dart
@@ -59,8 +59,8 @@ }); testUsingContext('No auto-sign if security or openssl not available', () async { - when(mockProcessManager.runSync(<String>['which', 'security'])) - .thenReturn(exitsFail); + when(mockProcessManager.run(<String>['which', 'security'])) + .thenAnswer((_) => Future<ProcessResult>.value(exitsFail)); final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app); expect(signingConfigs, isNull); }, @@ -69,15 +69,21 @@ }); testUsingContext('No valid code signing certificates shows instructions', () async { - when(mockProcessManager.runSync(<String>['which', 'security'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync(<String>['which', 'openssl'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync( - argThat(contains('find-identity')), + when(mockProcessManager.run( + <String>['which', 'security'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + <String>['which', 'openssl'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + argThat(contains('find-identity')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(exitsHappy); + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); Map<String, String> signingConfigs; @@ -95,39 +101,45 @@ }); testUsingContext('Test single identity and certificate organization works', () async { - when(mockProcessManager.runSync(<String>['which', 'security'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync(<String>['which', 'openssl'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync( - argThat(contains('find-identity')), + when(mockProcessManager.run( + <String>['which', 'security'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + <String>['which', 'openssl'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + argThat(contains('find-identity')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode ''' 1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "iPhone Developer: Profile 1 (1111AAAA11)" 1 valid identities found''', '', - )); - when(mockProcessManager.runSync( + ))); + when(mockProcessManager.run( <String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'], environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode 'This is a mock certificate', '', - )); + ))); final MockProcess mockProcess = MockProcess(); final MockStdIn mockStdIn = MockStdIn(); final MockStream mockStdErr = MockStream(); when(mockProcessManager.start( - argThat(contains('openssl')), + argThat(contains('openssl')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), )).thenAnswer((Invocation invocation) => Future<Process>.value(mockProcess)); @@ -156,39 +168,45 @@ testUsingContext('Test single identity (Catalina format) and certificate organization works', () async { - when(mockProcessManager.runSync(<String>['which', 'security'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync(<String>['which', 'openssl'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync( - argThat(contains('find-identity')), + when(mockProcessManager.run( + <String>['which', 'security'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + <String>['which', 'openssl'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + argThat(contains('find-identity')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode ''' 1) 86f7e437faa5a7fce15d1ddcb9eaeaea377667b8 "Apple Development: Profile 1 (1111AAAA11)" 1 valid identities found''', '', - )); - when(mockProcessManager.runSync( + ))); + when(mockProcessManager.run( <String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'], environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode 'This is a mock certificate', '', - )); + ))); final MockProcess mockProcess = MockProcess(); final MockStdIn mockStdIn = MockStdIn(); final MockStream mockStdErr = MockStream(); when(mockProcessManager.start( - argThat(contains('openssl')), + argThat(contains('openssl')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), )).thenAnswer((Invocation invocation) => Future<Process>.value(mockProcess)); @@ -222,15 +240,21 @@ }); testUsingContext('Test multiple identity and certificate organization works', () async { - when(mockProcessManager.runSync(<String>['which', 'security'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync(<String>['which', 'openssl'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync( - argThat(contains('find-identity')), + when(mockProcessManager.run( + <String>['which', 'security'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + <String>['which', 'openssl'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + argThat(contains('find-identity')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode ''' @@ -239,26 +263,26 @@ 3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)" 3 valid identities found''', '', - )); + ))); mockTerminalStdInStream = Stream<String>.fromFuture(Future<String>.value('3')); - when(mockProcessManager.runSync( + when(mockProcessManager.run( <String>['security', 'find-certificate', '-c', '3333CCCC33', '-p'], environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode 'This is a mock certificate', '', - )); + ))); final MockProcess mockOpenSslProcess = MockProcess(); final MockStdIn mockOpenSslStdIn = MockStdIn(); final MockStream mockOpenSslStdErr = MockStream(); when(mockProcessManager.start( - argThat(contains('openssl')), + argThat(contains('openssl')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), )).thenAnswer((Invocation invocation) => Future<Process>.value(mockOpenSslProcess)); @@ -298,15 +322,21 @@ testUsingContext('Test multiple identity in machine mode works', () async { testTerminal.usesTerminalUi = false; - when(mockProcessManager.runSync(<String>['which', 'security'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync(<String>['which', 'openssl'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync( - argThat(contains('find-identity')), + when(mockProcessManager.run( + <String>['which', 'security'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + <String>['which', 'openssl'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + argThat(contains('find-identity')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode ''' @@ -315,26 +345,26 @@ 3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)" 3 valid identities found''', '', - )); + ))); mockTerminalStdInStream = Stream<String>.fromFuture(Future<String>.error(Exception('Cannot read from StdIn'))); - when(mockProcessManager.runSync( + when(mockProcessManager.run( <String>['security', 'find-certificate', '-c', '1111AAAA11', '-p'], environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode 'This is a mock certificate', '', - )); + ))); final MockProcess mockOpenSslProcess = MockProcess(); final MockStdIn mockOpenSslStdIn = MockStdIn(); final MockStream mockOpenSslStdErr = MockStream(); when(mockProcessManager.start( - argThat(contains('openssl')), + argThat(contains('openssl')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), )).thenAnswer((Invocation invocation) => Future<Process>.value(mockOpenSslProcess)); @@ -367,15 +397,21 @@ }); testUsingContext('Test saved certificate used', () async { - when(mockProcessManager.runSync(<String>['which', 'security'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync(<String>['which', 'openssl'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync( - argThat(contains('find-identity')), + when(mockProcessManager.run( + <String>['which', 'security'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + <String>['which', 'openssl'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + argThat(contains('find-identity')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode ''' @@ -384,24 +420,24 @@ 3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)" 3 valid identities found''', '', - )); - when(mockProcessManager.runSync( + ))); + when(mockProcessManager.run( <String>['security', 'find-certificate', '-c', '3333CCCC33', '-p'], environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode 'This is a mock certificate', '', - )); + ))); final MockProcess mockOpenSslProcess = MockProcess(); final MockStdIn mockOpenSslStdIn = MockStdIn(); final MockStream mockOpenSslStdErr = MockStream(); when(mockProcessManager.start( - argThat(contains('openssl')), + argThat(contains('openssl')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), )).thenAnswer((Invocation invocation) => Future<Process>.value(mockOpenSslProcess)); @@ -438,15 +474,21 @@ }); testUsingContext('Test invalid saved certificate shows error and prompts again', () async { - when(mockProcessManager.runSync(<String>['which', 'security'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync(<String>['which', 'openssl'])) - .thenReturn(exitsHappy); - when(mockProcessManager.runSync( - argThat(contains('find-identity')), + when(mockProcessManager.run( + <String>['which', 'security'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + <String>['which', 'openssl'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + argThat(contains('find-identity')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode ''' @@ -455,19 +497,19 @@ 3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)" 3 valid identities found''', '', - )); + ))); mockTerminalStdInStream = Stream<String>.fromFuture(Future<String>.value('3')); - when(mockProcessManager.runSync( + when(mockProcessManager.run( <String>['security', 'find-certificate', '-c', '3333CCCC33', '-p'], environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult( + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode 'This is a mock certificate', '', - )); + ))); final MockProcess mockOpenSslProcess = MockProcess(); @@ -475,7 +517,7 @@ final MockStream mockOpenSslStdErr = MockStream(); when(mockProcessManager.start( - argThat(contains('openssl')), + argThat(contains('openssl')), environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), )).thenAnswer((Invocation invocation) => Future<Process>.value(mockOpenSslProcess)); @@ -511,15 +553,23 @@ }); 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, '', '')); + when(mockProcessManager.run( + <String>['which', 'security'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + <String>['which', 'openssl'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + argThat(contains('find-identity')), + environment: anyNamed('environment'), + workingDirectory: anyNamed('workingDirectory'), + )).thenAnswer((_) => Future<ProcessResult>.value( + ProcessResult(0, 1, '', '') + )); final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app); expect(signingConfigs, isNull); @@ -531,15 +581,21 @@ }); 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( + when(mockProcessManager.run( + <String>['which', 'security'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + <String>['which', 'openssl'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((_) => Future<ProcessResult>.value(exitsHappy)); + when(mockProcessManager.run( + argThat(contains('find-identity')), + environment: anyNamed('environment'), + workingDirectory: anyNamed('workingDirectory'), + )).thenAnswer((_) => Future<ProcessResult>.value(ProcessResult( 1, // pid 0, // exitCode ''' @@ -548,14 +604,16 @@ 3) 5bf1fd927dfb8679496a2e6cf00cbe50c1c87145 "iPhone Developer: Profile 3 (3333CCCC33)" 3 valid identities found''', '', - )); + ))); mockTerminalStdInStream = Stream<String>.fromFuture(Future<String>.value('3')); - when(mockProcessManager.runSync( + when(mockProcessManager.run( <String>['security', 'find-certificate', '-c', '3333CCCC33', '-p'], environment: anyNamed('environment'), workingDirectory: anyNamed('workingDirectory'), - )).thenReturn(ProcessResult(1, 1, '', '' )); + )).thenAnswer((_) => Future<ProcessResult>.value( + ProcessResult(1, 1, '', '' )) + ); final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app); expect(signingConfigs, isNull);
diff --git a/packages/flutter_tools/test/general.shard/ios/devices_test.dart b/packages/flutter_tools/test/general.shard/ios/devices_test.dart index f2b38c0..a05f1d0 100644 --- a/packages/flutter_tools/test/general.shard/ios/devices_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/devices_test.dart
@@ -142,10 +142,13 @@ final MockDirectory directory = MockDirectory(); when(mockFileSystem.directory(bundlePath)).thenReturn(directory); when(directory.existsSync()).thenReturn(true); - when(mockProcessManager.run(installArgs, environment: env)) - .thenAnswer( - (_) => Future<ProcessResult>.value(ProcessResult(1, 0, '', '')) - ); + when(mockProcessManager.run( + installArgs, + workingDirectory: anyNamed('workingDirectory'), + environment: env + )).thenAnswer( + (_) => Future<ProcessResult>.value(ProcessResult(1, 0, '', '')) + ); when(mockIMobileDevice.getInfoForDevice(any, 'CPUArchitecture')) .thenAnswer((_) => Future<String>.value('arm64')); @@ -259,6 +262,21 @@ return Future<ProcessResult>.value(ProcessResult(0, 0, '', '')); }); + when(mockProcessManager.run( + argThat(contains('find-identity')), + environment: anyNamed('environment'), + workingDirectory: anyNamed('workingDirectory'), + )).thenAnswer((_) => Future<ProcessResult>.value(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''', + '', + ))); + // Deploy works. when(mockIosDeploy.runApp( deviceId: anyNamed('deviceId'),
diff --git a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart index cf74b33..6870c50 100644 --- a/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/xcodeproj_test.dart
@@ -146,7 +146,10 @@ }); testUsingOsxContext('build settings is empty when xcodebuild failed to get the build settings', () { - when(mockProcessManager.runSync(argThat(contains(xcodebuild)))) + when(mockProcessManager.runSync( + argThat(contains(xcodebuild)), + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'))) .thenReturn(ProcessResult(0, 1, '', '')); expect(xcodeProjectInterpreter.getBuildSettings('', ''), const <String, String>{}); });
diff --git a/packages/flutter_tools/test/general.shard/windows/visual_studio_test.dart b/packages/flutter_tools/test/general.shard/windows/visual_studio_test.dart index 26a591b..d6da8bf 100644 --- a/packages/flutter_tools/test/general.shard/windows/visual_studio_test.dart +++ b/packages/flutter_tools/test/general.shard/windows/visual_studio_test.dart
@@ -69,18 +69,23 @@ final String finalResponse = json.encode(<Map<String, dynamic>>[response]); when<String>(result.stdout).thenReturn(finalResponse); + when<String>(result.stderr).thenReturn(''); final List<String> requirementArguments = requiredComponents == null ? <String>[] : <String>['-requires', ...requiredComponents]; - when(mockProcessManager.runSync(<String>[ - vswherePath, - '-format', - 'json', - '-utf8', - '-latest', - ...?additionalArguments, - ...?requirementArguments, - ])).thenAnswer((Invocation invocation) { + when(mockProcessManager.runSync( + <String>[ + vswherePath, + '-format', + 'json', + '-utf8', + '-latest', + ...?additionalArguments, + ...?requirementArguments, + ], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((Invocation invocation) { return result; }); } @@ -111,8 +116,11 @@ }); testUsingContext('isInstalled returns false when vswhere is missing', () { - when(mockProcessManager.runSync(any)) - .thenThrow(const ProcessException('vswhere', <String>[])); + when(mockProcessManager.runSync( + any, + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenThrow(const ProcessException('vswhere', <String>[])); visualStudio = VisualStudio(); expect(visualStudio.isInstalled, false); @@ -123,8 +131,11 @@ }); testUsingContext('vcvarsPath returns null when vswhere is missing', () { - when(mockProcessManager.runSync(any)) - .thenThrow(const ProcessException('vswhere', <String>[])); + when(mockProcessManager.runSync( + any, + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenThrow(const ProcessException('vswhere', <String>[])); visualStudio = VisualStudio(); expect(visualStudio.vcvarsPath, isNull); @@ -135,13 +146,24 @@ }); testUsingContext('isInstalled returns false when vswhere returns non-zero', () { - when(mockProcessManager.runSync(any)) - .thenThrow(const ProcessException('vswhere', <String>[])); + + when(mockProcessManager.runSync( + any, + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenThrow(const ProcessException('vswhere', <String>[])); + final MockProcessResult result = MockProcessResult(); when(result.exitCode).thenReturn(1); - when(mockProcessManager.runSync(any)).thenAnswer((Invocation invocation) { + when(mockProcessManager.runSync( + any, + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((Invocation invocation) { return result; }); + when<String>(result.stdout).thenReturn(''); + when<String>(result.stderr).thenReturn(''); visualStudio = VisualStudio(); expect(visualStudio.isInstalled, false);
diff --git a/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart b/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart index c1d650f..755d08f 100644 --- a/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart +++ b/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart
@@ -26,18 +26,23 @@ when(notWindows.isWindows).thenReturn(false); when(notWindows.environment).thenReturn(const <String, String>{}); - when(mockProcessManager.runSync(<String>[ - 'powershell', '-script="Get-CimInstance Win32_Process"' - ])).thenAnswer((Invocation invocation) { + when(mockProcessManager.runSync( + <String>['powershell', '-script="Get-CimInstance Win32_Process"'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenAnswer((Invocation invocation) { // The flutter tool process is returned as output to the powershell script final MockProcessResult result = MockProcessResult(); when(result.exitCode).thenReturn(0); when<String>(result.stdout).thenReturn('$pid $flutterToolBinary'); + when<String>(result.stderr).thenReturn(''); return result; }); - when(mockProcessManager.run(<String>[ - 'Taskkill', '/PID', '$pid', '/F' - ])).thenThrow(Exception('Flutter tool process has been killed')); + when(mockProcessManager.run( + <String>['Taskkill', '/PID', '$pid', '/F'], + workingDirectory: anyNamed('workingDirectory'), + environment: anyNamed('environment'), + )).thenThrow(Exception('Flutter tool process has been killed')); testUsingContext('defaults', () async { final PrebuiltWindowsApp windowsApp = PrebuiltWindowsApp(executable: 'foo');