[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], '');
   }
 }