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_link.dart'; import 'replay_random_access_file.dart'; import 'result_reference.dart'; -/// Converter that leaves object untouched. -const Converter kPassthrough = const _PassthroughConverter(); - -/// Converter that will turn an object into a [Future] of that object. -const Converter kFutureReviver = - const _FutureReviver(); - -/// Converter that will convert an [Iterable] into a [Stream]. -Converter kStreamReviver = const _StreamReviver(); - -/// Converter that will deserialize a [DateTime]. -const Converter kDateTimeReviver = _DateTimeCodec.kDecoder; - -/// Converter that will deserialize a [FileStat]. -const Converter kFileStatReviver = _FileStatCodec.kDecoder; - -/// Converter that will deserialize a [FileSystemEntityType]. -const Converter kEntityTypeReviver = - _EntityTypeCodec.kDecoder; - -/// Converter that will deserialize a [path.Context]. -const Converter kPathContextReviver = - _PathContextCodec.kDecoder; - -/// Converter that will deserialize a [Uri]. -const Converter kUriReviver = _UriCodec.kDecoder; - -/// Converter that will deserialize a [Encoding]. -const Converter kEncodingReviver = _EncodingCodec.kDecoder; - -/// Converter that will deserialize a [FileSystemEvent]. -const Converter kFileSystemEventReviver = - _FileSystemEventCodec.kDecoder; - -/// Converter that will deserialize each element of a [List] by delegating to -/// the specified [elementReviver]. -Converter listReviver( - Converter elementReviver) => - new _ListReviver(elementReviver); - -/// Converter that will deserialize a blob file reference into the file's bytes. -Converter blobReviver(ReplayFileSystemImpl fileSystem) => - new _BlobReviver(fileSystem); - -/// Converter that will deserialize a [ReplayDirectory]. -Converter directoryReviver(ReplayFileSystemImpl fileSystem) => - new _DirectoryReviver(fileSystem); - -/// Converter that will deserialize a [ReplayFile]. -Converter fileReviver(ReplayFileSystemImpl fileSystem) => - new _FileReviver(fileSystem); - -/// Converter that will deserialize a [ReplayLink]. -Converter linkReviver(ReplayFileSystemImpl fileSystem) => - new _LinkReviver(fileSystem); - -/// Converter that will deserialize an arbitrary [FileSystemEntity]. -Converter entityReviver(ReplayFileSystemImpl fileSystem) => - new _FileSystemEntityReviver(fileSystem); - -/// Converter that will deserialize a [ReplayRandomAccessFile]. -Converter randomAccessFileReviver( - ReplayFileSystemImpl fileSystem) => - new _RandomAccessFileReviver(fileSystem); - -/// Converter that will deserialize a [ReplayRandomAccessFile]. -Converter 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 @@ class _GenericEncoder extends Converter { /// object, the first one will win. static const Map, Converter> _encoders = const , Converter>{ - const TypeMatcher(): const _PassthroughConverter(), - const TypeMatcher(): const _PassthroughConverter(), - const TypeMatcher(): const _PassthroughConverter(), - const TypeMatcher(): const _PassthroughConverter(), + const TypeMatcher(): const Passthrough(), + const TypeMatcher(): const Passthrough(), + const TypeMatcher(): const Passthrough(), + const TypeMatcher(): const Passthrough(), const TypeMatcher>(): const _IterableEncoder(), const TypeMatcher>(): const _MapEncoder(), const TypeMatcher(): const _SymbolEncoder(), - const TypeMatcher(): _DateTimeCodec.kEncoder, - const TypeMatcher(): _UriCodec.kEncoder, - const TypeMatcher(): _PathContextCodec.kEncoder, + const TypeMatcher(): DateTimeCodec.serialize, + const TypeMatcher(): UriCodec.serialize, + const TypeMatcher(): PathContextCodec.serialize, const TypeMatcher>(): const _ResultEncoder(), const TypeMatcher>(): const _EventEncoder(), const TypeMatcher(): const _ReplayAwareEncoder(), - const TypeMatcher(): _EncodingCodec.kEncoder, + const TypeMatcher(): EncodingCodec.serialize, const TypeMatcher(): const _FileModeEncoder(), - const TypeMatcher(): _FileStatCodec.kEncoder, - const TypeMatcher(): _EntityTypeCodec.kEncoder, - const TypeMatcher(): _FileSystemEventCodec.kEncoder, + const TypeMatcher(): FileStatCodec.serialize, + const TypeMatcher(): EntityTypeCodec.serialize, + const TypeMatcher(): FileSystemEventCodec.serialize, }; /// Default encoder (used for types not covered in [_encoders]). @@ -155,11 +86,13 @@ class _GenericEncoder extends Converter { } } -class _PassthroughConverter extends Converter { - const _PassthroughConverter(); +/// Converter that leaves an object untouched. +class Passthrough extends Converter { + /// Creates a new [Passthrough]. + const Passthrough(); @override - dynamic convert(dynamic input) => input; + T convert(T input) => input; } class _IterableEncoder extends Converter, List> { @@ -199,8 +132,10 @@ class _SymbolEncoder extends Converter { String convert(Symbol input) => getSymbolName(input); } -class _DateTimeCodec extends Codec { - const _DateTimeCodec(); +/// A [DateTimeCodec] serializes and deserializes [DateTime] instances. +class DateTimeCodec extends Codec { + /// Creates a new [DateTimeCodec]. + const DateTimeCodec(); static int _encode(DateTime input) => input?.millisecondsSinceEpoch; @@ -210,41 +145,49 @@ class _DateTimeCodec extends Codec { : new DateTime.fromMillisecondsSinceEpoch(input); } - static const Converter kEncoder = + /// Converter that serializes [DateTime] instances. + static const Converter serialize = const _ForwardingConverter(_encode); - static const Converter kDecoder = + /// Converter that deserializes [DateTime] instances. + static const Converter deserialize = const _ForwardingConverter(_decode); @override - Converter get encoder => kEncoder; + Converter get encoder => serialize; @override - Converter get decoder => kDecoder; + Converter get decoder => deserialize; } -class _UriCodec extends Codec { - const _UriCodec(); +/// A [UriCodec] serializes and deserializes [Uri] instances. +class UriCodec extends Codec { + /// Creates a new [UriCodec]. + const UriCodec(); static String _encode(Uri input) => input.toString(); static Uri _decode(String input) => Uri.parse(input); - static const Converter kEncoder = + /// Converter that serializes [Uri] instances. + static const Converter serialize = const _ForwardingConverter(_encode); - static const Converter kDecoder = + /// Converter that deserializes [Uri] instances. + static const Converter deserialize = const _ForwardingConverter(_decode); @override - Converter get encoder => kEncoder; + Converter get encoder => serialize; @override - Converter get decoder => kDecoder; + Converter get decoder => deserialize; } -class _PathContextCodec extends Codec> { - const _PathContextCodec(); +/// A [PathContextCodec] serializes and deserializes [path.Context] instances. +class PathContextCodec extends Codec> { + /// Creates a new [PathContextCodec]. + const PathContextCodec(); static Map _encode(path.Context input) { return { @@ -264,17 +207,19 @@ class _PathContextCodec extends Codec> { ); } - static const Converter> kEncoder = + /// Converter that serializes [path.Context] instances. + static const Converter> serialize = const _ForwardingConverter>(_encode); - static const Converter, path.Context> kDecoder = + /// Converter that deserializes [path.Context] instances. + static const Converter, path.Context> deserialize = const _ForwardingConverter, path.Context>(_decode); @override - Converter> get encoder => kEncoder; + Converter> get encoder => serialize; @override - Converter, path.Context> get decoder => kDecoder; + Converter, path.Context> get decoder => deserialize; } class _ResultEncoder extends Converter, Object> { @@ -301,8 +246,10 @@ class _ReplayAwareEncoder extends Converter { String convert(ReplayAware input) => input.identifier; } -class _EncodingCodec extends Codec { - const _EncodingCodec(); +/// An [EncodingCodec] serializes and deserializes [Encoding] instances. +class EncodingCodec extends Codec { + /// Creates a new [EncodingCodec]. + const EncodingCodec(); static String _encode(Encoding input) => input.name; @@ -315,17 +262,19 @@ class _EncodingCodec extends Codec { return null; } - static const Converter kEncoder = + /// Converter that serializes [Encoding] instances. + static const Converter serialize = const _ForwardingConverter(_encode); - static const Converter kDecoder = + /// Converter that deserializes [Encoding] instances. + static const Converter deserialize = const _ForwardingConverter(_decode); @override - Converter get encoder => kEncoder; + Converter get encoder => serialize; @override - Converter get decoder => kDecoder; + Converter get decoder => deserialize; } class _FileModeEncoder extends Converter { @@ -349,15 +298,17 @@ class _FileModeEncoder extends Converter { } } -class _FileStatCodec extends Codec> { - const _FileStatCodec(); +/// An [FileStatCodec] serializes and deserializes [FileStat] instances. +class FileStatCodec extends Codec> { + /// Creates a new [FileStatCodec]. + const FileStatCodec(); static Map _encode(FileStat input) { return { - '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 @@ class _FileStatCodec extends Codec> { static FileStat _decode(Map input) => new ReplayFileStat(input); - static const Converter> kEncoder = + /// Converter that serializes [FileStat] instances. + static const Converter> serialize = const _ForwardingConverter>(_encode); - static const Converter, FileStat> kDecoder = + /// Converter that deserializes [FileStat] instances. + static const Converter, FileStat> deserialize = const _ForwardingConverter, FileStat>(_decode); @override - Converter> get encoder => kEncoder; + Converter> get encoder => serialize; @override - Converter, FileStat> get decoder => kDecoder; + Converter, FileStat> get decoder => deserialize; } -class _EntityTypeCodec extends Codec { - const _EntityTypeCodec(); +/// An [EntityTypeCodec] serializes and deserializes [FileSystemEntity] +/// instances. +class EntityTypeCodec extends Codec { + /// Creates a new [EntityTypeCodec]. + const EntityTypeCodec(); static String _encode(FileSystemEntityType input) => input.toString(); @@ -394,22 +350,26 @@ class _EntityTypeCodec extends Codec { }[input]; } - static const Converter kEncoder = + /// Converter that serializes [FileSystemEntityType] instances. + static const Converter serialize = const _ForwardingConverter(_encode); - static const Converter kDecoder = + /// Converter that deserializes [FileSystemEntityType] instances. + static const Converter deserialize = const _ForwardingConverter(_decode); @override - Converter get encoder => kEncoder; + Converter get encoder => serialize; @override - Converter get decoder => kDecoder; + Converter get decoder => deserialize; } -class _FileSystemEventCodec - extends Codec> { - const _FileSystemEventCodec(); +/// A [FileSystemEventCodec] serializes and deserializes [FileSystemEvent] +/// instances. +class FileSystemEventCodec extends Codec> { + /// Creates a new [FileSystemEventCodec]. + const FileSystemEventCodec(); static Map _encode(FileSystemEvent input) { return { @@ -422,17 +382,19 @@ class _FileSystemEventCodec static FileSystemEvent _decode(Map input) => new _FileSystemEvent(input); - static const Converter> kEncoder = + /// Converter that serializes [FileSystemEvent] instances. + static const Converter> serialize = const _ForwardingConverter>(_encode); - static const Converter, FileSystemEvent> kDecoder = + /// Converter that deserializes [FileSystemEvent] instances. + static const Converter, FileSystemEvent> deserialize = const _ForwardingConverter, FileSystemEvent>(_decode); @override - Converter> get encoder => kEncoder; + Converter> get encoder => serialize; @override - Converter, FileSystemEvent> get decoder => kDecoder; + Converter, FileSystemEvent> get decoder => deserialize; } class _FileSystemEvent implements FileSystemEvent { @@ -450,100 +412,143 @@ class _FileSystemEvent implements FileSystemEvent { bool get isDirectory => _data['isDirectory']; } -class _FutureReviver extends Converter> { - const _FutureReviver(); +/// Converts an object into a [Future] that completes with that object. +class ToFuture extends Converter> { + /// Creates a new [ToFuture]. + const ToFuture(); @override Future convert(T input) async => input; } -class _DirectoryReviver extends Converter { - final ReplayFileSystemImpl fileSystem; - const _DirectoryReviver(this.fileSystem); +/// Converts an object into a single-element [List] containing that object. +class Listify extends Converter> { + /// Creates a new [Listify]. + const Listify(); @override - Directory convert(String input) => new ReplayDirectory(fileSystem, input); + List convert(T input) => [input]; } -class _FileReviver extends Converter { - final ReplayFileSystemImpl fileSystem; - const _FileReviver(this.fileSystem); +/// Revives a [Directory] entity reference into a [ReplayDirectory]. +class ReviveDirectory extends Converter { + 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 { - final ReplayFileSystemImpl fileSystem; - const _LinkReviver(this.fileSystem); +/// Revives a [File] entity reference into a [ReplayFile]. +class ReviveFile extends Converter { + 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 { - final ReplayFileSystemImpl fileSystem; - const _FileSystemEntityReviver(this.fileSystem); +/// Revives a [Link] entity reference into a [ReplayLink]. +class ReviveLink extends Converter { + 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 { + 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 { - final ReplayFileSystemImpl fileSystem; - const _RandomAccessFileReviver(this.fileSystem); +/// Revives a [RandomAccessFile] entity reference into a +/// [ReplayRandomAccessFile]. +class ReviveRandomAccessFile extends Converter { + 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 { - final ReplayFileSystemImpl fileSystem; - const _IOSinkReviver(this.fileSystem); +/// Revives an [IOSink] entity reference into a [ReplayIOSink]. +class ReviveIOSink extends Converter { + 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, List> { - final Converter elementReviver; +/// Converts all elements of a [List], returning a new [List] of converted +/// elements. +class ConvertElements extends Converter, List> { + final Converter _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 elementConverter) + : _delegate = elementConverter; @override - List convert(Iterable input) => - input.map(elementReviver.convert).toList(); + List convert(List input) => input.map(_delegate.convert).toList(); } -class _StreamReviver extends Converter, Stream> { - const _StreamReviver(); +/// Converts a [List] of elements into a [Stream] of the same elements. +class ToStream extends Converter, Stream> { + /// Creates a new [ToStream]. + const ToStream(); @override - Stream convert(List input) { - return new Stream.fromIterable(input); - } + Stream convert(List input) => new Stream.fromIterable(input); } -class _BlobReviver extends Converter> { - final ReplayFileSystemImpl fileSystem; - const _BlobReviver(this.fileSystem); +/// Converts a blob reference (serialized as a [String] of the form +/// `!`) into a byte list. +class BlobToBytes extends Converter> { + 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 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 @@ class ReplayDirectory extends ReplayFileSystemEntity implements Directory { /// Creates a new `ReplayDirectory`. ReplayDirectory(ReplayFileSystemImpl fileSystem, String identifier) : super(fileSystem, identifier) { - Converter convertThis = directoryReviver(fileSystem); - Converter convertFutureThis = - convertThis.fuse(kFutureReviver); + Converter reviveDirectory = + new ReviveDirectory(fileSystem); + Converter> reviveFutureDirectory = + reviveDirectory.fuse(const ToFuture()); + Converter reviveEntity = + new ReviveFileSystemEntity(fileSystem); + Converter, List> reviveEntities = + new ConvertElements(reviveEntity); methods.addAll(>{ - #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(), + #createTemp: reviveFutureDirectory, + #createTempSync: reviveDirectory, + #list: reviveEntities.fuse(const ToStream()), + #listSync: reviveEntities, }); properties.addAll(>{ - #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 @@ class ReplayFile extends ReplayFileSystemEntity implements File { /// Creates a new `ReplayFile`. ReplayFile(ReplayFileSystemImpl fileSystem, String identifier) : super(fileSystem, identifier) { - Converter convertThis = fileReviver(fileSystem); - Converter convertFutureThis = - convertThis.fuse(kFutureReviver); + Converter reviveFile = new ReviveFile(fileSystem); + Converter> reviveFileAsFuture = + reviveFile.fuse(const ToFuture()); + Converter> blobToBytes = new BlobToBytes(fileSystem); + Converter blobToString = blobToBytes.fuse(UTF8.decoder); + Converter reviveRandomAccessFile = + new ReviveRandomAccessFile(fileSystem); + // TODO(tvolkert) remove `as`: https://github.com/dart-lang/sdk/issues/28748 + Converter> lineSplitter = + const LineSplitter() as Converter>; + Converter> blobToLines = + blobToString.fuse(lineSplitter); + Converter>> blobToByteStream = blobToBytes + .fuse(const Listify>()) + .fuse(const ToStream>()); methods.addAll(>{ - #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(), + #copy: reviveFileAsFuture, + #copySync: reviveFile, + #length: const ToFuture(), + #lengthSync: const Passthrough(), + #lastModified: DateTimeCodec.deserialize.fuse(const ToFuture()), + #lastModifiedSync: DateTimeCodec.deserialize, + #open: reviveRandomAccessFile.fuse(const ToFuture()), + #openSync: reviveRandomAccessFile, + #openRead: blobToByteStream, + #openWrite: new ReviveIOSink(fileSystem), + #readAsBytes: blobToBytes.fuse(const ToFuture>()), + #readAsBytesSync: blobToBytes, + #readAsString: blobToString.fuse(const ToFuture()), + #readAsStringSync: blobToString, + #readAsLines: blobToLines.fuse(const ToFuture>()), + #readAsLinesSync: blobToLines, + #writeAsBytes: reviveFileAsFuture, + #writeAsBytesSync: const Passthrough(), + #writeAsString: reviveFileAsFuture, + #writeAsStringSync: const Passthrough(), }); properties.addAll(>{ - #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 @@ class ReplayFileStat implements FileStat { ReplayFileStat(Map 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 @@ class ReplayFileSystemImpl extends FileSystem implements ReplayFileSystem, ReplayAware { /// Creates a new `ReplayFileSystemImpl`. ReplayFileSystemImpl(this.recording, this.manifest) { + Converter reviveDirectory = new ReviveDirectory(this); + ToFuture toFutureType = + const ToFuture(); + methods.addAll(>{ - #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()), + #statSync: FileStatCodec.deserialize, + #identical: const Passthrough().fuse(const ToFuture()), + #identicalSync: const Passthrough(), + #type: EntityTypeCodec.deserialize.fuse(toFutureType), + #typeSync: EntityTypeCodec.deserialize, }); properties.addAll(>{ - #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(), + #isWatchSupported: const Passthrough(), }); } 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 @@ abstract class ReplayFileSystemEntity extends Object implements FileSystemEntity { /// Creates a new `ReplayFileSystemEntity`. ReplayFileSystemEntity(this.fileSystem, this.identifier) { + Converter>, List> toEvents = + const ConvertElements, FileSystemEvent>( + FileSystemEventCodec.deserialize); + Converter>, Stream> + toEventStream = toEvents.fuse(const ToStream()); + methods.addAll(>{ - #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(), + #existsSync: const Passthrough(), + #resolveSymbolicLinks: const ToFuture(), + #resolveSymbolicLinksSync: const Passthrough(), + #stat: FileStatCodec.deserialize.fuse(const ToFuture()), + #statSync: FileStatCodec.deserialize, + #deleteSync: const Passthrough(), + #watch: toEventStream, }); properties.addAll(>{ - #path: kPassthrough, - #uri: kUriReviver, - #isAbsolute: kPassthrough, - #parent: directoryReviver(fileSystem), - #basename: kPassthrough, - #dirname: kPassthrough, + #path: const Passthrough(), + #uri: UriCodec.deserialize, + #isAbsolute: const Passthrough(), + #parent: new ReviveDirectory(fileSystem), + #basename: const Passthrough(), + #dirname: const Passthrough(), }); } 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 @@ import 'replay_proxy_mixin.dart'; 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(>{ - #add: kPassthrough, - #write: kPassthrough, - #writeAll: kPassthrough, - #writeln: kPassthrough, - #writeCharCode: kPassthrough, - #addError: kPassthrough, - #addStream: kFutureReviver, - #flush: kFutureReviver, - #close: kFutureReviver, + #add: const Passthrough(), + #write: const Passthrough(), + #writeAll: const Passthrough(), + #writeln: const Passthrough(), + #writeCharCode: const Passthrough(), + #addError: const Passthrough(), + #addStream: const ToFuture(), + #flush: const ToFuture(), + #close: const ToFuture(), }); properties.addAll(>{ - #encoding: kEncodingReviver, - const Symbol('encoding='): kPassthrough, - #done: kPassthrough.fuse(kFutureReviver), + #encoding: EncodingCodec.deserialize, + const Symbol('encoding='): const Passthrough(), + #done: const Passthrough().fuse(const ToFuture()), }); } 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 @@ class ReplayLink extends ReplayFileSystemEntity implements Link { /// Creates a new `ReplayLink`. ReplayLink(ReplayFileSystemImpl fileSystem, String identifier) : super(fileSystem, identifier) { + Converter reviveLink = new ReviveLink(fileSystem); + Converter> reviveLinkAsFuture = + reviveLink.fuse(const ToFuture()); + methods.addAll(>{ - #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(), + #update: reviveLinkAsFuture, + #updateSync: const Passthrough(), + #target: const Passthrough().fuse(const ToFuture()), + #targetSync: const Passthrough(), }); properties.addAll(>{ - #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 @@ class ReplayRandomAccessFile extends Object implements RandomAccessFile { final ReplayFileSystemImpl _fileSystem; - /// Creates a new `ReplayIOSink`. + /// Creates a new [ReplayRandomAccessFile]. ReplayRandomAccessFile(this._fileSystem, this.identifier) { - Converter convertFutureThis = - randomAccessFileReviver(_fileSystem).fuse(kFutureReviver); + ToFuture toFuture = const ToFuture(); + Converter> reviveRandomAccessFileAsFuture = + new ReviveRandomAccessFile(_fileSystem).fuse(toFuture); methods.addAll(>{ - #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(), + #readByte: const ToFuture(), + #readByteSync: const Passthrough(), + #read: const ToFuture>(), + #readSync: const Passthrough>(), + #readInto: const ToFuture(), + #readIntoSync: const Passthrough(), + #writeByte: reviveRandomAccessFileAsFuture, + #writeByteSync: const Passthrough(), + #writeFrom: reviveRandomAccessFileAsFuture, + #writeFromSync: const Passthrough(), + #writeString: reviveRandomAccessFileAsFuture, + #writeStringSync: const Passthrough(), + #position: const ToFuture(), + #positionSync: const Passthrough(), + #setPosition: reviveRandomAccessFileAsFuture, + #setPositionSync: const Passthrough(), + #truncate: reviveRandomAccessFileAsFuture, + #truncateSync: const Passthrough(), + #length: const ToFuture(), + #lengthSync: const Passthrough(), + #flush: reviveRandomAccessFileAsFuture, + #flushSync: const Passthrough(), + #lock: reviveRandomAccessFileAsFuture, + #lockSync: const Passthrough(), + #unlock: reviveRandomAccessFileAsFuture, + #unlockSync: const Passthrough(), }); properties.addAll(>{ - #path: kPassthrough, + #path: const Passthrough(), }); } 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 - Yegor Jbanov - Todd Volkert 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 'dart:io' as io; 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 @@ typedef String RootPathGenerator(); /// [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 @@ void runCommonTests( group('common', () { FileSystemGenerator createFs; - List setUps; + List setUps; + List tearDowns; FileSystem fs; String root; @@ -72,7 +74,8 @@ void runCommonTests( testpkg.setUp(() async { createFs = createFileSystem; - setUps = []; + setUps = []; + tearDowns = []; fs = null; root = null; }); @@ -81,6 +84,14 @@ void runCommonTests( 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 @@ void runCommonTests( } 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 @@ void main() { // 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 ],