Clean up a few things in the record_replay library: (#22)
- use closures rather than subclasses to reduce some code in recording_file.dart
- `basename` and `dirname` were missing from `RecordingFileSystemEntity`
- `RecordingFileSystemEntity` didn't need to use two parameterized types.
That was an artifact of it having forked from `ForwardingFileSystemEntity`
- Change the name pattern of the blob files to use the basename of the
`RecordingFile` rather than the name of method being recorded
diff --git a/lib/src/backends/record_replay/encoding.dart b/lib/src/backends/record_replay/encoding.dart
index 1f5037b..412b63e 100644
--- a/lib/src/backends/record_replay/encoding.dart
+++ b/lib/src/backends/record_replay/encoding.dart
@@ -5,7 +5,6 @@
import 'dart:convert';
import 'package:file/file.dart';
-import 'package:file/src/io.dart' as io;
import 'package:path/path.dart' as p;
import 'common.dart';
@@ -118,7 +117,7 @@
/// During replay, this allows us to tie the return value of of one event to
/// the object of another.
String _encodeFileSystemEntity(
- RecordingFileSystemEntity<FileSystemEntity, io.FileSystemEntity> entity) {
+ RecordingFileSystemEntity<FileSystemEntity> entity) {
return '${entity.runtimeType}@${entity.uid}';
}
diff --git a/lib/src/backends/record_replay/recording_directory.dart b/lib/src/backends/record_replay/recording_directory.dart
index 0686141..52334b1 100644
--- a/lib/src/backends/record_replay/recording_directory.dart
+++ b/lib/src/backends/record_replay/recording_directory.dart
@@ -12,8 +12,7 @@
/// [Directory] implementation that records all invocation activity to its file
/// system's recording.
-class RecordingDirectory
- extends RecordingFileSystemEntity<Directory, io.Directory>
+class RecordingDirectory extends RecordingFileSystemEntity<Directory>
implements Directory {
/// Creates a new `RecordingDirectory`.
RecordingDirectory(RecordingFileSystem fileSystem, io.Directory delegate)
@@ -29,7 +28,7 @@
}
@override
- Directory wrap(io.Directory delegate) =>
+ Directory wrap(Directory delegate) =>
super.wrap(delegate) ?? wrapDirectory(delegate);
Future<Directory> _create({bool recursive: false}) =>
diff --git a/lib/src/backends/record_replay/recording_file.dart b/lib/src/backends/record_replay/recording_file.dart
index 4eeeeae..51b447a 100644
--- a/lib/src/backends/record_replay/recording_file.dart
+++ b/lib/src/backends/record_replay/recording_file.dart
@@ -9,17 +9,36 @@
import 'package:file/file.dart';
import 'package:file/src/io.dart' as io;
-import 'mutable_recording.dart';
import 'recording_file_system.dart';
import 'recording_file_system_entity.dart';
import 'recording_io_sink.dart';
import 'recording_random_access_file.dart';
import 'result_reference.dart';
+/// Callback responsible for synchronously writing result [data] to the
+/// specified [file].
+///
+/// See also:
+/// - [_BlobReference]
+typedef void _BlobDataSyncWriter<T>(File file, T data);
+
+/// Callback responsible for asynchronously writing result [data] to the
+/// specified [file].
+///
+/// See also:
+/// - [_BlobFutureReference]
+typedef Future<Null> _BlobDataAsyncWriter<T>(File file, T data);
+
+/// Callback responsible writing streaming result [data] to the specified
+/// [sink].
+///
+/// See also:
+/// - [_BlobStreamReference]
+typedef void _BlobDataStreamWriter<T>(IOSink sink, T data);
+
/// [File] implementation that records all invocation activity to its file
/// system's recording.
-class RecordingFile extends RecordingFileSystemEntity<File, io.File>
- implements File {
+class RecordingFile extends RecordingFileSystemEntity<File> implements File {
/// Creates a new `RecordingFile`.
RecordingFile(RecordingFileSystem fileSystem, io.File delegate)
: super(fileSystem, delegate) {
@@ -50,7 +69,9 @@
}
@override
- File wrap(io.File delegate) => super.wrap(delegate) ?? wrapFile(delegate);
+ File wrap(File delegate) => super.wrap(delegate) ?? wrapFile(delegate);
+
+ File _newRecordingFile() => recording.newFile(delegate.basename);
RandomAccessFile _wrapRandomAccessFile(RandomAccessFile delegate) =>
new RecordingRandomAccessFile(fileSystem, delegate);
@@ -68,12 +89,15 @@
RandomAccessFile _openSync({FileMode mode: FileMode.READ}) =>
_wrapRandomAccessFile(delegate.openSync(mode: mode));
- StreamReference<List<int>> _openRead([int start, int end]) =>
- new _ByteArrayStreamReference(
- recording,
- 'openRead',
- delegate.openRead(start, end),
- );
+ StreamReference<List<int>> _openRead([int start, int end]) {
+ return new _BlobStreamReference<List<int>>(
+ file: _newRecordingFile(),
+ stream: delegate.openRead(start, end),
+ writer: (IOSink sink, List<int> bytes) {
+ sink.add(bytes);
+ },
+ );
+ }
IOSink _openWrite({FileMode mode: FileMode.WRITE, Encoding encoding: UTF8}) {
return new RecordingIOSink(
@@ -82,45 +106,65 @@
);
}
- FutureReference<List<int>> _readAsBytes() => new _ByteArrayFutureReference(
- recording,
- 'readAsBytes',
- delegate.readAsBytes(),
- );
+ FutureReference<List<int>> _readAsBytes() {
+ return new _BlobFutureReference<List<int>>(
+ file: _newRecordingFile(),
+ future: delegate.readAsBytes(),
+ writer: (File file, List<int> bytes) async {
+ await file.writeAsBytes(bytes, flush: true);
+ },
+ );
+ }
- ResultReference<List<int>> _readAsBytesSync() => new _ByteArrayReference(
- recording,
- 'readAsBytesSync',
- delegate.readAsBytesSync(),
- );
+ ResultReference<List<int>> _readAsBytesSync() {
+ return new _BlobReference<List<int>>(
+ file: _newRecordingFile(),
+ value: delegate.readAsBytesSync(),
+ writer: (File file, List<int> bytes) {
+ file.writeAsBytesSync(bytes, flush: true);
+ },
+ );
+ }
- FutureReference<String> _readAsString({Encoding encoding: UTF8}) =>
- new _FileContentFutureReference(
- recording,
- 'readAsString',
- delegate.readAsString(encoding: encoding),
- );
+ FutureReference<String> _readAsString({Encoding encoding: UTF8}) {
+ return new _BlobFutureReference<String>(
+ file: _newRecordingFile(),
+ future: delegate.readAsString(encoding: encoding),
+ writer: (File file, String content) async {
+ await file.writeAsString(content, flush: true);
+ },
+ );
+ }
- ResultReference<String> _readAsStringSync({Encoding encoding: UTF8}) =>
- new _FileContentReference(
- recording,
- 'readAsStringSync',
- delegate.readAsStringSync(encoding: encoding),
- );
+ ResultReference<String> _readAsStringSync({Encoding encoding: UTF8}) {
+ return new _BlobReference<String>(
+ file: _newRecordingFile(),
+ value: delegate.readAsStringSync(encoding: encoding),
+ writer: (File file, String content) {
+ file.writeAsStringSync(content, flush: true);
+ },
+ );
+ }
- FutureReference<List<String>> _readAsLines({Encoding encoding: UTF8}) =>
- new _LinesFutureReference(
- recording,
- 'readAsLines',
- delegate.readAsLines(encoding: encoding),
- );
+ FutureReference<List<String>> _readAsLines({Encoding encoding: UTF8}) {
+ return new _BlobFutureReference<List<String>>(
+ file: _newRecordingFile(),
+ future: delegate.readAsLines(encoding: encoding),
+ writer: (File file, List<String> lines) async {
+ await file.writeAsString(lines.join('\n'), flush: true);
+ },
+ );
+ }
- ResultReference<List<String>> _readAsLinesSync({Encoding encoding: UTF8}) =>
- new _LinesReference(
- recording,
- 'readAsLinesSync',
- delegate.readAsLinesSync(encoding: encoding),
- );
+ ResultReference<List<String>> _readAsLinesSync({Encoding encoding: UTF8}) {
+ return new _BlobReference<List<String>>(
+ file: _newRecordingFile(),
+ value: delegate.readAsLinesSync(encoding: encoding),
+ writer: (File file, List<String> lines) {
+ file.writeAsStringSync(lines.join('\n'), flush: true);
+ },
+ );
+ }
Future<File> _writeAsBytes(
List<int> bytes, {
@@ -140,19 +184,24 @@
.then(wrap);
}
-abstract class _ExternalReference<T> extends ResultReference<T> {
- final File file;
+/// A [ResultReference] that serializes its value data to a separate file.
+class _BlobReference<T> extends ResultReference<T> {
+ final File _file;
final T _value;
+ final _BlobDataSyncWriter<T> _writer;
- _ExternalReference(MutableRecording recording, String name, this._value)
- : file = recording.newFile(name);
-
- @protected
- void writeDataToFile(T value);
+ _BlobReference({
+ @required File file,
+ @required T value,
+ @required _BlobDataSyncWriter<T> writer,
+ })
+ : _file = file,
+ _value = value,
+ _writer = writer;
@override
T get value {
- writeDataToFile(_value);
+ _writer(_file, _value);
return _value;
}
@@ -160,47 +209,58 @@
T get recordedValue => _value;
@override
- dynamic get serializedValue => '!${file.basename}';
+ dynamic get serializedValue => '!${_file.basename}';
}
-abstract class _ExternalFutureReference<T> extends FutureReference<T> {
- final File file;
+/// A [FutureReference] that serializes its value data to a separate file.
+class _BlobFutureReference<T> extends FutureReference<T> {
+ final File _file;
+ final _BlobDataAsyncWriter<T> _writer;
- _ExternalFutureReference(
- MutableRecording recording, String name, Future<T> future)
- : file = recording.newFile(name),
+ _BlobFutureReference({
+ @required File file,
+ @required Future<T> future,
+ @required _BlobDataAsyncWriter<T> writer,
+ })
+ : _file = file,
+ _writer = writer,
super(future);
- @protected
- Future<Null> writeDataToFile(T value);
-
@override
Future<T> get value {
return super.value.then((T value) async {
- await writeDataToFile(value);
+ await _writer(_file, value);
return value;
});
}
@override
- dynamic get serializedValue => '!${file.basename}';
+ dynamic get serializedValue => '!${_file.basename}';
}
-class _ByteArrayStreamReference extends StreamReference<List<int>> {
- final File file;
+/// A [StreamReference] that serializes its value data to a separate file.
+class _BlobStreamReference<T> extends StreamReference<T> {
+ final File _file;
+ final _BlobDataStreamWriter<T> _writer;
IOSink _sink;
- _ByteArrayStreamReference(
- MutableRecording recording, String name, Stream<List<int>> stream)
- : file = recording.newFile(name)..createSync(),
- super(stream);
+ _BlobStreamReference({
+ @required File file,
+ @required Stream<T> stream,
+ @required _BlobDataStreamWriter<T> writer,
+ })
+ : _file = file,
+ _writer = writer,
+ super(stream) {
+ _file.createSync();
+ }
@override
- void onData(List<int> event) {
+ void onData(T event) {
if (_sink == null) {
- _sink = file.openWrite();
+ _sink = _file.openWrite();
}
- _sink.add(event);
+ _writer(_sink, event);
}
@override
@@ -211,73 +271,10 @@
}
@override
- dynamic get serializedValue => '!${file.basename}';
+ dynamic get serializedValue => '!${_file.basename}';
// TODO(tvolkert): remove `.then()` once Dart 1.22 is in stable
@override
Future<Null> get complete =>
Future.wait(<Future<dynamic>>[super.complete, _sink.done]).then((_) {});
}
-
-class _ByteArrayFutureReference extends _ExternalFutureReference<List<int>> {
- _ByteArrayFutureReference(
- MutableRecording recording, String name, Future<List<int>> future)
- : super(recording, name, future);
-
- @override
- Future<Null> writeDataToFile(List<int> bytes) async {
- await file.writeAsBytes(bytes, flush: true);
- }
-}
-
-class _ByteArrayReference extends _ExternalReference<List<int>> {
- _ByteArrayReference(MutableRecording recording, String name, List<int> bytes)
- : super(recording, name, bytes);
-
- @override
- void writeDataToFile(List<int> bytes) {
- file.writeAsBytesSync(bytes, flush: true);
- }
-}
-
-class _FileContentFutureReference extends _ExternalFutureReference<String> {
- _FileContentFutureReference(
- MutableRecording recording, String name, Future<String> future)
- : super(recording, name, future);
-
- @override
- Future<Null> writeDataToFile(String content) async {
- await file.writeAsString(content, flush: true);
- }
-}
-
-class _FileContentReference extends _ExternalReference<String> {
- _FileContentReference(MutableRecording recording, String name, String content)
- : super(recording, name, content);
-
- @override
- void writeDataToFile(String content) {
- file.writeAsStringSync(content, flush: true);
- }
-}
-
-class _LinesFutureReference extends _ExternalFutureReference<List<String>> {
- _LinesFutureReference(
- MutableRecording recording, String name, Future<List<String>> future)
- : super(recording, name, future);
-
- @override
- Future<Null> writeDataToFile(List<String> lines) async {
- await file.writeAsString(lines.join('\n'), flush: true);
- }
-}
-
-class _LinesReference extends _ExternalReference<List<String>> {
- _LinesReference(MutableRecording recording, String name, List<String> lines)
- : super(recording, name, lines);
-
- @override
- void writeDataToFile(List<String> lines) {
- file.writeAsStringSync(lines.join('\n'), flush: true);
- }
-}
diff --git a/lib/src/backends/record_replay/recording_file_system_entity.dart b/lib/src/backends/record_replay/recording_file_system_entity.dart
index e1f695b..91cb515 100644
--- a/lib/src/backends/record_replay/recording_file_system_entity.dart
+++ b/lib/src/backends/record_replay/recording_file_system_entity.dart
@@ -18,10 +18,8 @@
/// [FileSystemEntity] implementation that records all invocation activity to
/// its file system's recording.
-abstract class RecordingFileSystemEntity<T extends FileSystemEntity,
- D extends io.FileSystemEntity> extends Object
- with RecordingProxyMixin
- implements FileSystemEntity {
+abstract class RecordingFileSystemEntity<T extends FileSystemEntity>
+ extends Object with RecordingProxyMixin implements FileSystemEntity {
/// Creates a new `RecordingFileSystemEntity`.
RecordingFileSystemEntity(this.fileSystem, this.delegate) {
methods.addAll(<Symbol, Function>{
@@ -44,6 +42,8 @@
#isAbsolute: () => delegate.isAbsolute,
#absolute: _getAbsolute,
#parent: _getParent,
+ #basename: () => delegate.basename,
+ #dirname: () => delegate.dirname,
});
}
@@ -62,7 +62,7 @@
/// The entity to which this entity delegates its functionality while
/// recording.
@protected
- final D delegate;
+ final T delegate;
/// Returns an entity with the same file system and same type as this
/// entity but backed by the specified delegate.
@@ -74,7 +74,7 @@
/// returns `null`.
@protected
@mustCallSuper
- T wrap(D delegate) => delegate == this.delegate ? this as T : null;
+ T wrap(T delegate) => delegate == this.delegate ? this as T : null;
/// Returns a directory with the same file system as this entity but backed
/// by the specified delegate directory.
@@ -94,15 +94,15 @@
Future<T> _rename(String newPath) => delegate
.rename(newPath)
- .then((io.FileSystemEntity entity) => wrap(entity as D));
+ .then((io.FileSystemEntity entity) => wrap(entity as T));
- T _renameSync(String newPath) => wrap(delegate.renameSync(newPath) as D);
+ T _renameSync(String newPath) => wrap(delegate.renameSync(newPath) as T);
Future<T> _delete({bool recursive: false}) => delegate
.delete(recursive: recursive)
- .then((io.FileSystemEntity entity) => wrap(entity as D));
+ .then((io.FileSystemEntity entity) => wrap(entity as T));
- T _getAbsolute() => wrap(delegate.absolute as D);
+ T _getAbsolute() => wrap(delegate.absolute as T);
Directory _getParent() => wrapDirectory(delegate.parent);
}
diff --git a/lib/src/backends/record_replay/recording_link.dart b/lib/src/backends/record_replay/recording_link.dart
index 0114499..e06612e 100644
--- a/lib/src/backends/record_replay/recording_link.dart
+++ b/lib/src/backends/record_replay/recording_link.dart
@@ -12,8 +12,7 @@
/// [Link] implementation that records all invocation activity to its file
/// system's recording.
-class RecordingLink extends RecordingFileSystemEntity<Link, io.Link>
- implements Link {
+class RecordingLink extends RecordingFileSystemEntity<Link> implements Link {
/// Creates a new `RecordingLink`.
RecordingLink(RecordingFileSystem fileSystem, io.Link delegate)
: super(fileSystem, delegate) {
@@ -28,7 +27,7 @@
}
@override
- Link wrap(io.Link delegate) => super.wrap(delegate) ?? wrapLink(delegate);
+ Link wrap(Link delegate) => super.wrap(delegate) ?? wrapLink(delegate);
Future<Link> _create(String target, {bool recursive: false}) =>
delegate.create(target, recursive: recursive).then(wrap);
diff --git a/test/recording_test.dart b/test/recording_test.dart
index 18a273d..9ed12c7 100644
--- a/test/recording_test.dart
+++ b/test/recording_test.dart
@@ -491,7 +491,7 @@
containsPair('object', matches(r'^RecordingFile@[0-9]+$')),
containsPair('positionalArguments', isEmpty),
containsPair('namedArguments', isEmpty),
- containsPair('result', matches(r'^![0-9]+.openRead$')),
+ containsPair('result', matches(r'^![0-9]+.foo$')),
));
File file = _getRecordingFile(recording, manifest[1]['result']);
expect(file, exists);
@@ -521,7 +521,7 @@
containsPair('object', matches(r'^RecordingFile@[0-9]+$')),
containsPair('positionalArguments', isEmpty),
containsPair('namedArguments', isEmpty),
- containsPair('result', matches(r'^![0-9]+.readAsBytes$')),
+ containsPair('result', matches(r'^![0-9]+.foo$')),
));
File file = _getRecordingFile(recording, manifest[1]['result']);
expect(file, exists);
@@ -549,7 +549,7 @@
containsPair('object', matches(r'^RecordingFile@[0-9]+$')),
containsPair('positionalArguments', isEmpty),
containsPair('namedArguments', isEmpty),
- containsPair('result', matches(r'^![0-9]+.readAsBytesSync$')),
+ containsPair('result', matches(r'^![0-9]+.foo$')),
));
File file = _getRecordingFile(recording, manifest[1]['result']);
expect(file, exists);
@@ -578,7 +578,7 @@
containsPair('method', 'readAsString'),
containsPair('object', matches(r'^RecordingFile@[0-9]+$')),
containsPair('positionalArguments', isEmpty),
- containsPair('result', matches(r'^![0-9]+.readAsString$')),
+ containsPair('result', matches(r'^![0-9]+.foo$')),
containsPair(
'namedArguments',
allOf(
@@ -613,7 +613,7 @@
containsPair('method', 'readAsStringSync'),
containsPair('object', matches(r'^RecordingFile@[0-9]+$')),
containsPair('positionalArguments', isEmpty),
- containsPair('result', matches(r'^![0-9]+.readAsStringSync$')),
+ containsPair('result', matches(r'^![0-9]+.foo$')),
containsPair(
'namedArguments',
allOf(
@@ -646,7 +646,7 @@
containsPair('method', 'readAsLines'),
containsPair('object', matches(r'^RecordingFile@[0-9]+$')),
containsPair('positionalArguments', isEmpty),
- containsPair('result', matches(r'^![0-9]+.readAsLines$')),
+ containsPair('result', matches(r'^![0-9]+.foo$')),
containsPair('namedArguments', isEmpty),
));
File file = _getRecordingFile(recording, manifest[1]['result']);
@@ -674,7 +674,7 @@
containsPair('method', 'readAsLinesSync'),
containsPair('object', matches(r'^RecordingFile@[0-9]+$')),
containsPair('positionalArguments', isEmpty),
- containsPair('result', matches(r'^![0-9]+.readAsLinesSync$')),
+ containsPair('result', matches(r'^![0-9]+.foo$')),
containsPair('namedArguments', isEmpty),
));
File file = _getRecordingFile(recording, manifest[1]['result']);