Finish implementation of ReplayFileSystem (#33)
Fixes #11
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6114cbd..0a006da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-#### 1.0.2
+#### 2.0.0
* Improved `toString` implementations in file system entity classes
* Added `ForwardingFileSystem` and associated forwarding classes to the
diff --git a/lib/src/backends/record_replay/codecs.dart b/lib/src/backends/record_replay/codecs.dart
index 8b6bb55..2ecd1f7 100644
--- a/lib/src/backends/record_replay/codecs.dart
+++ b/lib/src/backends/record_replay/codecs.dart
@@ -20,75 +20,6 @@
import 'replay_random_access_file.dart';
import 'result_reference.dart';
-/// Converter that leaves object untouched.
-const Converter<dynamic, dynamic> kPassthrough = const _PassthroughConverter();
-
-/// Converter that will turn an object into a [Future] of that object.
-const Converter<dynamic, dynamic> kFutureReviver =
- const _FutureReviver<dynamic>();
-
-/// Converter that will convert an [Iterable] into a [Stream].
-Converter<dynamic, dynamic> kStreamReviver = const _StreamReviver();
-
-/// Converter that will deserialize a [DateTime].
-const Converter<dynamic, dynamic> kDateTimeReviver = _DateTimeCodec.kDecoder;
-
-/// Converter that will deserialize a [FileStat].
-const Converter<dynamic, dynamic> kFileStatReviver = _FileStatCodec.kDecoder;
-
-/// Converter that will deserialize a [FileSystemEntityType].
-const Converter<dynamic, dynamic> kEntityTypeReviver =
- _EntityTypeCodec.kDecoder;
-
-/// Converter that will deserialize a [path.Context].
-const Converter<dynamic, dynamic> kPathContextReviver =
- _PathContextCodec.kDecoder;
-
-/// Converter that will deserialize a [Uri].
-const Converter<dynamic, dynamic> kUriReviver = _UriCodec.kDecoder;
-
-/// Converter that will deserialize a [Encoding].
-const Converter<dynamic, dynamic> kEncodingReviver = _EncodingCodec.kDecoder;
-
-/// Converter that will deserialize a [FileSystemEvent].
-const Converter<dynamic, dynamic> kFileSystemEventReviver =
- _FileSystemEventCodec.kDecoder;
-
-/// Converter that will deserialize each element of a [List] by delegating to
-/// the specified [elementReviver].
-Converter<dynamic, dynamic> listReviver(
- Converter<dynamic, dynamic> elementReviver) =>
- new _ListReviver(elementReviver);
-
-/// Converter that will deserialize a blob file reference into the file's bytes.
-Converter<dynamic, dynamic> blobReviver(ReplayFileSystemImpl fileSystem) =>
- new _BlobReviver(fileSystem);
-
-/// Converter that will deserialize a [ReplayDirectory].
-Converter<dynamic, dynamic> directoryReviver(ReplayFileSystemImpl fileSystem) =>
- new _DirectoryReviver(fileSystem);
-
-/// Converter that will deserialize a [ReplayFile].
-Converter<dynamic, dynamic> fileReviver(ReplayFileSystemImpl fileSystem) =>
- new _FileReviver(fileSystem);
-
-/// Converter that will deserialize a [ReplayLink].
-Converter<dynamic, dynamic> linkReviver(ReplayFileSystemImpl fileSystem) =>
- new _LinkReviver(fileSystem);
-
-/// Converter that will deserialize an arbitrary [FileSystemEntity].
-Converter<dynamic, dynamic> entityReviver(ReplayFileSystemImpl fileSystem) =>
- new _FileSystemEntityReviver(fileSystem);
-
-/// Converter that will deserialize a [ReplayRandomAccessFile].
-Converter<dynamic, dynamic> randomAccessFileReviver(
- ReplayFileSystemImpl fileSystem) =>
- new _RandomAccessFileReviver(fileSystem);
-
-/// Converter that will deserialize a [ReplayRandomAccessFile].
-Converter<dynamic, dynamic> ioSinkReviver(ReplayFileSystemImpl fileSystem) =>
- new _IOSinkReviver(fileSystem);
-
/// Encodes an arbitrary [object] into a JSON-ready representation (a number,
/// boolean, string, null, list, or map).
///
@@ -118,24 +49,24 @@
/// object, the first one will win.
static const Map<TypeMatcher<dynamic>, Converter<Object, Object>> _encoders =
const <TypeMatcher<dynamic>, Converter<Object, Object>>{
- const TypeMatcher<num>(): const _PassthroughConverter(),
- const TypeMatcher<bool>(): const _PassthroughConverter(),
- const TypeMatcher<String>(): const _PassthroughConverter(),
- const TypeMatcher<Null>(): const _PassthroughConverter(),
+ const TypeMatcher<num>(): const Passthrough<num>(),
+ const TypeMatcher<bool>(): const Passthrough<bool>(),
+ const TypeMatcher<String>(): const Passthrough<String>(),
+ const TypeMatcher<Null>(): const Passthrough<Null>(),
const TypeMatcher<Iterable<dynamic>>(): const _IterableEncoder(),
const TypeMatcher<Map<dynamic, dynamic>>(): const _MapEncoder(),
const TypeMatcher<Symbol>(): const _SymbolEncoder(),
- const TypeMatcher<DateTime>(): _DateTimeCodec.kEncoder,
- const TypeMatcher<Uri>(): _UriCodec.kEncoder,
- const TypeMatcher<path.Context>(): _PathContextCodec.kEncoder,
+ const TypeMatcher<DateTime>(): DateTimeCodec.serialize,
+ const TypeMatcher<Uri>(): UriCodec.serialize,
+ const TypeMatcher<path.Context>(): PathContextCodec.serialize,
const TypeMatcher<ResultReference<dynamic>>(): const _ResultEncoder(),
const TypeMatcher<LiveInvocationEvent<dynamic>>(): const _EventEncoder(),
const TypeMatcher<ReplayAware>(): const _ReplayAwareEncoder(),
- const TypeMatcher<Encoding>(): _EncodingCodec.kEncoder,
+ const TypeMatcher<Encoding>(): EncodingCodec.serialize,
const TypeMatcher<FileMode>(): const _FileModeEncoder(),
- const TypeMatcher<FileStat>(): _FileStatCodec.kEncoder,
- const TypeMatcher<FileSystemEntityType>(): _EntityTypeCodec.kEncoder,
- const TypeMatcher<FileSystemEvent>(): _FileSystemEventCodec.kEncoder,
+ const TypeMatcher<FileStat>(): FileStatCodec.serialize,
+ const TypeMatcher<FileSystemEntityType>(): EntityTypeCodec.serialize,
+ const TypeMatcher<FileSystemEvent>(): FileSystemEventCodec.serialize,
};
/// Default encoder (used for types not covered in [_encoders]).
@@ -155,11 +86,13 @@
}
}
-class _PassthroughConverter extends Converter<dynamic, dynamic> {
- const _PassthroughConverter();
+/// Converter that leaves an object untouched.
+class Passthrough<T> extends Converter<T, T> {
+ /// Creates a new [Passthrough].
+ const Passthrough();
@override
- dynamic convert(dynamic input) => input;
+ T convert(T input) => input;
}
class _IterableEncoder extends Converter<Iterable<dynamic>, List<dynamic>> {
@@ -199,8 +132,10 @@
String convert(Symbol input) => getSymbolName(input);
}
-class _DateTimeCodec extends Codec<DateTime, int> {
- const _DateTimeCodec();
+/// A [DateTimeCodec] serializes and deserializes [DateTime] instances.
+class DateTimeCodec extends Codec<DateTime, int> {
+ /// Creates a new [DateTimeCodec].
+ const DateTimeCodec();
static int _encode(DateTime input) => input?.millisecondsSinceEpoch;
@@ -210,41 +145,49 @@
: new DateTime.fromMillisecondsSinceEpoch(input);
}
- static const Converter<DateTime, int> kEncoder =
+ /// Converter that serializes [DateTime] instances.
+ static const Converter<DateTime, int> serialize =
const _ForwardingConverter<DateTime, int>(_encode);
- static const Converter<int, DateTime> kDecoder =
+ /// Converter that deserializes [DateTime] instances.
+ static const Converter<int, DateTime> deserialize =
const _ForwardingConverter<int, DateTime>(_decode);
@override
- Converter<DateTime, int> get encoder => kEncoder;
+ Converter<DateTime, int> get encoder => serialize;
@override
- Converter<int, DateTime> get decoder => kDecoder;
+ Converter<int, DateTime> get decoder => deserialize;
}
-class _UriCodec extends Codec<Uri, String> {
- const _UriCodec();
+/// A [UriCodec] serializes and deserializes [Uri] instances.
+class UriCodec extends Codec<Uri, String> {
+ /// Creates a new [UriCodec].
+ const UriCodec();
static String _encode(Uri input) => input.toString();
static Uri _decode(String input) => Uri.parse(input);
- static const Converter<Uri, String> kEncoder =
+ /// Converter that serializes [Uri] instances.
+ static const Converter<Uri, String> serialize =
const _ForwardingConverter<Uri, String>(_encode);
- static const Converter<String, Uri> kDecoder =
+ /// Converter that deserializes [Uri] instances.
+ static const Converter<String, Uri> deserialize =
const _ForwardingConverter<String, Uri>(_decode);
@override
- Converter<Uri, String> get encoder => kEncoder;
+ Converter<Uri, String> get encoder => serialize;
@override
- Converter<String, Uri> get decoder => kDecoder;
+ Converter<String, Uri> get decoder => deserialize;
}
-class _PathContextCodec extends Codec<path.Context, Map<String, String>> {
- const _PathContextCodec();
+/// A [PathContextCodec] serializes and deserializes [path.Context] instances.
+class PathContextCodec extends Codec<path.Context, Map<String, String>> {
+ /// Creates a new [PathContextCodec].
+ const PathContextCodec();
static Map<String, String> _encode(path.Context input) {
return <String, String>{
@@ -264,17 +207,19 @@
);
}
- static const Converter<path.Context, Map<String, String>> kEncoder =
+ /// Converter that serializes [path.Context] instances.
+ static const Converter<path.Context, Map<String, String>> serialize =
const _ForwardingConverter<path.Context, Map<String, String>>(_encode);
- static const Converter<Map<String, String>, path.Context> kDecoder =
+ /// Converter that deserializes [path.Context] instances.
+ static const Converter<Map<String, String>, path.Context> deserialize =
const _ForwardingConverter<Map<String, String>, path.Context>(_decode);
@override
- Converter<path.Context, Map<String, String>> get encoder => kEncoder;
+ Converter<path.Context, Map<String, String>> get encoder => serialize;
@override
- Converter<Map<String, String>, path.Context> get decoder => kDecoder;
+ Converter<Map<String, String>, path.Context> get decoder => deserialize;
}
class _ResultEncoder extends Converter<ResultReference<dynamic>, Object> {
@@ -301,8 +246,10 @@
String convert(ReplayAware input) => input.identifier;
}
-class _EncodingCodec extends Codec<Encoding, String> {
- const _EncodingCodec();
+/// An [EncodingCodec] serializes and deserializes [Encoding] instances.
+class EncodingCodec extends Codec<Encoding, String> {
+ /// Creates a new [EncodingCodec].
+ const EncodingCodec();
static String _encode(Encoding input) => input.name;
@@ -315,17 +262,19 @@
return null;
}
- static const Converter<Encoding, String> kEncoder =
+ /// Converter that serializes [Encoding] instances.
+ static const Converter<Encoding, String> serialize =
const _ForwardingConverter<Encoding, String>(_encode);
- static const Converter<String, Encoding> kDecoder =
+ /// Converter that deserializes [Encoding] instances.
+ static const Converter<String, Encoding> deserialize =
const _ForwardingConverter<String, Encoding>(_decode);
@override
- Converter<Encoding, String> get encoder => kEncoder;
+ Converter<Encoding, String> get encoder => serialize;
@override
- Converter<String, Encoding> get decoder => kDecoder;
+ Converter<String, Encoding> get decoder => deserialize;
}
class _FileModeEncoder extends Converter<FileMode, String> {
@@ -349,15 +298,17 @@
}
}
-class _FileStatCodec extends Codec<FileStat, Map<String, Object>> {
- const _FileStatCodec();
+/// An [FileStatCodec] serializes and deserializes [FileStat] instances.
+class FileStatCodec extends Codec<FileStat, Map<String, Object>> {
+ /// Creates a new [FileStatCodec].
+ const FileStatCodec();
static Map<String, Object> _encode(FileStat input) {
return <String, dynamic>{
- 'changed': const _DateTimeCodec().encode(input.changed),
- 'modified': const _DateTimeCodec().encode(input.modified),
- 'accessed': const _DateTimeCodec().encode(input.accessed),
- 'type': const _EntityTypeCodec().encode(input.type),
+ 'changed': const DateTimeCodec().encode(input.changed),
+ 'modified': const DateTimeCodec().encode(input.modified),
+ 'accessed': const DateTimeCodec().encode(input.accessed),
+ 'type': const EntityTypeCodec().encode(input.type),
'mode': input.mode,
'size': input.size,
'modeString': input.modeString(),
@@ -367,21 +318,26 @@
static FileStat _decode(Map<String, Object> input) =>
new ReplayFileStat(input);
- static const Converter<FileStat, Map<String, Object>> kEncoder =
+ /// Converter that serializes [FileStat] instances.
+ static const Converter<FileStat, Map<String, Object>> serialize =
const _ForwardingConverter<FileStat, Map<String, Object>>(_encode);
- static const Converter<Map<String, Object>, FileStat> kDecoder =
+ /// Converter that deserializes [FileStat] instances.
+ static const Converter<Map<String, Object>, FileStat> deserialize =
const _ForwardingConverter<Map<String, Object>, FileStat>(_decode);
@override
- Converter<FileStat, Map<String, Object>> get encoder => kEncoder;
+ Converter<FileStat, Map<String, Object>> get encoder => serialize;
@override
- Converter<Map<String, Object>, FileStat> get decoder => kDecoder;
+ Converter<Map<String, Object>, FileStat> get decoder => deserialize;
}
-class _EntityTypeCodec extends Codec<FileSystemEntityType, String> {
- const _EntityTypeCodec();
+/// An [EntityTypeCodec] serializes and deserializes [FileSystemEntity]
+/// instances.
+class EntityTypeCodec extends Codec<FileSystemEntityType, String> {
+ /// Creates a new [EntityTypeCodec].
+ const EntityTypeCodec();
static String _encode(FileSystemEntityType input) => input.toString();
@@ -394,22 +350,26 @@
}[input];
}
- static const Converter<FileSystemEntityType, String> kEncoder =
+ /// Converter that serializes [FileSystemEntityType] instances.
+ static const Converter<FileSystemEntityType, String> serialize =
const _ForwardingConverter<FileSystemEntityType, String>(_encode);
- static const Converter<String, FileSystemEntityType> kDecoder =
+ /// Converter that deserializes [FileSystemEntityType] instances.
+ static const Converter<String, FileSystemEntityType> deserialize =
const _ForwardingConverter<String, FileSystemEntityType>(_decode);
@override
- Converter<FileSystemEntityType, String> get encoder => kEncoder;
+ Converter<FileSystemEntityType, String> get encoder => serialize;
@override
- Converter<String, FileSystemEntityType> get decoder => kDecoder;
+ Converter<String, FileSystemEntityType> get decoder => deserialize;
}
-class _FileSystemEventCodec
- extends Codec<FileSystemEvent, Map<String, Object>> {
- const _FileSystemEventCodec();
+/// A [FileSystemEventCodec] serializes and deserializes [FileSystemEvent]
+/// instances.
+class FileSystemEventCodec extends Codec<FileSystemEvent, Map<String, Object>> {
+ /// Creates a new [FileSystemEventCodec].
+ const FileSystemEventCodec();
static Map<String, Object> _encode(FileSystemEvent input) {
return <String, Object>{
@@ -422,17 +382,19 @@
static FileSystemEvent _decode(Map<String, Object> input) =>
new _FileSystemEvent(input);
- static const Converter<FileSystemEvent, Map<String, Object>> kEncoder =
+ /// Converter that serializes [FileSystemEvent] instances.
+ static const Converter<FileSystemEvent, Map<String, Object>> serialize =
const _ForwardingConverter<FileSystemEvent, Map<String, Object>>(_encode);
- static const Converter<Map<String, Object>, FileSystemEvent> kDecoder =
+ /// Converter that deserializes [FileSystemEvent] instances.
+ static const Converter<Map<String, Object>, FileSystemEvent> deserialize =
const _ForwardingConverter<Map<String, Object>, FileSystemEvent>(_decode);
@override
- Converter<FileSystemEvent, Map<String, Object>> get encoder => kEncoder;
+ Converter<FileSystemEvent, Map<String, Object>> get encoder => serialize;
@override
- Converter<Map<String, Object>, FileSystemEvent> get decoder => kDecoder;
+ Converter<Map<String, Object>, FileSystemEvent> get decoder => deserialize;
}
class _FileSystemEvent implements FileSystemEvent {
@@ -450,100 +412,143 @@
bool get isDirectory => _data['isDirectory'];
}
-class _FutureReviver<T> extends Converter<T, Future<T>> {
- const _FutureReviver();
+/// Converts an object into a [Future] that completes with that object.
+class ToFuture<T> extends Converter<T, Future<T>> {
+ /// Creates a new [ToFuture].
+ const ToFuture();
@override
Future<T> convert(T input) async => input;
}
-class _DirectoryReviver extends Converter<String, Directory> {
- final ReplayFileSystemImpl fileSystem;
- const _DirectoryReviver(this.fileSystem);
+/// Converts an object into a single-element [List] containing that object.
+class Listify<T> extends Converter<T, List<T>> {
+ /// Creates a new [Listify].
+ const Listify();
@override
- Directory convert(String input) => new ReplayDirectory(fileSystem, input);
+ List<T> convert(T input) => <T>[input];
}
-class _FileReviver extends Converter<String, File> {
- final ReplayFileSystemImpl fileSystem;
- const _FileReviver(this.fileSystem);
+/// Revives a [Directory] entity reference into a [ReplayDirectory].
+class ReviveDirectory extends Converter<String, Directory> {
+ final ReplayFileSystemImpl _fileSystem;
+
+ /// Creates a new [ReviveDirectory].
+ const ReviveDirectory(this._fileSystem);
@override
- File convert(String input) => new ReplayFile(fileSystem, input);
+ Directory convert(String input) => new ReplayDirectory(_fileSystem, input);
}
-class _LinkReviver extends Converter<String, Link> {
- final ReplayFileSystemImpl fileSystem;
- const _LinkReviver(this.fileSystem);
+/// Revives a [File] entity reference into a [ReplayFile].
+class ReviveFile extends Converter<String, File> {
+ final ReplayFileSystemImpl _fileSystem;
+
+ /// Creates a new [ReviveFile].
+ const ReviveFile(this._fileSystem);
@override
- Link convert(String input) => new ReplayLink(fileSystem, input);
+ File convert(String input) => new ReplayFile(_fileSystem, input);
}
-class _FileSystemEntityReviver extends Converter<String, FileSystemEntity> {
- final ReplayFileSystemImpl fileSystem;
- const _FileSystemEntityReviver(this.fileSystem);
+/// Revives a [Link] entity reference into a [ReplayLink].
+class ReviveLink extends Converter<String, Link> {
+ final ReplayFileSystemImpl _fileSystem;
+
+ /// Creates a new [ReviveLink].
+ const ReviveLink(this._fileSystem);
+
+ @override
+ Link convert(String input) => new ReplayLink(_fileSystem, input);
+}
+
+/// Revives a [FileSystemEntity] entity reference into a [ReplayDirectory],
+/// [ReplayFile], or a [ReplayLink] depending on the identifier of the entity
+/// reference.
+class ReviveFileSystemEntity extends Converter<String, FileSystemEntity> {
+ final ReplayFileSystemImpl _fileSystem;
+
+ /// Creates a new [ReviveFileSystemEntity].
+ const ReviveFileSystemEntity(this._fileSystem);
@override
FileSystemEntity convert(String input) {
if (input.contains('Directory')) {
- return new ReplayDirectory(fileSystem, input);
+ return new ReplayDirectory(_fileSystem, input);
} else if (input.contains('File')) {
- return new ReplayFile(fileSystem, input);
+ return new ReplayFile(_fileSystem, input);
} else {
- return new ReplayLink(fileSystem, input);
+ return new ReplayLink(_fileSystem, input);
}
}
}
-class _RandomAccessFileReviver extends Converter<String, RandomAccessFile> {
- final ReplayFileSystemImpl fileSystem;
- const _RandomAccessFileReviver(this.fileSystem);
+/// Revives a [RandomAccessFile] entity reference into a
+/// [ReplayRandomAccessFile].
+class ReviveRandomAccessFile extends Converter<String, RandomAccessFile> {
+ final ReplayFileSystemImpl _fileSystem;
+
+ /// Creates a new [ReviveRandomAccessFile] that will derive its behavior
+ /// from the specified file system's recording.
+ const ReviveRandomAccessFile(this._fileSystem);
@override
RandomAccessFile convert(String input) =>
- new ReplayRandomAccessFile(fileSystem, input);
+ new ReplayRandomAccessFile(_fileSystem, input);
}
-class _IOSinkReviver extends Converter<String, IOSink> {
- final ReplayFileSystemImpl fileSystem;
- const _IOSinkReviver(this.fileSystem);
+/// Revives an [IOSink] entity reference into a [ReplayIOSink].
+class ReviveIOSink extends Converter<String, IOSink> {
+ final ReplayFileSystemImpl _fileSystem;
+
+ /// Creates a new [ReviveIOSink] that will derive its behavior from the
+ /// specified file system's recording.
+ const ReviveIOSink(this._fileSystem);
@override
- IOSink convert(String input) => new ReplayIOSink(fileSystem, input);
+ IOSink convert(String input) => new ReplayIOSink(_fileSystem, input);
}
-class _ListReviver extends Converter<Iterable<dynamic>, List<dynamic>> {
- final Converter<dynamic, dynamic> elementReviver;
+/// Converts all elements of a [List], returning a new [List] of converted
+/// elements.
+class ConvertElements<S, T> extends Converter<List<S>, List<T>> {
+ final Converter<S, T> _delegate;
- const _ListReviver(this.elementReviver);
+ /// Creates a new [ConvertElements] that will use the specified
+ /// [elementConverter] to convert the elements of an [Iterable].
+ const ConvertElements(Converter<S, T> elementConverter)
+ : _delegate = elementConverter;
@override
- List<dynamic> convert(Iterable<dynamic> input) =>
- input.map(elementReviver.convert).toList();
+ List<T> convert(List<S> input) => input.map(_delegate.convert).toList();
}
-class _StreamReviver extends Converter<List<dynamic>, Stream<dynamic>> {
- const _StreamReviver();
+/// Converts a [List] of elements into a [Stream] of the same elements.
+class ToStream<T> extends Converter<List<T>, Stream<T>> {
+ /// Creates a new [ToStream].
+ const ToStream();
@override
- Stream<dynamic> convert(List<dynamic> input) {
- return new Stream<dynamic>.fromIterable(input);
- }
+ Stream<T> convert(List<T> input) => new Stream<T>.fromIterable(input);
}
-class _BlobReviver extends Converter<String, List<int>> {
- final ReplayFileSystemImpl fileSystem;
- const _BlobReviver(this.fileSystem);
+/// Converts a blob reference (serialized as a [String] of the form
+/// `!<filename>`) into a byte list.
+class BlobToBytes extends Converter<String, List<int>> {
+ final ReplayFileSystemImpl _fileSystem;
+
+ /// Creates a new [BlobToBytes] that will use the specified file system's
+ /// recording to load the blob.
+ const BlobToBytes(this._fileSystem);
@override
List<int> convert(String input) {
assert(input.startsWith('!'));
String basename = input.substring(1);
- String dirname = fileSystem.recording.path;
- String path = fileSystem.recording.fileSystem.path.join(dirname, basename);
- File file = fileSystem.recording.fileSystem.file(path);
+ String dirname = _fileSystem.recording.path;
+ String path = _fileSystem.recording.fileSystem.path.join(dirname, basename);
+ File file = _fileSystem.recording.fileSystem.file(path);
return file.readAsBytesSync();
}
}
diff --git a/lib/src/backends/record_replay/replay_directory.dart b/lib/src/backends/record_replay/replay_directory.dart
index 75e3b18..2d264e5 100644
--- a/lib/src/backends/record_replay/replay_directory.dart
+++ b/lib/src/backends/record_replay/replay_directory.dart
@@ -2,6 +2,7 @@
// for details. 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:async';
import 'dart:convert';
import 'package:file/file.dart';
@@ -16,24 +17,29 @@
/// Creates a new `ReplayDirectory`.
ReplayDirectory(ReplayFileSystemImpl fileSystem, String identifier)
: super(fileSystem, identifier) {
- Converter<dynamic, dynamic> convertThis = directoryReviver(fileSystem);
- Converter<dynamic, dynamic> convertFutureThis =
- convertThis.fuse(kFutureReviver);
+ Converter<String, Directory> reviveDirectory =
+ new ReviveDirectory(fileSystem);
+ Converter<String, Future<Directory>> reviveFutureDirectory =
+ reviveDirectory.fuse(const ToFuture<Directory>());
+ Converter<String, FileSystemEntity> reviveEntity =
+ new ReviveFileSystemEntity(fileSystem);
+ Converter<List<String>, List<FileSystemEntity>> reviveEntities =
+ new ConvertElements<String, FileSystemEntity>(reviveEntity);
methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #rename: convertFutureThis,
- #renameSync: convertThis,
- #delete: convertFutureThis,
- #create: convertFutureThis,
- #createSync: kPassthrough,
- #createTemp: convertFutureThis,
- #createTempSync: convertThis,
- #list: listReviver(entityReviver(fileSystem)).fuse(kStreamReviver),
- #listSync: listReviver(entityReviver(fileSystem)),
+ #rename: reviveFutureDirectory,
+ #renameSync: reviveDirectory,
+ #delete: reviveFutureDirectory,
+ #create: reviveFutureDirectory,
+ #createSync: const Passthrough<Null>(),
+ #createTemp: reviveFutureDirectory,
+ #createTempSync: reviveDirectory,
+ #list: reviveEntities.fuse(const ToStream<FileSystemEntity>()),
+ #listSync: reviveEntities,
});
properties.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #absolute: convertThis,
+ #absolute: reviveDirectory,
});
}
}
diff --git a/lib/src/backends/record_replay/replay_file.dart b/lib/src/backends/record_replay/replay_file.dart
index 149162d..fa5faf8 100644
--- a/lib/src/backends/record_replay/replay_file.dart
+++ b/lib/src/backends/record_replay/replay_file.dart
@@ -2,6 +2,7 @@
// for details. 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:async';
import 'dart:convert';
import 'package:file/file.dart';
@@ -16,40 +17,52 @@
/// Creates a new `ReplayFile`.
ReplayFile(ReplayFileSystemImpl fileSystem, String identifier)
: super(fileSystem, identifier) {
- Converter<dynamic, dynamic> convertThis = fileReviver(fileSystem);
- Converter<dynamic, dynamic> convertFutureThis =
- convertThis.fuse(kFutureReviver);
+ Converter<String, File> reviveFile = new ReviveFile(fileSystem);
+ Converter<String, Future<File>> reviveFileAsFuture =
+ reviveFile.fuse(const ToFuture<File>());
+ Converter<String, List<int>> blobToBytes = new BlobToBytes(fileSystem);
+ Converter<String, String> blobToString = blobToBytes.fuse(UTF8.decoder);
+ Converter<String, RandomAccessFile> reviveRandomAccessFile =
+ new ReviveRandomAccessFile(fileSystem);
+ // TODO(tvolkert) remove `as`: https://github.com/dart-lang/sdk/issues/28748
+ Converter<String, List<String>> lineSplitter =
+ const LineSplitter() as Converter<String, List<String>>;
+ Converter<String, List<String>> blobToLines =
+ blobToString.fuse(lineSplitter);
+ Converter<String, Stream<List<int>>> blobToByteStream = blobToBytes
+ .fuse(const Listify<List<int>>())
+ .fuse(const ToStream<List<int>>());
methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #rename: convertFutureThis,
- #renameSync: convertThis,
- #delete: convertFutureThis,
- #create: convertFutureThis,
- #createSync: kPassthrough,
- #copy: convertFutureThis,
- #copySync: convertThis,
- #length: kFutureReviver,
- #lengthSync: kPassthrough,
- #lastModified: kDateTimeReviver.fuse(kFutureReviver),
- #lastModifiedSync: kDateTimeReviver,
- #open: randomAccessFileReviver(fileSystem).fuse(kFutureReviver),
- #openSync: randomAccessFileReviver(fileSystem),
- #openRead: kStreamReviver,
- #openWrite: ioSinkReviver(fileSystem),
- #readAsBytes: blobReviver(fileSystem).fuse(kFutureReviver),
- #readAsBytesSync: blobReviver(fileSystem),
- #readAsString: kFutureReviver,
- #readAsStringSync: kPassthrough,
- #readAsLines: kFutureReviver,
- #readAsLinesSync: kPassthrough,
- #writeAsBytes: convertFutureThis,
- #writeAsBytesSync: kPassthrough,
- #writeAsString: convertFutureThis,
- #writeAsStringSync: kPassthrough,
+ #rename: reviveFileAsFuture,
+ #renameSync: reviveFile,
+ #delete: reviveFileAsFuture,
+ #create: reviveFileAsFuture,
+ #createSync: const Passthrough<Null>(),
+ #copy: reviveFileAsFuture,
+ #copySync: reviveFile,
+ #length: const ToFuture<int>(),
+ #lengthSync: const Passthrough<int>(),
+ #lastModified: DateTimeCodec.deserialize.fuse(const ToFuture<DateTime>()),
+ #lastModifiedSync: DateTimeCodec.deserialize,
+ #open: reviveRandomAccessFile.fuse(const ToFuture<RandomAccessFile>()),
+ #openSync: reviveRandomAccessFile,
+ #openRead: blobToByteStream,
+ #openWrite: new ReviveIOSink(fileSystem),
+ #readAsBytes: blobToBytes.fuse(const ToFuture<List<int>>()),
+ #readAsBytesSync: blobToBytes,
+ #readAsString: blobToString.fuse(const ToFuture<String>()),
+ #readAsStringSync: blobToString,
+ #readAsLines: blobToLines.fuse(const ToFuture<List<String>>()),
+ #readAsLinesSync: blobToLines,
+ #writeAsBytes: reviveFileAsFuture,
+ #writeAsBytesSync: const Passthrough<Null>(),
+ #writeAsString: reviveFileAsFuture,
+ #writeAsStringSync: const Passthrough<Null>(),
});
properties.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #absolute: convertThis,
+ #absolute: reviveFile,
});
}
}
diff --git a/lib/src/backends/record_replay/replay_file_stat.dart b/lib/src/backends/record_replay/replay_file_stat.dart
index 2e91c75..5883963 100644
--- a/lib/src/backends/record_replay/replay_file_stat.dart
+++ b/lib/src/backends/record_replay/replay_file_stat.dart
@@ -16,16 +16,17 @@
ReplayFileStat(Map<String, dynamic> data) : _data = data;
@override
- DateTime get changed => kDateTimeReviver.convert(_data['changed']);
+ DateTime get changed => DateTimeCodec.deserialize.convert(_data['changed']);
@override
- DateTime get modified => kDateTimeReviver.convert(_data['modified']);
+ DateTime get modified => DateTimeCodec.deserialize.convert(_data['modified']);
@override
- DateTime get accessed => kDateTimeReviver.convert(_data['accessed']);
+ DateTime get accessed => DateTimeCodec.deserialize.convert(_data['accessed']);
@override
- FileSystemEntityType get type => kEntityTypeReviver.convert(_data['type']);
+ FileSystemEntityType get type =>
+ EntityTypeCodec.deserialize.convert(_data['type']);
@override
int get mode => _data['mode'];
diff --git a/lib/src/backends/record_replay/replay_file_system.dart b/lib/src/backends/record_replay/replay_file_system.dart
index 93efa5e..0361071 100644
--- a/lib/src/backends/record_replay/replay_file_system.dart
+++ b/lib/src/backends/record_replay/replay_file_system.dart
@@ -70,24 +70,28 @@
implements ReplayFileSystem, ReplayAware {
/// Creates a new `ReplayFileSystemImpl`.
ReplayFileSystemImpl(this.recording, this.manifest) {
+ Converter<String, Directory> reviveDirectory = new ReviveDirectory(this);
+ ToFuture<FileSystemEntityType> toFutureType =
+ const ToFuture<FileSystemEntityType>();
+
methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #directory: directoryReviver(this),
- #file: fileReviver(this),
- #link: linkReviver(this),
- #stat: kFileStatReviver.fuse(kFutureReviver),
- #statSync: kFileStatReviver,
- #identical: kPassthrough.fuse(kFutureReviver),
- #identicalSync: kPassthrough,
- #type: kEntityTypeReviver.fuse(kFutureReviver),
- #typeSync: kEntityTypeReviver,
+ #directory: reviveDirectory,
+ #file: new ReviveFile(this),
+ #link: new ReviveLink(this),
+ #stat: FileStatCodec.deserialize.fuse(const ToFuture<FileStat>()),
+ #statSync: FileStatCodec.deserialize,
+ #identical: const Passthrough<bool>().fuse(const ToFuture<bool>()),
+ #identicalSync: const Passthrough<bool>(),
+ #type: EntityTypeCodec.deserialize.fuse(toFutureType),
+ #typeSync: EntityTypeCodec.deserialize,
});
properties.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #path: kPathContextReviver,
- #systemTempDirectory: directoryReviver(this),
- #currentDirectory: directoryReviver(this),
- const Symbol('currentDirectory='): kPassthrough,
- #isWatchSupported: kPassthrough,
+ #path: PathContextCodec.deserialize,
+ #systemTempDirectory: reviveDirectory,
+ #currentDirectory: reviveDirectory,
+ const Symbol('currentDirectory='): const Passthrough<Null>(),
+ #isWatchSupported: const Passthrough<bool>(),
});
}
diff --git a/lib/src/backends/record_replay/replay_file_system_entity.dart b/lib/src/backends/record_replay/replay_file_system_entity.dart
index da5631d..986d14b 100644
--- a/lib/src/backends/record_replay/replay_file_system_entity.dart
+++ b/lib/src/backends/record_replay/replay_file_system_entity.dart
@@ -2,6 +2,7 @@
// for details. 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:async';
import 'dart:convert';
import 'package:file/file.dart';
@@ -17,24 +18,30 @@
implements FileSystemEntity {
/// Creates a new `ReplayFileSystemEntity`.
ReplayFileSystemEntity(this.fileSystem, this.identifier) {
+ Converter<List<Map<String, Object>>, List<FileSystemEvent>> toEvents =
+ const ConvertElements<Map<String, Object>, FileSystemEvent>(
+ FileSystemEventCodec.deserialize);
+ Converter<List<Map<String, Object>>, Stream<FileSystemEvent>>
+ toEventStream = toEvents.fuse(const ToStream<FileSystemEvent>());
+
methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #exists: kPassthrough.fuse(kFutureReviver),
- #existsSync: kPassthrough,
- #resolveSymbolicLinks: kPassthrough.fuse(kFutureReviver),
- #resolveSymbolicLinksSync: kPassthrough,
- #stat: kFileStatReviver.fuse(kFutureReviver),
- #statSync: kFileStatReviver,
- #deleteSync: kPassthrough,
- #watch: listReviver(kFileSystemEventReviver).fuse(kStreamReviver),
+ #exists: const ToFuture<bool>(),
+ #existsSync: const Passthrough<bool>(),
+ #resolveSymbolicLinks: const ToFuture<String>(),
+ #resolveSymbolicLinksSync: const Passthrough<String>(),
+ #stat: FileStatCodec.deserialize.fuse(const ToFuture<FileStat>()),
+ #statSync: FileStatCodec.deserialize,
+ #deleteSync: const Passthrough<Null>(),
+ #watch: toEventStream,
});
properties.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #path: kPassthrough,
- #uri: kUriReviver,
- #isAbsolute: kPassthrough,
- #parent: directoryReviver(fileSystem),
- #basename: kPassthrough,
- #dirname: kPassthrough,
+ #path: const Passthrough<String>(),
+ #uri: UriCodec.deserialize,
+ #isAbsolute: const Passthrough<bool>(),
+ #parent: new ReviveDirectory(fileSystem),
+ #basename: const Passthrough<String>(),
+ #dirname: const Passthrough<String>(),
});
}
diff --git a/lib/src/backends/record_replay/replay_io_sink.dart b/lib/src/backends/record_replay/replay_io_sink.dart
index b625810..2bacb1f 100644
--- a/lib/src/backends/record_replay/replay_io_sink.dart
+++ b/lib/src/backends/record_replay/replay_io_sink.dart
@@ -15,24 +15,24 @@
class ReplayIOSink extends Object with ReplayProxyMixin implements IOSink {
final ReplayFileSystemImpl _fileSystem;
- /// Creates a new `ReplayIOSink`.
+ /// Creates a new [ReplayIOSink].
ReplayIOSink(this._fileSystem, this.identifier) {
methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #add: kPassthrough,
- #write: kPassthrough,
- #writeAll: kPassthrough,
- #writeln: kPassthrough,
- #writeCharCode: kPassthrough,
- #addError: kPassthrough,
- #addStream: kFutureReviver,
- #flush: kFutureReviver,
- #close: kFutureReviver,
+ #add: const Passthrough<Null>(),
+ #write: const Passthrough<Null>(),
+ #writeAll: const Passthrough<Null>(),
+ #writeln: const Passthrough<Null>(),
+ #writeCharCode: const Passthrough<Null>(),
+ #addError: const Passthrough<Null>(),
+ #addStream: const ToFuture<dynamic>(),
+ #flush: const ToFuture<dynamic>(),
+ #close: const ToFuture<dynamic>(),
});
properties.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #encoding: kEncodingReviver,
- const Symbol('encoding='): kPassthrough,
- #done: kPassthrough.fuse(kFutureReviver),
+ #encoding: EncodingCodec.deserialize,
+ const Symbol('encoding='): const Passthrough<Null>(),
+ #done: const Passthrough<dynamic>().fuse(const ToFuture<dynamic>()),
});
}
diff --git a/lib/src/backends/record_replay/replay_link.dart b/lib/src/backends/record_replay/replay_link.dart
index 6094ca5..bf29d01 100644
--- a/lib/src/backends/record_replay/replay_link.dart
+++ b/lib/src/backends/record_replay/replay_link.dart
@@ -2,6 +2,7 @@
// for details. 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:async';
import 'dart:convert';
import 'package:file/file.dart';
@@ -16,20 +17,24 @@
/// Creates a new `ReplayLink`.
ReplayLink(ReplayFileSystemImpl fileSystem, String identifier)
: super(fileSystem, identifier) {
+ Converter<String, Link> reviveLink = new ReviveLink(fileSystem);
+ Converter<String, Future<Link>> reviveLinkAsFuture =
+ reviveLink.fuse(const ToFuture<Link>());
+
methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #rename: linkReviver(fileSystem).fuse(kFutureReviver),
- #renameSync: linkReviver(fileSystem),
- #delete: linkReviver(fileSystem).fuse(kFutureReviver),
- #create: linkReviver(fileSystem).fuse(kFutureReviver),
- #createSync: kPassthrough,
- #update: linkReviver(fileSystem).fuse(kFutureReviver),
- #updateSync: kPassthrough,
- #target: kPassthrough.fuse(kFutureReviver),
- #targetSync: kPassthrough,
+ #rename: reviveLinkAsFuture,
+ #renameSync: reviveLink,
+ #delete: reviveLinkAsFuture,
+ #create: reviveLinkAsFuture,
+ #createSync: const Passthrough<Null>(),
+ #update: reviveLinkAsFuture,
+ #updateSync: const Passthrough<Null>(),
+ #target: const Passthrough<String>().fuse(const ToFuture<String>()),
+ #targetSync: const Passthrough<String>(),
});
properties.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #absolute: linkReviver(fileSystem),
+ #absolute: reviveLink,
});
}
}
diff --git a/lib/src/backends/record_replay/replay_random_access_file.dart b/lib/src/backends/record_replay/replay_random_access_file.dart
index 41774c4..c1219b4 100644
--- a/lib/src/backends/record_replay/replay_random_access_file.dart
+++ b/lib/src/backends/record_replay/replay_random_access_file.dart
@@ -2,6 +2,7 @@
// for details. 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:async';
import 'dart:convert';
import 'package:file/file.dart';
@@ -17,44 +18,45 @@
implements RandomAccessFile {
final ReplayFileSystemImpl _fileSystem;
- /// Creates a new `ReplayIOSink`.
+ /// Creates a new [ReplayRandomAccessFile].
ReplayRandomAccessFile(this._fileSystem, this.identifier) {
- Converter<dynamic, dynamic> convertFutureThis =
- randomAccessFileReviver(_fileSystem).fuse(kFutureReviver);
+ ToFuture<RandomAccessFile> toFuture = const ToFuture<RandomAccessFile>();
+ Converter<String, Future<RandomAccessFile>> reviveRandomAccessFileAsFuture =
+ new ReviveRandomAccessFile(_fileSystem).fuse(toFuture);
methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #close: convertFutureThis,
- #closeSync: kPassthrough,
- #readByte: kFutureReviver,
- #readByteSync: kPassthrough,
- #read: kFutureReviver,
- #readSync: kPassthrough,
- #readInto: kFutureReviver,
- #readIntoSync: kPassthrough,
- #writeByte: convertFutureThis,
- #writeByteSync: kPassthrough,
- #writeFrom: convertFutureThis,
- #writeFromSync: kPassthrough,
- #writeString: convertFutureThis,
- #writeStringSync: kPassthrough,
- #position: kFutureReviver,
- #positionSync: kPassthrough,
- #setPosition: convertFutureThis,
- #setPositionSync: kPassthrough,
- #truncate: convertFutureThis,
- #truncateSync: kPassthrough,
- #length: kFutureReviver,
- #lengthSync: kPassthrough,
- #flush: convertFutureThis,
- #flushSync: kPassthrough,
- #lock: convertFutureThis,
- #lockSync: kPassthrough,
- #unlock: convertFutureThis,
- #unlockSync: kPassthrough,
+ #close: reviveRandomAccessFileAsFuture,
+ #closeSync: const Passthrough<Null>(),
+ #readByte: const ToFuture<int>(),
+ #readByteSync: const Passthrough<int>(),
+ #read: const ToFuture<List<int>>(),
+ #readSync: const Passthrough<List<int>>(),
+ #readInto: const ToFuture<int>(),
+ #readIntoSync: const Passthrough<int>(),
+ #writeByte: reviveRandomAccessFileAsFuture,
+ #writeByteSync: const Passthrough<int>(),
+ #writeFrom: reviveRandomAccessFileAsFuture,
+ #writeFromSync: const Passthrough<Null>(),
+ #writeString: reviveRandomAccessFileAsFuture,
+ #writeStringSync: const Passthrough<Null>(),
+ #position: const ToFuture<int>(),
+ #positionSync: const Passthrough<int>(),
+ #setPosition: reviveRandomAccessFileAsFuture,
+ #setPositionSync: const Passthrough<Null>(),
+ #truncate: reviveRandomAccessFileAsFuture,
+ #truncateSync: const Passthrough<Null>(),
+ #length: const ToFuture<int>(),
+ #lengthSync: const Passthrough<int>(),
+ #flush: reviveRandomAccessFileAsFuture,
+ #flushSync: const Passthrough<Null>(),
+ #lock: reviveRandomAccessFileAsFuture,
+ #lockSync: const Passthrough<Null>(),
+ #unlock: reviveRandomAccessFileAsFuture,
+ #unlockSync: const Passthrough<Null>(),
});
properties.addAll(<Symbol, Converter<dynamic, dynamic>>{
- #path: kPassthrough,
+ #path: const Passthrough<String>(),
});
}
diff --git a/pubspec.yaml b/pubspec.yaml
index d9392a5..a58037f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,11 +1,11 @@
name: file
-version: 1.0.1
+version: 2.0.0
authors:
- Matan Lurey <matanl@google.com>
- Yegor Jbanov <yjbanov@google.com>
- Todd Volkert <tvolkert@google.com>
description: A pluggable, mockable file system abstraction for Dart.
-homepage: https://github.com/matanlurey/file
+homepage: https://github.com/google/file.dart
dependencies:
intl: ^0.14.0
diff --git a/test/common_tests.dart b/test/common_tests.dart
index 21312e3..630af33 100644
--- a/test/common_tests.dart
+++ b/test/common_tests.dart
@@ -10,7 +10,7 @@
import 'package:file/file.dart';
import 'package:file/testing.dart';
import 'package:test/test.dart';
-import 'package:test/test.dart' as testpkg show group, test, setUp;
+import 'package:test/test.dart' as testpkg show group, setUp, tearDown, test;
/// Callback used in [runCommonTests] to produce the root folder in which all
/// file system entities will be created.
@@ -21,8 +21,9 @@
/// [FileSystem].
typedef dynamic FileSystemGenerator();
-/// A function to run before tests (passed to [setUp]).
-typedef dynamic SetUpCallback();
+/// A function to run before tests (passed to [setUp]) or after tests
+/// (passed to [tearDown]).
+typedef dynamic SetUpTearDown();
/// Runs a suite of tests common to all file system implementations. All file
/// system implementations should run *at least* these tests to ensure
@@ -54,7 +55,8 @@
group('common', () {
FileSystemGenerator createFs;
- List<SetUpCallback> setUps;
+ List<SetUpTearDown> setUps;
+ List<SetUpTearDown> tearDowns;
FileSystem fs;
String root;
@@ -72,7 +74,8 @@
testpkg.setUp(() async {
createFs = createFileSystem;
- setUps = <SetUpCallback>[];
+ setUps = <SetUpTearDown>[];
+ tearDowns = <SetUpTearDown>[];
fs = null;
root = null;
});
@@ -81,6 +84,14 @@
testpkg.setUp(replay == null ? callback : () => setUps.add(callback));
}
+ void tearDown(callback()) {
+ if (replay == null) {
+ testpkg.tearDown(callback);
+ } else {
+ testpkg.setUp(() => tearDowns.insert(0, callback));
+ }
+ }
+
void group(String description, body()) =>
skipIfNecessary(description, () => testpkg.group(description, body));
@@ -90,13 +101,22 @@
} else {
group('rerun', () {
testpkg.setUp(() async {
- await Future.forEach(setUps, (SetUpCallback setUp) => setUp());
+ await Future.forEach(setUps, (SetUpTearDown setUp) => setUp());
await body();
+ for (SetUpTearDown tearDown in tearDowns) {
+ await tearDown();
+ }
createFs = replay;
- await Future.forEach(setUps, (SetUpCallback setUp) => setUp());
+ await Future.forEach(setUps, (SetUpTearDown setUp) => setUp());
});
testpkg.test(description, body);
+
+ testpkg.tearDown(() async {
+ for (SetUpTearDown tearDown in tearDowns) {
+ await tearDown();
+ }
+ });
});
}
});
diff --git a/test/replay_test.dart b/test/replay_test.dart
index d647f95..f68ebf3 100644
--- a/test/replay_test.dart
+++ b/test/replay_test.dart
@@ -42,14 +42,8 @@
// ReplayFileSystem does not yet replay exceptions
'.*(disallows|throws).*',
- // TODO(tvolkert): re-enable when these are implemented
- 'File > copy',
- 'File > openRead',
- 'File > openWrite',
- 'File > readAsLines',
- 'File > readAsString',
- 'File > writeAsBytes',
- 'File > writeAsString',
+ // TODO(tvolkert): Fix breakage, and re-enable
+ 'File > openWrite > ioSink > addStream',
'File > open', // Not yet implemented in MemoryFileSystem
],