Skip to content

Commit b166a0f

Browse files
[xdg_directories] Remove process dependency (flutter#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 flutter#129787
1 parent 808d790 commit b166a0f

File tree

4 files changed

+66
-35
lines changed

4 files changed

+66
-35
lines changed

packages/xdg_directories/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 1.0.1
22

3+
* Removes `process` dependency.
34
* Updates minimum supported SDK version to Flutter 3.3/Dart 2.18.
45

56
## 1.0.0

packages/xdg_directories/lib/xdg_directories.dart

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ import 'dart:io';
99

1010
import 'package:meta/meta.dart';
1111
import 'package:path/path.dart' as path;
12-
import 'package:process/process.dart';
12+
13+
// From errno definitions.
14+
const int _noSuchFileError = 2;
1315

1416
/// An override function used by the tests to override the environment variable
1517
/// lookups using [xdgEnvironmentOverride].
@@ -36,16 +38,44 @@ EnvironmentAccessor? _xdgEnvironmentOverride;
3638
EnvironmentAccessor _getenv = _productionGetEnv;
3739
String? _productionGetEnv(String value) => Platform.environment[value];
3840

39-
/// A testing function that replaces the process manager used to run xdg-user-path
40-
/// with the one supplied.
41+
/// A wrapper around Process.runSync to allow injection of a fake in tests.
42+
@visibleForTesting
43+
abstract class XdgProcessRunner {
44+
/// Runs the given command synchronously.
45+
ProcessResult runSync(
46+
String executable,
47+
List<String> arguments, {
48+
Encoding? stdoutEncoding = systemEncoding,
49+
Encoding? stderrEncoding = systemEncoding,
50+
});
51+
}
52+
53+
class _DefaultProcessRunner implements XdgProcessRunner {
54+
const _DefaultProcessRunner();
55+
56+
@override
57+
ProcessResult runSync(String executable, List<String> arguments,
58+
{Encoding? stdoutEncoding = systemEncoding,
59+
Encoding? stderrEncoding = systemEncoding}) {
60+
return Process.runSync(
61+
executable,
62+
arguments,
63+
stdoutEncoding: stdoutEncoding,
64+
stderrEncoding: stderrEncoding,
65+
);
66+
}
67+
}
68+
69+
/// A testing function that replaces the process runner used to run
70+
/// xdg-user-path with the one supplied.
4171
///
4272
/// Only available to tests.
4373
@visibleForTesting
44-
set xdgProcessManager(ProcessManager processManager) {
45-
_processManager = processManager;
74+
set xdgProcessRunner(XdgProcessRunner processRunner) {
75+
_processRunner = processRunner;
4676
}
4777

48-
ProcessManager _processManager = const LocalProcessManager();
78+
XdgProcessRunner _processRunner = const _DefaultProcessRunner();
4979

5080
List<Directory> _directoryListFromEnvironment(
5181
String envVar, List<Directory> fallback) {
@@ -152,13 +182,20 @@ Directory? get runtimeDir => _directoryFromEnvironment('XDG_RUNTIME_DIR');
152182
///
153183
/// If the `xdg-user-dir` executable is not present this returns null.
154184
Directory? getUserDirectory(String dirName) {
155-
if (!_processManager.canRun('xdg-user-dir')) {
156-
return null;
185+
final ProcessResult result;
186+
try {
187+
result = _processRunner.runSync(
188+
'xdg-user-dir',
189+
<String>[dirName],
190+
stdoutEncoding: utf8,
191+
);
192+
} on ProcessException catch (e) {
193+
// Silently return null if it's missing, otherwise pass the exception up.
194+
if (e.errorCode == _noSuchFileError) {
195+
return null;
196+
}
197+
rethrow;
157198
}
158-
final ProcessResult result = _processManager.runSync(
159-
<String>['xdg-user-dir', dirName],
160-
stdoutEncoding: utf8,
161-
);
162199
final String path = (result.stdout as String).split('\n')[0];
163200
return Directory(path);
164201
}

packages/xdg_directories/pubspec.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: xdg_directories
22
description: A Dart package for reading XDG directory configuration information on Linux.
33
repository: https://github.com/flutter/packages/tree/main/packages/xdg_directories
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+xdg_directories%22
5-
version: 1.0.0
5+
version: 1.0.1
66

77
environment:
88
sdk: ">=2.18.0 <4.0.0"
@@ -13,7 +13,6 @@ platforms:
1313
dependencies:
1414
meta: ^1.3.0
1515
path: ^1.8.0
16-
process: ^4.0.0
1716

1817
dev_dependencies:
1918
test: ^1.16.0

packages/xdg_directories/test/xdg_directories_test.dart

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import 'dart:convert';
66
import 'dart:io';
77

88
import 'package:path/path.dart' as path;
9-
import 'package:process/process.dart';
109
import 'package:test/fake.dart';
1110
import 'package:test/test.dart';
1211
import 'package:xdg_directories/xdg_directories.dart' as xdg;
@@ -27,6 +26,8 @@ void main() {
2726
String testPath(String subdir) => path.join(testRootPath(), subdir);
2827

2928
setUp(() {
29+
xdg.xdgProcessRunner =
30+
FakeProcessRunner(<String, String>{}, canRunExecutable: false);
3031
tmpDir = Directory.systemTemp.createTempSync('xdg_test');
3132
fakeEnv.clear();
3233
fakeEnv['HOME'] = testRootPath();
@@ -103,24 +104,22 @@ XDG_VIDEOS_DIR="$HOME/Videos"
103104
'TEMPLATES': testPath('Templates'),
104105
'VIDEOS': testPath('Videos'),
105106
};
106-
xdg.xdgProcessManager = FakeProcessManager(expected);
107+
xdg.xdgProcessRunner = FakeProcessRunner(expected);
107108
final Set<String> userDirs = xdg.getUserDirectoryNames();
108109
expect(userDirs, equals(expected.keys.toSet()));
109110
for (final String key in userDirs) {
110111
expect(xdg.getUserDirectory(key)!.path, equals(expected[key]),
111112
reason: 'Path $key value not correct');
112113
}
113-
xdg.xdgProcessManager = const LocalProcessManager();
114114
});
115115

116116
test('Returns null when xdg-user-dir executable is not present', () {
117-
xdg.xdgProcessManager = FakeProcessManager(
117+
xdg.xdgProcessRunner = FakeProcessRunner(
118118
<String, String>{},
119119
canRunExecutable: false,
120120
);
121121
expect(xdg.getUserDirectory('DESKTOP'), isNull,
122122
reason: 'Found xdg user directory without access to xdg-user-dir');
123-
xdg.xdgProcessManager = const LocalProcessManager();
124123
});
125124

126125
test('Throws StateError when HOME not set', () {
@@ -131,27 +130,22 @@ XDG_VIDEOS_DIR="$HOME/Videos"
131130
});
132131
}
133132

134-
class FakeProcessManager extends Fake implements ProcessManager {
135-
FakeProcessManager(this.expected, {this.canRunExecutable = true});
133+
class FakeProcessRunner extends Fake implements xdg.XdgProcessRunner {
134+
FakeProcessRunner(this.expected, {this.canRunExecutable = true});
136135

137136
Map<String, String> expected;
138137
final bool canRunExecutable;
139138

140139
@override
141140
ProcessResult runSync(
142-
List<dynamic> command, {
143-
String? workingDirectory,
144-
Map<String, String>? environment,
145-
bool includeParentEnvironment = true,
146-
bool runInShell = false,
147-
Encoding stdoutEncoding = systemEncoding,
148-
Encoding stderrEncoding = systemEncoding,
141+
String executable,
142+
List<String> arguments, {
143+
Encoding? stdoutEncoding = systemEncoding,
144+
Encoding? stderrEncoding = systemEncoding,
149145
}) {
150-
return ProcessResult(0, 0, expected[command[1]], '');
151-
}
152-
153-
@override
154-
bool canRun(dynamic executable, {String? workingDirectory}) {
155-
return canRunExecutable;
146+
if (!canRunExecutable) {
147+
throw ProcessException(executable, arguments, 'No such executable', 2);
148+
}
149+
return ProcessResult(0, 0, expected[arguments.first], '');
156150
}
157151
}

0 commit comments

Comments
 (0)