mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
137 lines
5.3 KiB
Dart
137 lines
5.3 KiB
Dart
// Copyright 2015 The Chromium 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 'package:file/file.dart';
|
|
import 'package:file/local.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:file/record_replay.dart';
|
|
|
|
|
|
import 'common.dart' show throwToolExit;
|
|
import 'context.dart';
|
|
import 'process.dart';
|
|
|
|
export 'package:file/file.dart';
|
|
export 'package:file/local.dart';
|
|
|
|
const String _kRecordingType = 'file';
|
|
const FileSystem _kLocalFs = const LocalFileSystem();
|
|
|
|
/// Currently active implementation of the file system.
|
|
///
|
|
/// By default it uses local disk-based implementation. Override this in tests
|
|
/// with [MemoryFileSystem].
|
|
FileSystem get fs => context == null ? _kLocalFs : context[FileSystem];
|
|
|
|
/// Enables recording of file system activity to the specified base recording
|
|
/// [location].
|
|
///
|
|
/// This sets the [active file system](fs) to one that records all invocation
|
|
/// activity before delegating to a [LocalFileSystem].
|
|
///
|
|
/// Activity will be recorded in a subdirectory of [location] named `"file"`.
|
|
/// It is permissible for [location] to represent an existing non-empty
|
|
/// directory as long as there is no collision with the `"file"` subdirectory.
|
|
void enableRecordingFileSystem(String location) {
|
|
final FileSystem originalFileSystem = fs;
|
|
final Directory dir = getRecordingSink(location, _kRecordingType);
|
|
final RecordingFileSystem fileSystem = new RecordingFileSystem(
|
|
delegate: _kLocalFs, destination: dir);
|
|
addShutdownHook(() async {
|
|
await fileSystem.recording.flush();
|
|
context.setVariable(FileSystem, originalFileSystem);
|
|
}, ShutdownStage.SERIALIZE_RECORDING);
|
|
context.setVariable(FileSystem, fileSystem);
|
|
}
|
|
|
|
/// Enables file system replay mode.
|
|
///
|
|
/// This sets the [active file system](fs) to one that replays invocation
|
|
/// activity from a previously recorded set of invocations.
|
|
///
|
|
/// [location] must represent a directory to which file system activity has
|
|
/// been recorded (i.e. the result of having been previously passed to
|
|
/// [enableRecordingFileSystem]), or a [ToolExit] will be thrown.
|
|
void enableReplayFileSystem(String location) {
|
|
final Directory dir = getReplaySource(location, _kRecordingType);
|
|
context.setVariable(FileSystem, new ReplayFileSystem(recording: dir));
|
|
}
|
|
|
|
/// Create the ancestor directories of a file path if they do not already exist.
|
|
void ensureDirectoryExists(String filePath) {
|
|
final String dirPath = fs.path.dirname(filePath);
|
|
if (fs.isDirectorySync(dirPath))
|
|
return;
|
|
try {
|
|
fs.directory(dirPath).createSync(recursive: true);
|
|
} on FileSystemException catch (e) {
|
|
throwToolExit('Failed to create directory "$dirPath": ${e.osError.message}');
|
|
}
|
|
}
|
|
|
|
/// Recursively copies `srcDir` to `destDir`.
|
|
///
|
|
/// Creates `destDir` if needed.
|
|
void copyDirectorySync(Directory srcDir, Directory destDir) {
|
|
if (!srcDir.existsSync())
|
|
throw new Exception('Source directory "${srcDir.path}" does not exist, nothing to copy');
|
|
|
|
if (!destDir.existsSync())
|
|
destDir.createSync(recursive: true);
|
|
|
|
srcDir.listSync().forEach((FileSystemEntity entity) {
|
|
final String newPath = destDir.fileSystem.path.join(destDir.path, entity.basename);
|
|
if (entity is File) {
|
|
final File newFile = destDir.fileSystem.file(newPath);
|
|
newFile.writeAsBytesSync(entity.readAsBytesSync());
|
|
} else if (entity is Directory) {
|
|
copyDirectorySync(
|
|
entity, destDir.fileSystem.directory(newPath));
|
|
} else {
|
|
throw new Exception('${entity.path} is neither File nor Directory');
|
|
}
|
|
});
|
|
}
|
|
|
|
/// Gets a directory to act as a recording destination, creating the directory
|
|
/// as necessary.
|
|
///
|
|
/// The directory will exist in the local file system, be named [basename], and
|
|
/// be a child of the directory identified by [dirname].
|
|
///
|
|
/// If the target directory already exists as a directory, the existing
|
|
/// directory must be empty, or a [ToolExit] will be thrown. If the target
|
|
/// directory exists as an entity other than a directory, a [ToolExit] will
|
|
/// also be thrown.
|
|
Directory getRecordingSink(String dirname, String basename) {
|
|
final String location = _kLocalFs.path.join(dirname, basename);
|
|
switch (_kLocalFs.typeSync(location, followLinks: false)) {
|
|
case FileSystemEntityType.FILE:
|
|
case FileSystemEntityType.LINK:
|
|
throwToolExit('Invalid record-to location: $dirname ("$basename" exists as non-directory)');
|
|
break;
|
|
case FileSystemEntityType.DIRECTORY:
|
|
if (_kLocalFs.directory(location).listSync(followLinks: false).isNotEmpty)
|
|
throwToolExit('Invalid record-to location: $dirname ("$basename" is not empty)');
|
|
break;
|
|
case FileSystemEntityType.NOT_FOUND:
|
|
_kLocalFs.directory(location).createSync(recursive: true);
|
|
}
|
|
return _kLocalFs.directory(location);
|
|
}
|
|
|
|
/// Gets a directory that holds a saved recording to be used for the purpose of
|
|
/// replay.
|
|
///
|
|
/// The directory will exist in the local file system, be named [basename], and
|
|
/// be a child of the directory identified by [dirname].
|
|
///
|
|
/// If the target directory does not exist, a [ToolExit] will be thrown.
|
|
Directory getReplaySource(String dirname, String basename) {
|
|
final Directory dir = _kLocalFs.directory(_kLocalFs.path.join(dirname, basename));
|
|
if (!dir.existsSync())
|
|
throwToolExit('Invalid replay-from location: $dirname ("$basename" does not exist)');
|
|
return dir;
|
|
}
|