Add MultiRootFileSystem to better support using --filesystem-root. (#82991)
This commit is contained in:
parent
bfff43cf6f
commit
fdb1fb184d
271
packages/flutter_tools/lib/src/base/multi_root_file_system.dart
Normal file
271
packages/flutter_tools/lib/src/base/multi_root_file_system.dart
Normal file
@ -0,0 +1,271 @@
|
||||
// Copyright 2014 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
|
||||
show
|
||||
Directory,
|
||||
File,
|
||||
FileStat,
|
||||
FileSystemEntity,
|
||||
FileSystemEntityType,
|
||||
Link;
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:path/path.dart' as p; // flutter_ignore: package_path_import
|
||||
|
||||
/// A [FileSystem] that wraps the [delegate] file system to create an overlay of
|
||||
/// files from multiple [roots].
|
||||
///
|
||||
/// Regular paths or `file:` URIs are resolved directly in the underlying file
|
||||
/// system, but URIs that use a special [scheme] are resolved by searching
|
||||
/// under a set of given roots in order.
|
||||
///
|
||||
/// For example, consider the following inputs:
|
||||
///
|
||||
/// - scheme is `multi-root`
|
||||
/// - the set of roots are `/a` and `/b`
|
||||
/// - the underlying file system contains files:
|
||||
/// /root_a/dir/only_a.dart
|
||||
/// /root_a/dir/both.dart
|
||||
/// /root_b/dir/only_b.dart
|
||||
/// /root_b/dir/both.dart
|
||||
/// /other/other.dart
|
||||
///
|
||||
/// Then:
|
||||
///
|
||||
/// - file:///other/other.dart is resolved as /other/other.dart
|
||||
/// - multi-root:///dir/only_a.dart is resolved as /root_a/dir/only_a.dart
|
||||
/// - multi-root:///dir/only_b.dart is resolved as /root_b/dir/only_b.dart
|
||||
/// - multi-root:///dir/both.dart is resolved as /root_a/dir/only_a.dart
|
||||
class MultiRootFileSystem extends ForwardingFileSystem {
|
||||
MultiRootFileSystem({
|
||||
required FileSystem delegate,
|
||||
required String scheme,
|
||||
required List<String> roots,
|
||||
}) : assert(delegate != null),
|
||||
assert(roots.isNotEmpty),
|
||||
_scheme = scheme,
|
||||
_roots = roots.map((String root) => delegate.path.normalize(root)).toList(),
|
||||
super(delegate);
|
||||
|
||||
@visibleForTesting
|
||||
FileSystem get fileSystem => delegate;
|
||||
|
||||
final String _scheme;
|
||||
final List<String> _roots;
|
||||
|
||||
@override
|
||||
File file(dynamic path) => MultiRootFile(
|
||||
fileSystem: this,
|
||||
delegate: delegate.file(_resolve(path)),
|
||||
);
|
||||
|
||||
@override
|
||||
Directory directory(dynamic path) => MultiRootDirectory(
|
||||
fileSystem: this,
|
||||
delegate: delegate.directory(_resolve(path)),
|
||||
);
|
||||
|
||||
@override
|
||||
Link link(dynamic path) => MultiRootLink(
|
||||
fileSystem: this,
|
||||
delegate: delegate.link(_resolve(path)),
|
||||
);
|
||||
|
||||
@override
|
||||
Future<io.FileStat> stat(String path) =>
|
||||
delegate.stat(_resolve(path).toString());
|
||||
|
||||
@override
|
||||
io.FileStat statSync(String path) =>
|
||||
delegate.statSync(_resolve(path).toString());
|
||||
|
||||
@override
|
||||
Future<bool> identical(String path1, String path2) =>
|
||||
delegate.identical(_resolve(path1).toString(), _resolve(path2).toString());
|
||||
|
||||
@override
|
||||
bool identicalSync(String path1, String path2) =>
|
||||
delegate.identicalSync(_resolve(path1).toString(), _resolve(path2).toString());
|
||||
|
||||
@override
|
||||
Future<io.FileSystemEntityType> type(String path, {bool followLinks = true}) =>
|
||||
delegate.type(_resolve(path).toString(), followLinks: followLinks);
|
||||
|
||||
@override
|
||||
io.FileSystemEntityType typeSync(String path, {bool followLinks = true}) =>
|
||||
delegate.typeSync(_resolve(path).toString(), followLinks: followLinks);
|
||||
|
||||
// Caching the path context here and clearing when the currentDirectory setter
|
||||
// is updated works since the flutter tool restricts usage of dart:io directly
|
||||
// via the forbidden import tests. Otherwise, the path context's current
|
||||
// working directory might get out of sync, leading to unexpected results from
|
||||
// methods like `path.relative`.
|
||||
@override
|
||||
p.Context get path => _cachedPath ??= delegate.path;
|
||||
p.Context? _cachedPath;
|
||||
|
||||
@override
|
||||
set currentDirectory(dynamic path) {
|
||||
_cachedPath = null;
|
||||
delegate.currentDirectory = path;
|
||||
}
|
||||
|
||||
/// If the path is a multiroot uri, resolve to the actual path of the
|
||||
/// underlying file system. Otherwise, return as is.
|
||||
dynamic _resolve(dynamic path) {
|
||||
Uri uri;
|
||||
if (path == null) {
|
||||
return null;
|
||||
} else if (path is String) {
|
||||
uri = Uri.parse(path);
|
||||
} else if (path is Uri) {
|
||||
uri = path;
|
||||
} else if (path is FileSystemEntity) {
|
||||
uri = path.uri;
|
||||
} else {
|
||||
throw ArgumentError('Invalid type for "path": ${path?.runtimeType}');
|
||||
}
|
||||
|
||||
if (!uri.hasScheme || uri.scheme != _scheme) {
|
||||
return path;
|
||||
}
|
||||
|
||||
String? firstRootPath;
|
||||
final String relativePath = delegate.path.joinAll(uri.pathSegments);
|
||||
for (final String root in _roots) {
|
||||
final String pathWithRoot = delegate.path.join(root, relativePath);
|
||||
if (delegate.typeSync(pathWithRoot, followLinks: false) !=
|
||||
FileSystemEntityType.notFound) {
|
||||
return pathWithRoot;
|
||||
}
|
||||
firstRootPath ??= pathWithRoot;
|
||||
}
|
||||
|
||||
// If not found, construct the path with the first root.
|
||||
return firstRootPath!;
|
||||
}
|
||||
|
||||
Uri _toMultiRootUri(Uri uri) {
|
||||
if (uri.scheme != 'file') {
|
||||
return uri;
|
||||
}
|
||||
|
||||
final p.Context pathContext = delegate.path;
|
||||
final bool isWindows = pathContext.style == p.Style.windows;
|
||||
final String path = uri.toFilePath(windows: isWindows);
|
||||
for (final String root in _roots) {
|
||||
if (path.startsWith('$root${pathContext.separator}')) {
|
||||
String pathWithoutRoot = path.substring(root.length + 1);
|
||||
if (isWindows) {
|
||||
// Convert the path from Windows style
|
||||
pathWithoutRoot = p.url.joinAll(pathContext.split(pathWithoutRoot));
|
||||
}
|
||||
return Uri.parse('$_scheme:///$pathWithoutRoot');
|
||||
}
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'MultiRootFileSystem(scheme = $_scheme, roots = $_roots, delegate = $delegate)';
|
||||
}
|
||||
|
||||
abstract class MultiRootFileSystemEntity<T extends FileSystemEntity,
|
||||
D extends io.FileSystemEntity> extends ForwardingFileSystemEntity<T, D> {
|
||||
MultiRootFileSystemEntity({
|
||||
required this.fileSystem,
|
||||
required this.delegate,
|
||||
});
|
||||
|
||||
@override
|
||||
final D delegate;
|
||||
|
||||
@override
|
||||
final MultiRootFileSystem fileSystem;
|
||||
|
||||
@override
|
||||
File wrapFile(io.File delegate) => MultiRootFile(
|
||||
fileSystem: fileSystem,
|
||||
delegate: delegate,
|
||||
);
|
||||
|
||||
@override
|
||||
Directory wrapDirectory(io.Directory delegate) => MultiRootDirectory(
|
||||
fileSystem: fileSystem,
|
||||
delegate: delegate,
|
||||
);
|
||||
|
||||
@override
|
||||
Link wrapLink(io.Link delegate) => MultiRootLink(
|
||||
fileSystem: fileSystem,
|
||||
delegate: delegate,
|
||||
);
|
||||
|
||||
@override
|
||||
Uri get uri => fileSystem._toMultiRootUri(delegate.uri);
|
||||
}
|
||||
|
||||
class MultiRootFile extends MultiRootFileSystemEntity<File, io.File>
|
||||
with ForwardingFile {
|
||||
MultiRootFile({
|
||||
required MultiRootFileSystem fileSystem,
|
||||
required io.File delegate,
|
||||
}) : super(
|
||||
fileSystem: fileSystem,
|
||||
delegate: delegate,
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'MultiRootFile(fileSystem = $fileSystem, delegate = $delegate)';
|
||||
}
|
||||
|
||||
class MultiRootDirectory
|
||||
extends MultiRootFileSystemEntity<Directory, io.Directory>
|
||||
with ForwardingDirectory<Directory> {
|
||||
MultiRootDirectory({
|
||||
required MultiRootFileSystem fileSystem,
|
||||
required io.Directory delegate,
|
||||
}) : super(
|
||||
fileSystem: fileSystem,
|
||||
delegate: delegate,
|
||||
);
|
||||
|
||||
// For the childEntity methods, we first obtain an instance of the entity
|
||||
// from the underlying file system, then invoke childEntity() on it, then
|
||||
// wrap in the ErrorHandling version.
|
||||
@override
|
||||
Directory childDirectory(String basename) =>
|
||||
fileSystem.directory(fileSystem.path.join(delegate.path, basename));
|
||||
|
||||
@override
|
||||
File childFile(String basename) =>
|
||||
fileSystem.file(fileSystem.path.join(delegate.path, basename));
|
||||
|
||||
@override
|
||||
Link childLink(String basename) =>
|
||||
fileSystem.link(fileSystem.path.join(delegate.path, basename));
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'MultiRootDirectory(fileSystem = $fileSystem, delegate = $delegate)';
|
||||
}
|
||||
|
||||
class MultiRootLink extends MultiRootFileSystemEntity<Link, io.Link>
|
||||
with ForwardingLink {
|
||||
MultiRootLink({
|
||||
required MultiRootFileSystem fileSystem,
|
||||
required io.Link delegate,
|
||||
}) : super(
|
||||
fileSystem: fileSystem,
|
||||
delegate: delegate,
|
||||
);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
'MultiRootLink(fileSystem = $fileSystem, delegate = $delegate)';
|
||||
}
|
@ -608,8 +608,8 @@ class RunCommand extends RunCommandBase {
|
||||
for (final Device device in devices)
|
||||
await FlutterDevice.create(
|
||||
device,
|
||||
fileSystemRoots: stringsArg(FlutterOptions.kFileSystemRoot),
|
||||
fileSystemScheme: stringArg(FlutterOptions.kFileSystemScheme),
|
||||
fileSystemRoots: fileSystemRoots,
|
||||
fileSystemScheme: fileSystemScheme,
|
||||
experimentalFlags: expFlags,
|
||||
target: targetFile,
|
||||
buildInfo: buildInfo,
|
||||
|
@ -36,14 +36,14 @@ void _renderTemplateToFile(String template, dynamic context, File file, Template
|
||||
|
||||
Plugin _pluginFromPackage(String name, Uri packageRoot, Set<String> appDependencies, {FileSystem fileSystem}) {
|
||||
final FileSystem fs = fileSystem ?? globals.fs;
|
||||
final String pubspecPath = fs.path.fromUri(packageRoot.resolve('pubspec.yaml'));
|
||||
if (!fs.isFileSync(pubspecPath)) {
|
||||
final File pubspecFile = fs.file(packageRoot.resolve('pubspec.yaml'));
|
||||
if (!pubspecFile.existsSync()) {
|
||||
return null;
|
||||
}
|
||||
dynamic pubspec;
|
||||
|
||||
try {
|
||||
pubspec = loadYaml(fs.file(pubspecPath).readAsStringSync());
|
||||
pubspec = loadYaml(pubspecFile.readAsStringSync());
|
||||
} on YamlException catch (err) {
|
||||
globals.printTrace('Failed to parse plugin manifest for $name: $err');
|
||||
// Do nothing, potentially not a plugin.
|
||||
|
@ -1070,7 +1070,7 @@ abstract class ResidentRunner extends ResidentHandlers {
|
||||
String dillOutputPath,
|
||||
this.machine = false,
|
||||
ResidentDevtoolsHandlerFactory devtoolsHandler = createDefaultHandler,
|
||||
}) : mainPath = globals.fs.path.absolute(target),
|
||||
}) : mainPath = globals.fs.file(target).absolute.path,
|
||||
packagesFilePath = debuggingOptions.buildInfo.packagesPath,
|
||||
projectRootPath = projectRootPath ?? globals.fs.currentDirectory.path,
|
||||
_dillOutputPath = dillOutputPath,
|
||||
|
@ -1242,7 +1242,8 @@ class ProjectFileInvalidator {
|
||||
for (final Uri uri in urisToScan) {
|
||||
waitList.add(pool.withResource<void>(
|
||||
() => _fileSystem
|
||||
.stat(uri.toFilePath(windows: _platform.isWindows))
|
||||
.file(uri)
|
||||
.stat()
|
||||
.then((FileStat stat) {
|
||||
final DateTime updatedAt = stat.modified;
|
||||
if (updatedAt != null && updatedAt.isAfter(lastCompiled)) {
|
||||
@ -1254,17 +1255,16 @@ class ProjectFileInvalidator {
|
||||
await Future.wait<void>(waitList);
|
||||
} else {
|
||||
for (final Uri uri in urisToScan) {
|
||||
final DateTime updatedAt = _fileSystem.statSync(
|
||||
uri.toFilePath(windows: _platform.isWindows)).modified;
|
||||
final DateTime updatedAt = _fileSystem.file(uri).statSync().modified;
|
||||
if (updatedAt != null && updatedAt.isAfter(lastCompiled)) {
|
||||
invalidatedFiles.add(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
// We need to check the .packages file too since it is not used in compilation.
|
||||
final Uri packageUri = _fileSystem.file(packagesPath).uri;
|
||||
final DateTime updatedAt = _fileSystem.statSync(
|
||||
packageUri.toFilePath(windows: _platform.isWindows)).modified;
|
||||
final File packageFile = _fileSystem.file(packagesPath);
|
||||
final Uri packageUri = packageFile.uri;
|
||||
final DateTime updatedAt = packageFile.statSync().modified;
|
||||
if (updatedAt != null && updatedAt.isAfter(lastCompiled)) {
|
||||
invalidatedFiles.add(packageUri);
|
||||
packageConfig = await _createPackageConfig(packagesPath);
|
||||
|
@ -289,6 +289,22 @@ abstract class FlutterCommand extends Command<void> {
|
||||
/// This can be overridden by some of its subclasses.
|
||||
String get packagesPath => globalResults['packages'] as String;
|
||||
|
||||
/// The value of the `--filesystem-scheme` argument.
|
||||
///
|
||||
/// This can be overridden by some of its subclasses.
|
||||
String get fileSystemScheme =>
|
||||
argParser.options.containsKey(FlutterOptions.kFileSystemScheme)
|
||||
? stringArg(FlutterOptions.kFileSystemScheme)
|
||||
: null;
|
||||
|
||||
/// The values of the `--filesystem-root` argument.
|
||||
///
|
||||
/// This can be overridden by some of its subclasses.
|
||||
List<String> get fileSystemRoots =>
|
||||
argParser.options.containsKey(FlutterOptions.kFileSystemRoot)
|
||||
? stringsArg(FlutterOptions.kFileSystemRoot)
|
||||
: null;
|
||||
|
||||
void usesPubOption({bool hide = false}) {
|
||||
argParser.addFlag('pub',
|
||||
defaultsTo: true,
|
||||
@ -1032,12 +1048,8 @@ abstract class FlutterCommand extends Command<void> {
|
||||
extraGenSnapshotOptions: extraGenSnapshotOptions?.isNotEmpty ?? false
|
||||
? extraGenSnapshotOptions
|
||||
: null,
|
||||
fileSystemRoots: argParser.options.containsKey(FlutterOptions.kFileSystemRoot)
|
||||
? stringsArg(FlutterOptions.kFileSystemRoot)
|
||||
: null,
|
||||
fileSystemScheme: argParser.options.containsKey(FlutterOptions.kFileSystemScheme)
|
||||
? stringArg(FlutterOptions.kFileSystemScheme)
|
||||
: null,
|
||||
fileSystemRoots: fileSystemRoots,
|
||||
fileSystemScheme: fileSystemScheme,
|
||||
buildNumber: buildNumber,
|
||||
buildName: argParser.options.containsKey('build-name')
|
||||
? stringArg('build-name')
|
||||
|
@ -0,0 +1,189 @@
|
||||
// Copyright 2014 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/memory.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/multi_root_file_system.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
|
||||
void setupFileSystem({
|
||||
required MemoryFileSystem fs,
|
||||
required List<String> directories,
|
||||
required List<String> files,
|
||||
}) {
|
||||
for (final String directory in directories) {
|
||||
fs.directory(directory).createSync(recursive: true);
|
||||
}
|
||||
|
||||
for (final String file in files) {
|
||||
fs.file(file).writeAsStringSync('Content: $file');
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('Posix style', () {
|
||||
runTest(FileSystemStyle.posix);
|
||||
});
|
||||
|
||||
group('Windows style', () {
|
||||
runTest(FileSystemStyle.windows);
|
||||
});
|
||||
}
|
||||
|
||||
void runTest(FileSystemStyle style) {
|
||||
final String sep = style == FileSystemStyle.windows ? r'\' : '/';
|
||||
final String root = style == FileSystemStyle.windows ? r'C:\' : '/';
|
||||
final String rootUri = style == FileSystemStyle.windows ? 'C:/' : '';
|
||||
|
||||
late MultiRootFileSystem fs;
|
||||
|
||||
setUp(() {
|
||||
final MemoryFileSystem memory = MemoryFileSystem(style: style);
|
||||
setupFileSystem(
|
||||
fs: memory,
|
||||
directories: <String>[
|
||||
'${root}foo${sep}subdir',
|
||||
'${root}bar',
|
||||
'${root}bar${sep}bar_subdir',
|
||||
'${root}other${sep}directory',
|
||||
],
|
||||
files: <String>[
|
||||
'${root}foo${sep}only_in_foo',
|
||||
'${root}foo${sep}in_both',
|
||||
'${root}foo${sep}subdir${sep}in_subdir',
|
||||
'${root}bar${sep}only_in_bar',
|
||||
'${root}bar${sep}in_both',
|
||||
'${root}bar${sep}bar_subdir${sep}in_subdir',
|
||||
'${root}other${sep}directory${sep}file',
|
||||
],
|
||||
);
|
||||
|
||||
fs = MultiRootFileSystem(
|
||||
delegate: memory,
|
||||
scheme: 'scheme',
|
||||
roots: <String>[
|
||||
'${root}foo$sep',
|
||||
'${root}bar',
|
||||
],
|
||||
);
|
||||
});
|
||||
|
||||
testWithoutContext('file inside root', () {
|
||||
final File file = fs.file('${root}foo${sep}only_in_foo');
|
||||
expect(file.readAsStringSync(), 'Content: ${root}foo${sep}only_in_foo');
|
||||
expect(file.path, '${root}foo${sep}only_in_foo');
|
||||
expect(file.uri, Uri.parse('scheme:///only_in_foo'));
|
||||
});
|
||||
|
||||
testWithoutContext('file inside second root', () {
|
||||
final File file = fs.file('${root}bar${sep}only_in_bar');
|
||||
expect(file.readAsStringSync(), 'Content: ${root}bar${sep}only_in_bar');
|
||||
expect(file.path, '${root}bar${sep}only_in_bar');
|
||||
expect(file.uri, Uri.parse('scheme:///only_in_bar'));
|
||||
});
|
||||
|
||||
testWithoutContext('file outside root', () {
|
||||
final File file = fs.file('${root}other${sep}directory${sep}file');
|
||||
expect(file.readAsStringSync(),
|
||||
'Content: ${root}other${sep}directory${sep}file');
|
||||
expect(file.path, '${root}other${sep}directory${sep}file');
|
||||
expect(file.uri, Uri.parse('file:///${rootUri}other/directory/file'));
|
||||
});
|
||||
|
||||
testWithoutContext('file with file system scheme', () {
|
||||
final File file = fs.file('scheme:///only_in_foo');
|
||||
expect(file.readAsStringSync(), 'Content: ${root}foo${sep}only_in_foo');
|
||||
expect(file.path, '${root}foo${sep}only_in_foo');
|
||||
expect(file.uri, Uri.parse('scheme:///only_in_foo'));
|
||||
});
|
||||
|
||||
testWithoutContext('file with file system scheme URI', () {
|
||||
final File file = fs.file(Uri.parse('scheme:///only_in_foo'));
|
||||
expect(file.readAsStringSync(), 'Content: ${root}foo${sep}only_in_foo');
|
||||
expect(file.path, '${root}foo${sep}only_in_foo');
|
||||
expect(file.uri, Uri.parse('scheme:///only_in_foo'));
|
||||
});
|
||||
|
||||
testWithoutContext('file in second root with file system scheme', () {
|
||||
final File file = fs.file('scheme:///only_in_bar');
|
||||
expect(file.readAsStringSync(), 'Content: ${root}bar${sep}only_in_bar');
|
||||
expect(file.path, '${root}bar${sep}only_in_bar');
|
||||
expect(file.uri, Uri.parse('scheme:///only_in_bar'));
|
||||
});
|
||||
|
||||
testWithoutContext('file in second root with file system scheme URI', () {
|
||||
final File file = fs.file(Uri.parse('scheme:///only_in_bar'));
|
||||
expect(file.readAsStringSync(), 'Content: ${root}bar${sep}only_in_bar');
|
||||
expect(file.path, '${root}bar${sep}only_in_bar');
|
||||
expect(file.uri, Uri.parse('scheme:///only_in_bar'));
|
||||
});
|
||||
|
||||
testWithoutContext('file in both roots', () {
|
||||
final File file = fs.file(Uri.parse('scheme:///in_both'));
|
||||
expect(file.readAsStringSync(), 'Content: ${root}foo${sep}in_both');
|
||||
expect(file.path, '${root}foo${sep}in_both');
|
||||
expect(file.uri, Uri.parse('scheme:///in_both'));
|
||||
});
|
||||
|
||||
testWithoutContext('file with scheme in subdirectory', () {
|
||||
final File file = fs.file(Uri.parse('scheme:///subdir/in_subdir'));
|
||||
expect(file.readAsStringSync(),
|
||||
'Content: ${root}foo${sep}subdir${sep}in_subdir');
|
||||
expect(file.path, '${root}foo${sep}subdir${sep}in_subdir');
|
||||
expect(file.uri, Uri.parse('scheme:///subdir/in_subdir'));
|
||||
});
|
||||
|
||||
testWithoutContext('file in second root with scheme in subdirectory', () {
|
||||
final File file = fs.file(Uri.parse('scheme:///bar_subdir/in_subdir'));
|
||||
expect(file.readAsStringSync(),
|
||||
'Content: ${root}bar${sep}bar_subdir${sep}in_subdir');
|
||||
expect(file.path, '${root}bar${sep}bar_subdir${sep}in_subdir');
|
||||
expect(file.uri, Uri.parse('scheme:///bar_subdir/in_subdir'));
|
||||
});
|
||||
|
||||
testWithoutContext('non-existent file with scheme', () {
|
||||
final File file = fs.file(Uri.parse('scheme:///not_exist'));
|
||||
expect(file.uri, Uri.parse('scheme:///not_exist'));
|
||||
expect(file.path, '${root}foo${sep}not_exist');
|
||||
});
|
||||
|
||||
testWithoutContext('stat', () async {
|
||||
expect((await fs.stat('${root}foo${sep}only_in_foo')).type, io.FileSystemEntityType.file);
|
||||
expect((await fs.stat('scheme:///only_in_foo')).type, io.FileSystemEntityType.file);
|
||||
expect(fs.statSync('${root}foo${sep}only_in_foo').type, io.FileSystemEntityType.file);
|
||||
expect(fs.statSync('scheme:///only_in_foo').type, io.FileSystemEntityType.file);
|
||||
});
|
||||
|
||||
testWithoutContext('type', () async {
|
||||
expect(await fs.type('${root}foo${sep}only_in_foo'), io.FileSystemEntityType.file);
|
||||
expect(await fs.type('scheme:///only_in_foo'), io.FileSystemEntityType.file);
|
||||
expect(await fs.type('${root}foo${sep}subdir'), io.FileSystemEntityType.directory);
|
||||
expect(await fs.type('scheme:///subdir'), io.FileSystemEntityType.directory);
|
||||
expect(await fs.type('${root}foo${sep}not_found'), io.FileSystemEntityType.notFound);
|
||||
expect(await fs.type('scheme:///not_found'), io.FileSystemEntityType.notFound);
|
||||
|
||||
expect(fs.typeSync('${root}foo${sep}only_in_foo'), io.FileSystemEntityType.file);
|
||||
expect(fs.typeSync('scheme:///only_in_foo'), io.FileSystemEntityType.file);
|
||||
expect(fs.typeSync('${root}foo${sep}subdir'), io.FileSystemEntityType.directory);
|
||||
expect(fs.typeSync('scheme:///subdir'), io.FileSystemEntityType.directory);
|
||||
expect(fs.typeSync('${root}foo${sep}not_found'), io.FileSystemEntityType.notFound);
|
||||
expect(fs.typeSync('scheme:///not_found'), io.FileSystemEntityType.notFound);
|
||||
});
|
||||
|
||||
testWithoutContext('identical', () async {
|
||||
expect(await fs.identical('${root}foo${sep}in_both', '${root}foo${sep}in_both'), true);
|
||||
expect(await fs.identical('${root}foo${sep}in_both', 'scheme:///in_both'), true);
|
||||
expect(await fs.identical('${root}foo${sep}in_both', 'scheme:///in_both'), true);
|
||||
expect(await fs.identical('${root}bar${sep}in_both', 'scheme:///in_both'), false);
|
||||
|
||||
expect(fs.identicalSync('${root}foo${sep}in_both', '${root}foo${sep}in_both'), true);
|
||||
expect(fs.identicalSync('${root}foo${sep}in_both', 'scheme:///in_both'), true);
|
||||
expect(fs.identicalSync('${root}foo${sep}in_both', 'scheme:///in_both'), true);
|
||||
expect(fs.identicalSync('${root}bar${sep}in_both', 'scheme:///in_both'), false);
|
||||
});
|
||||
}
|
@ -509,6 +509,18 @@ void main() {
|
||||
expect(buildInfo.packagesPath, 'foo');
|
||||
});
|
||||
|
||||
testUsingContext('use fileSystemScheme to generate BuildInfo', () async {
|
||||
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(fileSystemScheme: 'foo');
|
||||
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
|
||||
expect(buildInfo.fileSystemScheme, 'foo');
|
||||
});
|
||||
|
||||
testUsingContext('use fileSystemRoots to generate BuildInfo', () async {
|
||||
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(fileSystemRoots: <String>['foo', 'bar']);
|
||||
final BuildInfo buildInfo = await flutterCommand.getBuildInfo(forcedBuildMode: BuildMode.debug);
|
||||
expect(buildInfo.fileSystemRoots, <String>['foo', 'bar']);
|
||||
});
|
||||
|
||||
testUsingContext('dds options', () async {
|
||||
final FakeDdsCommand ddsCommand = FakeDdsCommand();
|
||||
final CommandRunner<void> runner = createTestCommandRunner(ddsCommand);
|
||||
|
@ -16,6 +16,8 @@ class DummyFlutterCommand extends FlutterCommand {
|
||||
this.name = 'dummy',
|
||||
this.commandFunction,
|
||||
this.packagesPath,
|
||||
this.fileSystemScheme,
|
||||
this.fileSystemRoots,
|
||||
});
|
||||
|
||||
final bool noUsagePath;
|
||||
@ -40,4 +42,10 @@ class DummyFlutterCommand extends FlutterCommand {
|
||||
|
||||
@override
|
||||
final String packagesPath;
|
||||
|
||||
@override
|
||||
final String fileSystemScheme;
|
||||
|
||||
@override
|
||||
final List<String> fileSystemRoots;
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ void main() {
|
||||
fileSystem.path.join(flutterTools, 'lib', 'src', 'base', 'io.dart'),
|
||||
fileSystem.path.join(flutterTools, 'lib', 'src', 'base', 'platform.dart'),
|
||||
fileSystem.path.join(flutterTools, 'lib', 'src', 'base', 'error_handling_io.dart'),
|
||||
fileSystem.path.join(flutterTools, 'lib', 'src', 'base', 'multi_root_file_system.dart'),
|
||||
];
|
||||
bool _isNotAllowed(FileSystemEntity entity) => allowedPaths.every((String path) => path != entity.path);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user