[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
This commit is contained in:
stuartmorgan
2023-07-13 20:27:45 -04:00
committed by GitHub
parent 808d790d8f
commit b166a0fd91
4 changed files with 66 additions and 35 deletions

View File

@ -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

View File

@ -9,7 +9,9 @@ import 'dart:io';
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? _xdgEnvironmentOverride;
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 @@ Directory? get runtimeDir => _directoryFromEnvironment('XDG_RUNTIME_DIR');
///
/// 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);
}

View File

@ -2,7 +2,7 @@ name: xdg_directories
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 @@ platforms:
dependencies:
meta: ^1.3.0
path: ^1.8.0
process: ^4.0.0
dev_dependencies:
test: ^1.16.0

View File

@ -6,7 +6,6 @@ import 'dart:convert';
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 @@ void main() {
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 @@ XDG_VIDEOS_DIR="$HOME/Videos"
'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 @@ XDG_VIDEOS_DIR="$HOME/Videos"
});
}
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], '');
}
}