diff --git a/CODEOWNERS b/CODEOWNERS index c58640cf95d..6a8439a4fb9 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -29,7 +29,6 @@ packages/pigeon/** @tarrinneal packages/platform/** @stuartmorgan-g packages/plugin_platform_interface/** @stuartmorgan-g packages/pointer_interceptor/** @ditman -packages/process/** @stuartmorgan-g packages/quick_actions/** @bparrishMines packages/rfw/** @Hixie packages/shared_preferences/** @tarrinneal diff --git a/README.md b/README.md index 4b2831bfcc8..0ea43b66729 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,6 @@ These are the packages hosted in this repository: | [pigeon](./packages/pigeon/) | [![pub package](https://img.shields.io/pub/v/pigeon.svg)](https://pub.dev/packages/pigeon) | [![pub points](https://img.shields.io/pub/points/pigeon)](https://pub.dev/packages/pigeon/score) | [![popularity](https://img.shields.io/pub/popularity/pigeon)](https://pub.dev/packages/pigeon/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20pigeon?label=)](https://github.com/flutter/flutter/labels/p%3A%20pigeon) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20pigeon?label=)](https://github.com/flutter/packages/labels/p%3A%20pigeon) | | [pointer\_interceptor](./packages/pointer_interceptor/) | [![pub package](https://img.shields.io/pub/v/pointer_interceptor.svg)](https://pub.dev/packages/pointer_interceptor) | [![pub points](https://img.shields.io/pub/points/pointer_interceptor)](https://pub.dev/packages/pointer_interceptor/score) | [![popularity](https://img.shields.io/pub/popularity/pointer_interceptor)](https://pub.dev/packages/pointer_interceptor/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20pointer_interceptor?label=)](https://github.com/flutter/flutter/labels/p%3A%20pointer_interceptor) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20pointer_interceptor?label=)](https://github.com/flutter/packages/labels/p%3A%20pointer_interceptor) | | [plugin\_platform\_interface](./packages/plugin_platform_interface/) | [![pub package](https://img.shields.io/pub/v/plugin_platform_interface.svg)](https://pub.dev/packages/plugin_platform_interface) | [![pub points](https://img.shields.io/pub/points/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![popularity](https://img.shields.io/pub/popularity/plugin_platform_interface)](https://pub.dev/packages/plugin_platform_interface/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20plugin_platform_interface?label=)](https://github.com/flutter/flutter/labels/p%3A%20plugin_platform_interface) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20plugin_platform_interface?label=)](https://github.com/flutter/packages/labels/p%3A%20plugin_platform_interface) | -| [process](./packages/process/) | [![pub package](https://img.shields.io/pub/v/process.svg)](https://pub.dev/packages/process) | [![pub points](https://img.shields.io/pub/points/process)](https://pub.dev/packages/process/score) | [![popularity](https://img.shields.io/pub/popularity/process)](https://pub.dev/packages/process/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20process?label=)](https://github.com/flutter/flutter/labels/p%3A%20process) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20process?label=)](https://github.com/flutter/packages/labels/p%3A%20process) | | [quick\_actions](./packages/quick_actions/) | [![pub package](https://img.shields.io/pub/v/quick_actions.svg)](https://pub.dev/packages/quick_actions) | [![pub points](https://img.shields.io/pub/points/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![popularity](https://img.shields.io/pub/popularity/quick_actions)](https://pub.dev/packages/quick_actions/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20quick_actions?label=)](https://github.com/flutter/flutter/labels/p%3A%20quick_actions) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20quick_actions?label=)](https://github.com/flutter/packages/labels/p%3A%20quick_actions) | | [google\_identity\_services\_web](./packages/google_identity_services_web/) | [![pub package](https://img.shields.io/pub/v/google_identity_services_web.svg)](https://pub.dev/packages/google_identity_services_web) | [![pub points](https://img.shields.io/pub/points/google_identity_services_web)](https://pub.dev/packages/google_identity_services_web/score) | [![popularity](https://img.shields.io/pub/popularity/google_identity_services_web)](https://pub.dev/packages/google_identity_services_web/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20google_identity_services_web?label=)](https://github.com/flutter/flutter/labels/p%3A%20google_identity_services_web) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20google_identity_services_web?label=)](https://github.com/flutter/packages/labels/p%3A%20google_identity_services_web) | | [rfw](./packages/rfw/) | [![pub package](https://img.shields.io/pub/v/rfw.svg)](https://pub.dev/packages/rfw) | [![pub points](https://img.shields.io/pub/points/rfw)](https://pub.dev/packages/rfw/score) | [![popularity](https://img.shields.io/pub/popularity/rfw)](https://pub.dev/packages/rfw/score) | [![GitHub issues by-label](https://img.shields.io/github/issues/flutter/flutter/p%3A%20rfw?label=)](https://github.com/flutter/flutter/labels/p%3A%20rfw) | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr/flutter/packages/p%3A%20rfw?label=)](https://github.com/flutter/packages/labels/p%3A%20rfw) | diff --git a/packages/process/.gitignore b/packages/process/.gitignore deleted file mode 100644 index fda175088d2..00000000000 --- a/packages/process/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -### Dart template -# Don’t commit the following directories created by pub. -.buildlog -.dart_tool -.pub/ -build/ -packages -.packages - -# Include when developing application packages. -pubspec.lock - -# IDE -.project -.settings -.idea -.c9 diff --git a/packages/process/AUTHORS b/packages/process/AUTHORS deleted file mode 100644 index ad59f118417..00000000000 --- a/packages/process/AUTHORS +++ /dev/null @@ -1,6 +0,0 @@ -# Below is a list of people and organizations that have contributed -# to the Process project. Names should be added to the list like so: -# -# Name/Organization - -Google Inc. diff --git a/packages/process/CHANGELOG.md b/packages/process/CHANGELOG.md deleted file mode 100644 index e6971d759f7..00000000000 --- a/packages/process/CHANGELOG.md +++ /dev/null @@ -1,208 +0,0 @@ -## NEXT - -* Updates minimum supported SDK version to Flutter 3.24/Dart 3.5. - -## 5.0.3 - -* Adds `missing_code_block_language_in_doc_comment` lint. -* Updates minimum supported SDK version to Flutter 3.19/Dart 3.3. - -## 5.0.2 - -* Removes mention of the removed record/replay feature from README. -* Updates minimum supported SDK version to Flutter 3.10/Dart 3.0. -* Fixes new lint warnings. - -## 5.0.1 - -* Transfers the package source from https://github.com/google/process.dart to - https://github.com/flutter/packages. - -## 5.0.0 - -* Remove the `covariant` keyword from `stderrEncoding` and `stdoutEncoding` - parameters. -* Update dependencies to work on Dart 3. -* Bumped min SDK dependency to nearest non-prerelease version (2.14.0) - -## 4.2.4 - -* Mark `stderrEncoding` and `stdoutEncoding` parameters as nullable again, - now that the upstream SDK issue has been fixed. - -## 4.2.3 - -* Rollback to version 4.2.1 (https://github.com/google/process.dart/issues/64) - -## 4.2.2 - -* Mark `stderrEncoding` and `stdoutEncoding` parameters as nullable. - -## 4.2.1 - -* Added custom exception types `ProcessPackageException` and - `ProcessPackageExecutableNotFoundException` to provide extra - information from exception conditions. - -## 4.2.0 - -* Fix the signature of `ProcessManager.canRun` to be consistent with - `LocalProcessManager`. - -## 4.1.1 - -* Fixed `getExecutablePath()` to only return path items that are - executable and readable to the user. - -## 4.1.0 - -* Fix the signatures of `ProcessManager.run`, `.runSync`, and `.start` to be - consistent with `LocalProcessManager`'s. -* Added more details to the `ArgumentError` thrown when a command cannot be resolved - to an executable. - -## 4.0.0 - -* First stable null safe release. - -## 4.0.0-nullsafety.4 - -* Update supported SDK range. - -## 4.0.0-nullsafety.3 - -* Update supported SDK range. - -## 4.0.0-nullsafety.2 - -* Update supported SDK range. - -## 4.0.0-nullsafety.1 - -* Migrate to null-safety. -* Remove record/replay functionality. -* Remove implicit casts in preparation for null-safety. -* Remove dependency on `package:intl` and `package:meta`. - -## 3.0.13 - -* Handle `currentDirectory` throwing an exception in `getExecutablePath()`. - -## 3.0.12 - -* Updated version constraint on intl. - -## 3.0.11 - -* Fix bug: don't add quotes if the file name already has quotes. - -## 3.0.10 - -* Added quoted strings to indicate where the command name ends and the arguments -begin otherwise, the file name is ambiguous on Windows. - -## 3.0.9 - -* Fixed bug in `ProcessWrapper` - -## 3.0.8 - -* Fixed bug in `ProcessWrapper` - -## 3.0.7 - -* Renamed `Process` to `ProcessWrapper` - -## 3.0.6 - -* Added class `Process`, a simple wrapper around dart:io's `Process` class. - -## 3.0.5 - -* Fixes for missing_return analysis errors with 2.10.0-dev.1.0. - -## 3.0.4 - -* Fix unit tests -* Update SDK constraint to 3. - -## 3.0.3 - -* Update dependency on `package:file` - -## 3.0.2 - -* Remove upper case constants. -* Update SDK constraint to 2.0.0-dev.54.0. -* Fix tests for Dart 2. - -## 3.0.1 - -* General cleanup - -## 3.0.0 - -* Cleanup getExecutablePath() to better respect the platform - -## 2.0.9 - -* Bumped `package:file` dependency - -### 2.0.8 - -* Fixed method getArguments to qualify the map method with the specific - String type - -### 2.0.7 - -* Remove `set exitCode` instances - -### 2.0.6 - -* Fix SDK constraint. -* rename .analysis_options file to analaysis_options.yaml. -* Use covariant in place of @checked. -* Update comment style generics. - -### 2.0.5 - -* Bumped maximum Dart SDK version to 2.0.0-dev.infinity - -### 2.0.4 - -* relax dependency requirement for `intl` - -### 2.0.3 - -* relax dependency requirement for `platform` - -## 2.0.2 - -* Fix a strong mode function expression return type inference bug with Dart - 1.23.0-dev.10.0. - -## 2.0.1 - -* Fixed bug in `ReplayProcessManager` whereby it could try to write to `stdout` - or `stderr` after the streams were closed. - -## 2.0.0 - -* Bumped `package:file` dependency to 2.0.1 - -## 1.1.0 - -* Added support to transparently find the right executable under Windows. - -## 1.0.1 - -* The `executable` and `arguments` parameters have been merged into one - `command` parameter in the `run`, `runSync`, and `start` methods of - `ProcessManager`. -* Added support for sanitization of command elements in - `RecordingProcessManager` and `ReplayProcessManager` via the `CommandElement` - class. - -## 1.0.0 - -* Initial version diff --git a/packages/process/LICENSE b/packages/process/LICENSE deleted file mode 100644 index c6823b81eb8..00000000000 --- a/packages/process/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright 2013 The Flutter Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google Inc. nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/process/README.md b/packages/process/README.md deleted file mode 100644 index 255cc17f22a..00000000000 --- a/packages/process/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Process - -A generic process invocation abstraction for Dart. - -Like `dart:io`, `package:process` supplies a rich, Dart-idiomatic API for -spawning OS processes. - -Unlike `dart:io`, `package:process` requires processes to be started with -[ProcessManager], which allows for easy mocking and testing of code that -spawns processes in a hermetic way. - -[ProcessManager]: https://pub.dev/documentation/process/latest/process/ProcessManager-class.html diff --git a/packages/process/dart_test.yaml b/packages/process/dart_test.yaml deleted file mode 100644 index 91ec220b8e2..00000000000 --- a/packages/process/dart_test.yaml +++ /dev/null @@ -1 +0,0 @@ -test_on: vm diff --git a/packages/process/lib/process.dart b/packages/process/lib/process.dart deleted file mode 100644 index 7ebedc7a812..00000000000 --- a/packages/process/lib/process.dart +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -export 'src/interface/exceptions.dart'; -export 'src/interface/local_process_manager.dart'; -export 'src/interface/process_manager.dart'; -export 'src/interface/process_wrapper.dart'; diff --git a/packages/process/lib/src/interface/common.dart b/packages/process/lib/src/interface/common.dart deleted file mode 100644 index 4771c21778f..00000000000 --- a/packages/process/lib/src/interface/common.dart +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:file/file.dart'; -import 'package:file/local.dart'; -import 'package:path/path.dart' show Context; -import 'package:platform/platform.dart'; - -import 'exceptions.dart'; - -const Map _osToPathStyle = { - 'linux': 'posix', - 'macos': 'posix', - 'android': 'posix', - 'ios': 'posix', - 'fuchsia': 'posix', - 'windows': 'windows', -}; - -/// Sanitizes the executable path on Windows. -/// https://github.com/dart-lang/sdk/issues/37751 -String sanitizeExecutablePath(String executable, - {Platform platform = const LocalPlatform()}) { - if (executable.isEmpty) { - return executable; - } - if (!platform.isWindows) { - return executable; - } - if (executable.contains(' ') && !executable.contains('"')) { - // Use quoted strings to indicate where the file name ends and the arguments begin; - // otherwise, the file name is ambiguous. - return '"$executable"'; - } - return executable; -} - -/// Searches the `PATH` for the executable that [executable] is supposed to launch. -/// -/// This first builds a list of candidate paths where the executable may reside. -/// If [executable] is already an absolute path, then the `PATH` environment -/// variable will not be consulted, and the specified absolute path will be the -/// only candidate that is considered. -/// -/// Once the list of candidate paths has been constructed, this will pick the -/// first such path that represents an existent file. -/// -/// Return `null` if there were no viable candidates, meaning the executable -/// could not be found. -/// -/// If [platform] is not specified, it will default to the current platform. -String? getExecutablePath( - String executable, - String? workingDirectory, { - Platform platform = const LocalPlatform(), - FileSystem fs = const LocalFileSystem(), - bool throwOnFailure = false, -}) { - assert(_osToPathStyle[platform.operatingSystem] == fs.path.style.name); - try { - workingDirectory ??= fs.currentDirectory.path; - } on FileSystemException { - // The `currentDirectory` getter can throw a FileSystemException for example - // when the process doesn't have read/list permissions in each component of - // the cwd path. In this case, fall back on '.'. - workingDirectory ??= '.'; - } - final Context context = - Context(style: fs.path.style, current: workingDirectory); - - // TODO(goderbauer): refactor when github.com/google/platform.dart/issues/2 - // is available. - final String pathSeparator = platform.isWindows ? ';' : ':'; - - List extensions = []; - if (platform.isWindows && context.extension(executable).isEmpty) { - extensions = platform.environment['PATHEXT']!.split(pathSeparator); - } - - List candidates = []; - List searchPath; - if (executable.contains(context.separator)) { - // Deal with commands that specify a relative or absolute path differently. - searchPath = [workingDirectory]; - } else { - searchPath = platform.environment['PATH']!.split(pathSeparator); - } - candidates = _getCandidatePaths(executable, searchPath, extensions, context); - final List foundCandidates = []; - for (final String path in candidates) { - final File candidate = fs.file(path); - final FileStat stat = candidate.statSync(); - // Only return files or links that exist. - if (stat.type == FileSystemEntityType.notFound || - stat.type == FileSystemEntityType.directory) { - continue; - } - - // File exists, but we don't know if it's readable/executable yet. - foundCandidates.add(candidate.path); - - const int isExecutable = 0x40; - const int isReadable = 0x100; - const int isExecutableAndReadable = isExecutable | isReadable; - // Should only return files or links that are readable and executable by the - // user. - - // On Windows it's not actually possible to only return files that are - // readable, since Dart reports files that have had read permission removed - // as being readable, but not checking for it is the same as checking for it - // and finding it readable, so we use the same check here on all platforms, - // so that if Dart ever gets fixed, it'll just work. - if (stat.mode & isExecutableAndReadable == isExecutableAndReadable) { - return path; - } - } - if (throwOnFailure) { - if (foundCandidates.isNotEmpty) { - throw ProcessPackageExecutableNotFoundException( - executable, - message: - 'Found candidates, but lacked sufficient permissions to execute "$executable".', - workingDirectory: workingDirectory, - candidates: foundCandidates, - searchPath: searchPath, - ); - } else { - throw ProcessPackageExecutableNotFoundException( - executable, - message: 'Failed to find "$executable" in the search path.', - workingDirectory: workingDirectory, - searchPath: searchPath, - ); - } - } - return null; -} - -/// Returns all possible combinations of `$searchPath\$command.$ext` for -/// `searchPath` in [searchPaths] and `ext` in [extensions]. -/// -/// If [extensions] is empty, it will just enumerate all -/// `$searchPath\$command`. -/// If [command] is an absolute path, it will just enumerate -/// `$command.$ext`. -List _getCandidatePaths( - String command, - List searchPaths, - List extensions, - Context context, -) { - final List withExtensions = extensions.isNotEmpty - ? extensions.map((String ext) => '$command$ext').toList() - : [command]; - if (context.isAbsolute(command)) { - return withExtensions; - } - return searchPaths - .map((String path) => - withExtensions.map((String command) => context.join(path, command))) - .expand((Iterable e) => e) - .toList() - .cast(); -} diff --git a/packages/process/lib/src/interface/exceptions.dart b/packages/process/lib/src/interface/exceptions.dart deleted file mode 100644 index 51bc6a986ea..00000000000 --- a/packages/process/lib/src/interface/exceptions.dart +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io' show ProcessException; - -/// A specialized exception class for this package, so that it can throw -/// customized exceptions with more information. -class ProcessPackageException extends ProcessException { - /// Create a const ProcessPackageException. - /// - /// The [executable] should be the name of the executable to be run. - /// - /// The optional [workingDirectory] is the directory where the command - /// execution is attempted. - /// - /// The optional [arguments] is a list of the arguments to given to the - /// executable, already separated. - /// - /// The optional [message] is an additional message to be included in the - /// exception string when printed. - /// - /// The optional [errorCode] is the error code received when the executable - /// was run. Zero means it ran successfully, or that no error code was - /// available. - /// - /// See [ProcessException] for more information. - const ProcessPackageException( - String executable, { - List arguments = const [], - String message = '', - int errorCode = 0, - this.workingDirectory, - }) : super(executable, arguments, message, errorCode); - - /// Creates a [ProcessPackageException] from a [ProcessException]. - factory ProcessPackageException.fromProcessException( - ProcessException exception, { - String? workingDirectory, - }) { - return ProcessPackageException( - exception.executable, - arguments: exception.arguments, - message: exception.message, - errorCode: exception.errorCode, - workingDirectory: workingDirectory, - ); - } - - /// The optional working directory that the command was being executed in. - final String? workingDirectory; - - // Don't implement a toString() for this exception, since code may be - // depending upon the format of ProcessException.toString(). -} - -/// An exception for when an executable is not found that was expected to be found. -class ProcessPackageExecutableNotFoundException - extends ProcessPackageException { - /// Creates a const ProcessPackageExecutableNotFoundException - /// - /// The optional [candidates] are the files matching the expected executable - /// on the [searchPath]. - /// - /// The optional [searchPath] is the list of directories searched for the - /// expected executable. - /// - /// See [ProcessPackageException] for more information. - const ProcessPackageExecutableNotFoundException( - super.executable, { - super.arguments, - super.message, - super.errorCode, - super.workingDirectory, - this.candidates = const [], - this.searchPath = const [], - }); - - /// The list of non-viable executable candidates found. - final List candidates; - - /// The search path used to find candidates. - final List searchPath; - - @override - String toString() { - final StringBuffer buffer = - StringBuffer('ProcessPackageExecutableNotFoundException: $message\n'); - // Don't add an extra space if there are no arguments. - final String args = arguments.isNotEmpty ? ' ${arguments.join(' ')}' : ''; - buffer.writeln(' Command: $executable$args'); - if (workingDirectory != null && workingDirectory!.isNotEmpty) { - buffer.writeln(' Working Directory: $workingDirectory'); - } - if (candidates.isNotEmpty) { - buffer.writeln(' Candidates:\n ${candidates.join('\n ')}'); - } - buffer.writeln(' Search Path:\n ${searchPath.join('\n ')}'); - return buffer.toString(); - } -} diff --git a/packages/process/lib/src/interface/local_process_manager.dart b/packages/process/lib/src/interface/local_process_manager.dart deleted file mode 100644 index fd0d1c2ced9..00000000000 --- a/packages/process/lib/src/interface/local_process_manager.dart +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:convert'; -import 'dart:io' - show - Process, - ProcessException, - ProcessResult, - ProcessSignal, - ProcessStartMode, - systemEncoding; - -import 'common.dart'; -import 'exceptions.dart'; -import 'process_manager.dart'; - -/// Local implementation of the `ProcessManager` interface. -/// -/// This implementation delegates directly to the corresponding static methods -/// in `dart:io`. -/// -/// All methods that take a `command` will run `toString()` on the command -/// elements to derive the executable and arguments that should be passed to -/// the underlying `dart:io` methods. Thus, the degenerate case of -/// `List` will trivially work as expected. -class LocalProcessManager implements ProcessManager { - /// Creates a new `LocalProcessManager`. - const LocalProcessManager(); - - @override - Future start( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - ProcessStartMode mode = ProcessStartMode.normal, - }) { - try { - return Process.start( - sanitizeExecutablePath(_getExecutable( - command, - workingDirectory, - runInShell, - )), - _getArguments(command), - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment, - runInShell: runInShell, - mode: mode, - ); - } on ProcessException catch (exception) { - throw ProcessPackageException.fromProcessException(exception, - workingDirectory: workingDirectory); - } - } - - @override - Future run( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - Encoding? stdoutEncoding = systemEncoding, - Encoding? stderrEncoding = systemEncoding, - }) { - try { - return Process.run( - sanitizeExecutablePath(_getExecutable( - command, - workingDirectory, - runInShell, - )), - _getArguments(command), - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment, - runInShell: runInShell, - stdoutEncoding: stdoutEncoding, - stderrEncoding: stderrEncoding, - ); - } on ProcessException catch (exception) { - throw ProcessPackageException.fromProcessException(exception, - workingDirectory: workingDirectory); - } - } - - @override - ProcessResult runSync( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - Encoding? stdoutEncoding = systemEncoding, - Encoding? stderrEncoding = systemEncoding, - }) { - try { - return Process.runSync( - sanitizeExecutablePath(_getExecutable( - command, - workingDirectory, - runInShell, - )), - _getArguments(command), - workingDirectory: workingDirectory, - environment: environment, - includeParentEnvironment: includeParentEnvironment, - runInShell: runInShell, - stdoutEncoding: stdoutEncoding, - stderrEncoding: stderrEncoding, - ); - } on ProcessException catch (exception) { - throw ProcessPackageException.fromProcessException(exception, - workingDirectory: workingDirectory); - } - } - - @override - bool canRun(covariant String executable, {String? workingDirectory}) => - getExecutablePath(executable, workingDirectory) != null; - - @override - bool killPid(int pid, [ProcessSignal signal = ProcessSignal.sigterm]) { - return Process.killPid(pid, signal); - } -} - -String _getExecutable( - List command, String? workingDirectory, bool runInShell) { - final String commandName = command.first.toString(); - if (runInShell) { - return commandName; - } - return getExecutablePath( - commandName, - workingDirectory, - throwOnFailure: true, - )!; -} - -List _getArguments(List command) => - // Adding a specific type to map in order to workaround dart issue - // https://github.com/dart-lang/sdk/issues/32414 - command - .skip(1) - .map((dynamic element) => element.toString()) - .toList(); diff --git a/packages/process/lib/src/interface/process_manager.dart b/packages/process/lib/src/interface/process_manager.dart deleted file mode 100644 index d506cdc20f0..00000000000 --- a/packages/process/lib/src/interface/process_manager.dart +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:convert'; -import 'dart:io' - show - Process, - ProcessResult, - ProcessSignal, - ProcessStartMode, - systemEncoding; - -/// Manages the creation of abstract processes. -/// -/// Using instances of this class provides level of indirection from the static -/// methods in the [Process] class, which in turn allows the underlying -/// implementation to be mocked out or decorated for testing and debugging -/// purposes. -abstract class ProcessManager { - /// Starts a process by running the specified [command]. - /// - /// The first element in [command] will be treated as the executable to run, - /// with subsequent elements being passed as arguments to the executable. It - /// is left to implementations to decide what element types they support in - /// the [command] list. - /// - /// Returns a `Future` that completes with a Process instance when - /// the process has been successfully started. That [Process] object can be - /// used to interact with the process. If the process cannot be started, the - /// returned [Future] completes with an exception. - /// - /// Use [workingDirectory] to set the working directory for the process. Note - /// that the change of directory occurs before executing the process on some - /// platforms, which may have impact when using relative paths for the - /// executable and the arguments. - /// - /// Use [environment] to set the environment variables for the process. If not - /// set, the environment of the parent process is inherited. Currently, only - /// US-ASCII environment variables are supported and errors are likely to occur - /// if an environment variable with code-points outside the US-ASCII range is - /// passed in. - /// - /// If [includeParentEnvironment] is `true`, the process's environment will - /// include the parent process's environment, with [environment] taking - /// precedence. Default is `true`. - /// - /// If [runInShell] is `true`, the process will be spawned through a system - /// shell. On Linux and OS X, `/bin/sh` is used, while - /// `%WINDIR%\system32\cmd.exe` is used on Windows. - /// - /// Users must read all data coming on the `stdout` and `stderr` - /// streams of processes started with [start]. If the user - /// does not read all data on the streams the underlying system - /// resources will not be released since there is still pending data. - /// - /// The following code uses `start` to grep for `main` in the - /// file `test.dart` on Linux. - /// - /// ```dart - /// ProcessManager mgr = new LocalProcessManager(); - /// mgr.start(['grep', '-i', 'main', 'test.dart']).then((process) { - /// stdout.addStream(process.stdout); - /// stderr.addStream(process.stderr); - /// }); - /// ``` - /// - /// If [mode] is [ProcessStartMode.normal] (the default) a child - /// process will be started with `stdin`, `stdout` and `stderr` - /// connected. - /// - /// If `mode` is [ProcessStartMode.detached] a detached process will - /// be created. A detached process has no connection to its parent, - /// and can keep running on its own when the parent dies. The only - /// information available from a detached process is its `pid`. There - /// is no connection to its `stdin`, `stdout` or `stderr`, nor will - /// the process' exit code become available when it terminates. - /// - /// If `mode` is [ProcessStartMode.detachedWithStdio] a detached - /// process will be created where the `stdin`, `stdout` and `stderr` - /// are connected. The creator can communicate with the child through - /// these. The detached process will keep running even if these - /// communication channels are closed. The process' exit code will - /// not become available when it terminated. - /// - /// The default value for `mode` is `ProcessStartMode.normal`. - Future start( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - ProcessStartMode mode = ProcessStartMode.normal, - }); - - /// Starts a process and runs it non-interactively to completion. - /// - /// The first element in [command] will be treated as the executable to run, - /// with subsequent elements being passed as arguments to the executable. It - /// is left to implementations to decide what element types they support in - /// the [command] list. - /// - /// Use [workingDirectory] to set the working directory for the process. Note - /// that the change of directory occurs before executing the process on some - /// platforms, which may have impact when using relative paths for the - /// executable and the arguments. - /// - /// Use [environment] to set the environment variables for the process. If not - /// set the environment of the parent process is inherited. Currently, only - /// US-ASCII environment variables are supported and errors are likely to occur - /// if an environment variable with code-points outside the US-ASCII range is - /// passed in. - /// - /// If [includeParentEnvironment] is `true`, the process's environment will - /// include the parent process's environment, with [environment] taking - /// precedence. Default is `true`. - /// - /// If [runInShell] is true, the process will be spawned through a system - /// shell. On Linux and OS X, `/bin/sh` is used, while - /// `%WINDIR%\system32\cmd.exe` is used on Windows. - /// - /// The encoding used for decoding `stdout` and `stderr` into text is - /// controlled through [stdoutEncoding] and [stderrEncoding]. The - /// default encoding is [systemEncoding]. If `null` is used no - /// decoding will happen and the [ProcessResult] will hold binary - /// data. - /// - /// Returns a `Future` that completes with the - /// result of running the process, i.e., exit code, standard out and - /// standard in. - /// - /// The following code uses `run` to grep for `main` in the - /// file `test.dart` on Linux. - /// - /// ```dart - /// ProcessManager mgr = new LocalProcessManager(); - /// mgr.run('grep', ['-i', 'main', 'test.dart']).then((result) { - /// stdout.write(result.stdout); - /// stderr.write(result.stderr); - /// }); - /// ``` - Future run( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - Encoding? stdoutEncoding = systemEncoding, - Encoding? stderrEncoding = systemEncoding, - }); - - /// Starts a process and runs it to completion. This is a synchronous - /// call and will block until the child process terminates. - /// - /// The arguments are the same as for [run]`. - /// - /// Returns a `ProcessResult` with the result of running the process, - /// i.e., exit code, standard out and standard in. - ProcessResult runSync( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - Encoding? stdoutEncoding = systemEncoding, - Encoding? stderrEncoding = systemEncoding, - }); - - /// Returns `true` if the [executable] exists and if it can be executed. - bool canRun(dynamic executable, {String? workingDirectory}); - - /// Kills the process with id [pid]. - /// - /// Where possible, sends the [signal] to the process with id - /// `pid`. This includes Linux and OS X. The default signal is - /// [ProcessSignal.sigterm] which will normally terminate the - /// process. - /// - /// On platforms without signal support, including Windows, the call - /// just terminates the process with id `pid` in a platform specific - /// way, and the `signal` parameter is ignored. - /// - /// Returns `true` if the signal is successfully delivered to the - /// process. Otherwise the signal could not be sent, usually meaning - /// that the process is already dead. - bool killPid(int pid, [ProcessSignal signal = ProcessSignal.sigterm]); -} diff --git a/packages/process/lib/src/interface/process_wrapper.dart b/packages/process/lib/src/interface/process_wrapper.dart deleted file mode 100644 index 3b3ab3c667f..00000000000 --- a/packages/process/lib/src/interface/process_wrapper.dart +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'dart:io' as io; - -/// A wrapper around an [io.Process] class that adds some convenience methods. -class ProcessWrapper implements io.Process { - /// Constructs a [ProcessWrapper] object that delegates to the specified - /// underlying object. - ProcessWrapper(this._delegate) - : _stdout = StreamController>(), - _stderr = StreamController>(), - _stdoutDone = Completer(), - _stderrDone = Completer() { - _monitorStdioStream(_delegate.stdout, _stdout, _stdoutDone); - _monitorStdioStream(_delegate.stderr, _stderr, _stderrDone); - } - - final io.Process _delegate; - final StreamController> _stdout; - final StreamController> _stderr; - final Completer _stdoutDone; - final Completer _stderrDone; - - /// Listens to the specified [stream], repeating events on it via - /// [controller], and completing [completer] once the stream is done. - void _monitorStdioStream( - Stream> stream, - StreamController> controller, - Completer completer, - ) { - stream.listen( - controller.add, - onError: controller.addError, - onDone: () { - controller.close(); - completer.complete(); - }, - ); - } - - @override - Future get exitCode => _delegate.exitCode; - - /// A [Future] that completes when the process has exited and its standard - /// output and error streams have closed. - /// - /// This exists as an alternative to [exitCode], which does not guarantee - /// that the stdio streams have closed (it is possible for the exit code to - /// be available before stdout and stderr have closed). - /// - /// The future returned here will complete with the exit code of the process. - Future get done async { - late int result; - await Future.wait(>[ - _stdoutDone.future, - _stderrDone.future, - _delegate.exitCode.then((int value) { - result = value; - }), - ]); - return result; - } - - @override - bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) { - return _delegate.kill(signal); - } - - @override - int get pid => _delegate.pid; - - @override - io.IOSink get stdin => _delegate.stdin; - - @override - Stream> get stdout => _stdout.stream; - - @override - Stream> get stderr => _stderr.stream; -} diff --git a/packages/process/pubspec.yaml b/packages/process/pubspec.yaml deleted file mode 100644 index ac3b51322e9..00000000000 --- a/packages/process/pubspec.yaml +++ /dev/null @@ -1,19 +0,0 @@ -name: process -description: A pluggable, mockable process invocation abstraction for Dart. -repository: https://github.com/flutter/packages/tree/main/packages/process -issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+process%22 -version: 5.0.3 - -environment: - sdk: ^3.5.0 - -dependencies: - file: '>=6.0.0 <8.0.0' - path: ^1.8.0 - platform: '^3.0.0' - -dev_dependencies: - test: ^1.16.8 - -topics: - - process diff --git a/packages/process/test/src/interface/common_test.dart b/packages/process/test/src/interface/common_test.dart deleted file mode 100644 index 743b7cd34b6..00000000000 --- a/packages/process/test/src/interface/common_test.dart +++ /dev/null @@ -1,589 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:io' as io; - -import 'package:file/file.dart'; -import 'package:file/local.dart'; -import 'package:file/memory.dart'; -import 'package:platform/platform.dart'; -import 'package:process/process.dart'; -import 'package:process/src/interface/common.dart'; -import 'package:test/test.dart'; - -void main() { - group('getExecutablePath', () { - late FileSystem fs; - late Directory workingDir, dir1, dir2, dir3; - - void initialize(FileSystemStyle style) { - setUp(() { - fs = MemoryFileSystem(style: style); - workingDir = fs.systemTempDirectory.createTempSync('work_dir_'); - dir1 = fs.systemTempDirectory.createTempSync('dir1_'); - dir2 = fs.systemTempDirectory.createTempSync('dir2_'); - dir3 = fs.systemTempDirectory.createTempSync('dir3_'); - }); - } - - tearDown(() { - for (final Directory directory in [ - workingDir, - dir1, - dir2, - dir3 - ]) { - directory.deleteSync(recursive: true); - } - }); - - group('on windows', () { - late Platform platform; - - initialize(FileSystemStyle.windows); - - setUp(() { - platform = FakePlatform( - operatingSystem: 'windows', - environment: { - 'PATH': '${dir1.path};${dir2.path}', - 'PATHEXT': '.exe;.bat' - }, - ); - }); - - test('absolute', () { - String command = fs.path.join(dir3.path, 'bla.exe'); - final String expectedPath = command; - fs.file(command).createSync(); - - String? executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - - command = fs.path.withoutExtension(command); - executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - }); - - test('in path', () { - String command = 'bla.exe'; - final String expectedPath = fs.path.join(dir2.path, command); - fs.file(expectedPath).createSync(); - - String? executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - - command = fs.path.withoutExtension(command); - executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - }); - - test('in path multiple times', () { - String command = 'bla.exe'; - final String expectedPath = fs.path.join(dir1.path, command); - final String wrongPath = fs.path.join(dir2.path, command); - fs.file(expectedPath).createSync(); - fs.file(wrongPath).createSync(); - - String? executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - - command = fs.path.withoutExtension(command); - executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - }); - - test('in subdir of work dir', () { - String command = fs.path.join('.', 'foo', 'bla.exe'); - final String expectedPath = fs.path.join(workingDir.path, command); - fs.file(expectedPath).createSync(recursive: true); - - String? executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - - command = fs.path.withoutExtension(command); - executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - }); - - test('in work dir', () { - String command = fs.path.join('.', 'bla.exe'); - final String expectedPath = fs.path.join(workingDir.path, command); - final String wrongPath = fs.path.join(dir2.path, command); - fs.file(expectedPath).createSync(); - fs.file(wrongPath).createSync(); - - String? executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - - command = fs.path.withoutExtension(command); - executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - }); - - test('with multiple extensions', () { - const String command = 'foo'; - final String expectedPath = fs.path.join(dir1.path, '$command.exe'); - final String wrongPath1 = fs.path.join(dir1.path, '$command.bat'); - final String wrongPath2 = fs.path.join(dir2.path, '$command.exe'); - fs.file(expectedPath).createSync(); - fs.file(wrongPath1).createSync(); - fs.file(wrongPath2).createSync(); - - final String? executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - }); - - test('not found', () { - const String command = 'foo.exe'; - - final String? executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - expect(executablePath, isNull); - }); - - test('not found with throwOnFailure throws exception with match state', - () { - const String command = 'foo.exe'; - expect( - () => getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - throwOnFailure: true, - ), - throwsA(isA() - .having( - (ProcessPackageExecutableNotFoundException - notFoundException) => - notFoundException.candidates, - 'candidates', - isEmpty) - .having( - (ProcessPackageExecutableNotFoundException - notFoundException) => - notFoundException.workingDirectory, - 'workingDirectory', - equals(workingDir.path)) - .having( - (ProcessPackageExecutableNotFoundException - notFoundException) => - notFoundException.toString(), - 'toString', - contains( - ' Working Directory: C:\\.tmp_rand0\\work_dir_rand0\n' - ' Search Path:\n' - ' C:\\.tmp_rand0\\dir1_rand0\n' - ' C:\\.tmp_rand0\\dir2_rand0\n')))); - }); - - test('when path has spaces', () { - expect( - sanitizeExecutablePath(r'Program Files\bla.exe', - platform: platform), - r'"Program Files\bla.exe"'); - expect( - sanitizeExecutablePath(r'ProgramFiles\bla.exe', platform: platform), - r'ProgramFiles\bla.exe'); - expect( - sanitizeExecutablePath(r'"Program Files\bla.exe"', - platform: platform), - r'"Program Files\bla.exe"'); - expect( - sanitizeExecutablePath(r'"Program Files\bla.exe"', - platform: platform), - r'"Program Files\bla.exe"'); - expect( - sanitizeExecutablePath(r'C:\"Program Files"\bla.exe', - platform: platform), - r'C:\"Program Files"\bla.exe'); - }); - - test('with absolute path when currentDirectory getter throws', () { - final FileSystem fsNoCwd = MemoryFileSystemNoCwd(fs); - final String command = fs.path.join(dir3.path, 'bla.exe'); - final String expectedPath = command; - fs.file(command).createSync(); - - final String? executablePath = getExecutablePath( - command, - null, - platform: platform, - fs: fsNoCwd, - ); - _expectSamePath(executablePath, expectedPath); - }); - - test('with relative path when currentDirectory getter throws', () { - final FileSystem fsNoCwd = MemoryFileSystemNoCwd(fs); - final String command = fs.path.join('.', 'bla.exe'); - - final String? executablePath = getExecutablePath( - command, - null, - platform: platform, - fs: fsNoCwd, - ); - expect(executablePath, isNull); - }); - }); - - group('on Linux', () { - late Platform platform; - - initialize(FileSystemStyle.posix); - - setUp(() { - platform = FakePlatform( - operatingSystem: 'linux', - environment: {'PATH': '${dir1.path}:${dir2.path}'}); - }); - - test('absolute', () { - final String command = fs.path.join(dir3.path, 'bla'); - final String expectedPath = command; - final String wrongPath = fs.path.join(dir3.path, 'bla.bat'); - fs.file(command).createSync(); - fs.file(wrongPath).createSync(); - - final String? executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - }); - - test('in path multiple times', () { - const String command = 'xxx'; - final String expectedPath = fs.path.join(dir1.path, command); - final String wrongPath = fs.path.join(dir2.path, command); - fs.file(expectedPath).createSync(); - fs.file(wrongPath).createSync(); - - final String? executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - _expectSamePath(executablePath, expectedPath); - }); - - test('not found', () { - const String command = 'foo'; - - final String? executablePath = getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - ); - expect(executablePath, isNull); - }); - - test('not found with throwOnFailure throws exception with match state', - () { - const String command = 'foo'; - expect( - () => getExecutablePath( - command, - workingDir.path, - platform: platform, - fs: fs, - throwOnFailure: true, - ), - throwsA(isA() - .having( - (ProcessPackageExecutableNotFoundException - notFoundException) => - notFoundException.candidates, - 'candidates', - isEmpty) - .having( - (ProcessPackageExecutableNotFoundException - notFoundException) => - notFoundException.workingDirectory, - 'workingDirectory', - equals(workingDir.path)) - .having( - (ProcessPackageExecutableNotFoundException - notFoundException) => - notFoundException.toString(), - 'toString', - contains(' Working Directory: /.tmp_rand0/work_dir_rand0\n' - ' Search Path:\n' - ' /.tmp_rand0/dir1_rand0\n' - ' /.tmp_rand0/dir2_rand0\n')))); - }); - - test('when path has spaces', () { - expect( - sanitizeExecutablePath('/usr/local/bin/foo bar', - platform: platform), - '/usr/local/bin/foo bar'); - }); - }); - }); - group('Real Filesystem', () { - // These tests don't use the memory filesystem because Dart can't modify file - // executable permissions, so we have to create them with actual commands. - - late Platform platform; - late Directory tmpDir; - late Directory pathDir1; - late Directory pathDir2; - late Directory pathDir3; - late Directory pathDir4; - late Directory pathDir5; - late File command1; - late File command2; - late File command3; - late File command4; - late File command5; - const Platform localPlatform = LocalPlatform(); - late FileSystem fs; - - setUp(() { - fs = const LocalFileSystem(); - tmpDir = fs.systemTempDirectory.createTempSync(); - pathDir1 = tmpDir.childDirectory('path1')..createSync(); - pathDir2 = tmpDir.childDirectory('path2')..createSync(); - pathDir3 = tmpDir.childDirectory('path3')..createSync(); - pathDir4 = tmpDir.childDirectory('path4')..createSync(); - pathDir5 = tmpDir.childDirectory('path5')..createSync(); - command1 = pathDir1.childFile('command')..createSync(); - command2 = pathDir2.childFile('command')..createSync(); - command3 = pathDir3.childFile('command')..createSync(); - command4 = pathDir4.childFile('command')..createSync(); - command5 = pathDir5.childFile('command')..createSync(); - platform = FakePlatform( - operatingSystem: localPlatform.operatingSystem, - environment: { - 'PATH': [ - pathDir1, - pathDir2, - pathDir3, - pathDir4, - pathDir5, - ].map((Directory dir) => dir.absolute.path).join(':'), - }, - ); - }); - - tearDown(() { - tmpDir.deleteSync(recursive: true); - }); - - test('Only returns executables in PATH', () { - if (localPlatform.isWindows) { - // Windows doesn't check for executable-ness, and we can't run 'chmod' - // on Windows anyhow. - return; - } - - // Make the second command in the path executable, but not the first. - // No executable permissions - io.Process.runSync('chmod', ['0644', '--', command1.path]); - // Only group executable permissions - io.Process.runSync('chmod', ['0645', '--', command2.path]); - // Only other executable permissions - io.Process.runSync('chmod', ['0654', '--', command3.path]); - // All executable permissions, but not readable - io.Process.runSync('chmod', ['0311', '--', command4.path]); - // All executable permissions - io.Process.runSync('chmod', ['0755', '--', command5.path]); - - final String? executablePath = getExecutablePath( - 'command', - tmpDir.path, - platform: platform, - fs: fs, - ); - - // Make sure that the path returned is for the last command, since that - // one comes last in the PATH, but is the only one executable by the - // user. - _expectSamePath(executablePath, command5.absolute.path); - }); - - test( - 'Test that finding non-executable paths throws with proper information', - () { - if (localPlatform.isWindows) { - // Windows doesn't check for executable-ness, and we can't run 'chmod' - // on Windows anyhow. - return; - } - - // Make the second command in the path executable, but not the first. - // No executable permissions - io.Process.runSync('chmod', ['0644', '--', command1.path]); - // Only group executable permissions - io.Process.runSync('chmod', ['0645', '--', command2.path]); - // Only other executable permissions - io.Process.runSync('chmod', ['0654', '--', command3.path]); - // All executable permissions, but not readable - io.Process.runSync('chmod', ['0311', '--', command4.path]); - - expect( - () => getExecutablePath( - 'command', - tmpDir.path, - platform: platform, - fs: fs, - throwOnFailure: true, - ), - throwsA(isA() - .having( - (ProcessPackageExecutableNotFoundException - notFoundException) => - notFoundException.candidates, - 'candidates', - equals([ - '${tmpDir.path}/path1/command', - '${tmpDir.path}/path2/command', - '${tmpDir.path}/path3/command', - '${tmpDir.path}/path4/command', - '${tmpDir.path}/path5/command', - ])) - .having( - (ProcessPackageExecutableNotFoundException - notFoundException) => - notFoundException.toString(), - 'toString', - contains( - 'ProcessPackageExecutableNotFoundException: Found candidates, but lacked sufficient permissions to execute "command".\n' - ' Command: command\n' - ' Working Directory: ${tmpDir.path}\n' - ' Candidates:\n' - ' ${tmpDir.path}/path1/command\n' - ' ${tmpDir.path}/path2/command\n' - ' ${tmpDir.path}/path3/command\n' - ' ${tmpDir.path}/path4/command\n' - ' ${tmpDir.path}/path5/command\n' - ' Search Path:\n' - ' ${tmpDir.path}/path1\n' - ' ${tmpDir.path}/path2\n' - ' ${tmpDir.path}/path3\n' - ' ${tmpDir.path}/path4\n' - ' ${tmpDir.path}/path5\n')))); - }); - - test('Test that finding no executable paths throws with proper information', - () { - if (localPlatform.isWindows) { - // Windows doesn't check for executable-ness, and we can't run 'chmod' - // on Windows anyhow. - return; - } - - expect( - () => getExecutablePath( - 'non-existent-command', - tmpDir.path, - platform: platform, - fs: fs, - throwOnFailure: true, - ), - throwsA(isA() - .having( - (ProcessPackageExecutableNotFoundException - notFoundException) => - notFoundException.candidates, - 'candidates', - isEmpty) - .having( - (ProcessPackageExecutableNotFoundException - notFoundException) => - notFoundException.toString(), - 'toString', - contains( - 'ProcessPackageExecutableNotFoundException: Failed to find "non-existent-command" in the search path.\n' - ' Command: non-existent-command\n' - ' Working Directory: ${tmpDir.path}\n' - ' Search Path:\n' - ' ${tmpDir.path}/path1\n' - ' ${tmpDir.path}/path2\n' - ' ${tmpDir.path}/path3\n' - ' ${tmpDir.path}/path4\n' - ' ${tmpDir.path}/path5\n')))); - }); - }); -} - -void _expectSamePath(String? actual, String? expected) { - expect(actual, isNotNull); - expect(actual!.toLowerCase(), expected!.toLowerCase()); -} - -class MemoryFileSystemNoCwd extends ForwardingFileSystem { - MemoryFileSystemNoCwd(super.delegate); - - @override - Directory get currentDirectory { - throw const FileSystemException('Access denied'); - } -} diff --git a/packages/process/test/src/interface/process_wrapper_test.dart b/packages/process/test/src/interface/process_wrapper_test.dart deleted file mode 100644 index 3060fd81fe9..00000000000 --- a/packages/process/test/src/interface/process_wrapper_test.dart +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:async'; -import 'dart:convert'; -import 'dart:io' as io; - -import 'package:process/process.dart'; -import 'package:test/test.dart'; - -void main() { - group('ProcessWrapper', () { - late TestProcess delegate; - late ProcessWrapper process; - - setUp(() { - delegate = TestProcess(); - process = ProcessWrapper(delegate); - }); - - group('done', () { - late bool done; - - setUp(() { - done = false; - // ignore: unawaited_futures - process.done.then((int result) { - done = true; - }); - }); - - test('completes only when all done', () async { - expect(done, isFalse); - delegate.exitCodeCompleter.complete(0); - await Future.value(); - expect(done, isFalse); - await delegate.stdoutController.close(); - await Future.value(); - expect(done, isFalse); - await delegate.stderrController.close(); - await Future.value(); - expect(done, isTrue); - expect(await process.exitCode, 0); - }); - - test('works in conjunction with subscribers to stdio streams', () async { - process.stdout - .transform(utf8.decoder) - .transform(const LineSplitter()) - // ignore: avoid_print - .listen(print); - delegate.exitCodeCompleter.complete(0); - await delegate.stdoutController.close(); - await delegate.stderrController.close(); - await Future.value(); - expect(done, isTrue); - }); - }); - - group('stdio', () { - test('streams properly close', () async { - Future testStream( - Stream> stream, - StreamController> controller, - String name, - ) async { - bool closed = false; - stream.listen( - (_) {}, - onDone: () { - closed = true; - }, - ); - await controller.close(); - await Future.value(); - expect(closed, isTrue, reason: 'for $name'); - } - - await testStream(process.stdout, delegate.stdoutController, 'stdout'); - await testStream(process.stderr, delegate.stderrController, 'stderr'); - }); - }); - }); -} - -class TestProcess implements io.Process { - TestProcess([this.pid = 123]) - : exitCodeCompleter = Completer(), - stdoutController = StreamController>(), - stderrController = StreamController>(); - - @override - final int pid; - final Completer exitCodeCompleter; - final StreamController> stdoutController; - final StreamController> stderrController; - - @override - Future get exitCode => exitCodeCompleter.future; - - @override - bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) { - exitCodeCompleter.complete(-1); - return true; - } - - @override - Stream> get stderr => stderrController.stream; - - @override - io.IOSink get stdin => throw UnsupportedError('Not supported'); - - @override - Stream> get stdout => stdoutController.stream; -} diff --git a/packages/process/test/utils.dart b/packages/process/test/utils.dart deleted file mode 100644 index 678a8b3bca7..00000000000 --- a/packages/process/test/utils.dart +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'dart:convert'; - -/// Decodes a UTF8-encoded byte array into a list of Strings, where each list -/// entry represents a line of text. -List decode(List data) => - const LineSplitter().convert(utf8.decode(data)); - -/// Consumes and returns an entire stream of bytes. -Future> consume(Stream> stream) => - stream.expand((List data) => data).toList(); diff --git a/script/configs/allowed_unpinned_deps.yaml b/script/configs/allowed_unpinned_deps.yaml index 9970a8b472c..be706a2c669 100644 --- a/script/configs/allowed_unpinned_deps.yaml +++ b/script/configs/allowed_unpinned_deps.yaml @@ -33,6 +33,8 @@ - devtools_extensions - fake_async - ffi +- file +- file_testing - gcloud - graphs - html @@ -52,6 +54,7 @@ - path - package_config - platform +- process - pub_semver - shelf - shelf_static @@ -66,8 +69,6 @@ # Google-owned packages - _discoveryapis_commons - adaptive_navigation -- file -- file_testing - googleapis - googleapis_auth - json_annotation