[xdg_directories] Remove `process` dependency (#4460)
Replaces the `process` dependency with direct use of `io.Process`, to reduce external dependencies, since this is a `path_provider` dependency and thus a core package.
Fixes https://github.com/flutter/flutter/issues/129787
diff --git a/packages/xdg_directories/CHANGELOG.md b/packages/xdg_directories/CHANGELOG.md
index 5c63413..ff8640d 100644
--- a/packages/xdg_directories/CHANGELOG.md
+++ b/packages/xdg_directories/CHANGELOG.md
@@ -1,5 +1,6 @@
-## NEXT
+## 1.0.1
+* Removes `process` dependency.
* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18.
## 1.0.0
diff --git a/packages/xdg_directories/lib/xdg_directories.dart b/packages/xdg_directories/lib/xdg_directories.dart
index bf6de2d..7e115a5 100644
--- a/packages/xdg_directories/lib/xdg_directories.dart
+++ b/packages/xdg_directories/lib/xdg_directories.dart
@@ -9,7 +9,9 @@
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path;
-import 'package:process/process.dart';
+
+// From errno definitions.
+const int _noSuchFileError = 2;
/// An override function used by the tests to override the environment variable
/// lookups using [xdgEnvironmentOverride].
@@ -36,16 +38,44 @@
EnvironmentAccessor _getenv = _productionGetEnv;
String? _productionGetEnv(String value) => Platform.environment[value];
-/// A testing function that replaces the process manager used to run xdg-user-path
-/// with the one supplied.
+/// A wrapper around Process.runSync to allow injection of a fake in tests.
+@visibleForTesting
+abstract class XdgProcessRunner {
+ /// Runs the given command synchronously.
+ ProcessResult runSync(
+ String executable,
+ List<String> arguments, {
+ Encoding? stdoutEncoding = systemEncoding,
+ Encoding? stderrEncoding = systemEncoding,
+ });
+}
+
+class _DefaultProcessRunner implements XdgProcessRunner {
+ const _DefaultProcessRunner();
+
+ @override
+ ProcessResult runSync(String executable, List<String> arguments,
+ {Encoding? stdoutEncoding = systemEncoding,
+ Encoding? stderrEncoding = systemEncoding}) {
+ return Process.runSync(
+ executable,
+ arguments,
+ stdoutEncoding: stdoutEncoding,
+ stderrEncoding: stderrEncoding,
+ );
+ }
+}
+
+/// A testing function that replaces the process runner used to run
+/// xdg-user-path with the one supplied.
///
/// Only available to tests.
@visibleForTesting
-set xdgProcessManager(ProcessManager processManager) {
- _processManager = processManager;
+set xdgProcessRunner(XdgProcessRunner processRunner) {
+ _processRunner = processRunner;
}
-ProcessManager _processManager = const LocalProcessManager();
+XdgProcessRunner _processRunner = const _DefaultProcessRunner();
List<Directory> _directoryListFromEnvironment(
String envVar, List<Directory> fallback) {
@@ -152,13 +182,20 @@
///
/// If the `xdg-user-dir` executable is not present this returns null.
Directory? getUserDirectory(String dirName) {
- if (!_processManager.canRun('xdg-user-dir')) {
- return null;
+ final ProcessResult result;
+ try {
+ result = _processRunner.runSync(
+ 'xdg-user-dir',
+ <String>[dirName],
+ stdoutEncoding: utf8,
+ );
+ } on ProcessException catch (e) {
+ // Silently return null if it's missing, otherwise pass the exception up.
+ if (e.errorCode == _noSuchFileError) {
+ return null;
+ }
+ rethrow;
}
- final ProcessResult result = _processManager.runSync(
- <String>['xdg-user-dir', dirName],
- stdoutEncoding: utf8,
- );
final String path = (result.stdout as String).split('\n')[0];
return Directory(path);
}
diff --git a/packages/xdg_directories/pubspec.yaml b/packages/xdg_directories/pubspec.yaml
index edf6517..e9c4628 100644
--- a/packages/xdg_directories/pubspec.yaml
+++ b/packages/xdg_directories/pubspec.yaml
@@ -2,7 +2,7 @@
description: A Dart package for reading XDG directory configuration information on Linux.
repository: https://github.com/flutter/packages/tree/main/packages/xdg_directories
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+xdg_directories%22
-version: 1.0.0
+version: 1.0.1
environment:
sdk: ">=2.18.0 <4.0.0"
@@ -13,7 +13,6 @@
dependencies:
meta: ^1.3.0
path: ^1.8.0
- process: ^4.0.0
dev_dependencies:
test: ^1.16.0
diff --git a/packages/xdg_directories/test/xdg_directories_test.dart b/packages/xdg_directories/test/xdg_directories_test.dart
index b3c95d3..9c99afd 100644
--- a/packages/xdg_directories/test/xdg_directories_test.dart
+++ b/packages/xdg_directories/test/xdg_directories_test.dart
@@ -6,7 +6,6 @@
import 'dart:io';
import 'package:path/path.dart' as path;
-import 'package:process/process.dart';
import 'package:test/fake.dart';
import 'package:test/test.dart';
import 'package:xdg_directories/xdg_directories.dart' as xdg;
@@ -27,6 +26,8 @@
String testPath(String subdir) => path.join(testRootPath(), subdir);
setUp(() {
+ xdg.xdgProcessRunner =
+ FakeProcessRunner(<String, String>{}, canRunExecutable: false);
tmpDir = Directory.systemTemp.createTempSync('xdg_test');
fakeEnv.clear();
fakeEnv['HOME'] = testRootPath();
@@ -103,24 +104,22 @@
'TEMPLATES': testPath('Templates'),
'VIDEOS': testPath('Videos'),
};
- xdg.xdgProcessManager = FakeProcessManager(expected);
+ xdg.xdgProcessRunner = FakeProcessRunner(expected);
final Set<String> userDirs = xdg.getUserDirectoryNames();
expect(userDirs, equals(expected.keys.toSet()));
for (final String key in userDirs) {
expect(xdg.getUserDirectory(key)!.path, equals(expected[key]),
reason: 'Path $key value not correct');
}
- xdg.xdgProcessManager = const LocalProcessManager();
});
test('Returns null when xdg-user-dir executable is not present', () {
- xdg.xdgProcessManager = FakeProcessManager(
+ xdg.xdgProcessRunner = FakeProcessRunner(
<String, String>{},
canRunExecutable: false,
);
expect(xdg.getUserDirectory('DESKTOP'), isNull,
reason: 'Found xdg user directory without access to xdg-user-dir');
- xdg.xdgProcessManager = const LocalProcessManager();
});
test('Throws StateError when HOME not set', () {
@@ -131,27 +130,22 @@
});
}
-class FakeProcessManager extends Fake implements ProcessManager {
- FakeProcessManager(this.expected, {this.canRunExecutable = true});
+class FakeProcessRunner extends Fake implements xdg.XdgProcessRunner {
+ FakeProcessRunner(this.expected, {this.canRunExecutable = true});
Map<String, String> expected;
final bool canRunExecutable;
@override
ProcessResult runSync(
- List<dynamic> command, {
- String? workingDirectory,
- Map<String, String>? environment,
- bool includeParentEnvironment = true,
- bool runInShell = false,
- Encoding stdoutEncoding = systemEncoding,
- Encoding stderrEncoding = systemEncoding,
+ String executable,
+ List<String> arguments, {
+ Encoding? stdoutEncoding = systemEncoding,
+ Encoding? stderrEncoding = systemEncoding,
}) {
- return ProcessResult(0, 0, expected[command[1]], '');
- }
-
- @override
- bool canRun(dynamic executable, {String? workingDirectory}) {
- return canRunExecutable;
+ if (!canRunExecutable) {
+ throw ProcessException(executable, arguments, 'No such executable', 2);
+ }
+ return ProcessResult(0, 0, expected[arguments.first], '');
}
}