Skip to content

Commit 3ba4cf2

Browse files
authored
Implement RecordingFileSystem. (flutter#16)
To be followed by ReplayFileSystem in an upcoming commit. First part of flutter#11
1 parent eda5a8b commit 3ba4cf2

21 files changed

+2049
-78
lines changed

.analysis_options

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,68 @@
11
analyzer:
22
strong-mode: true
33
language:
4-
enableSuperMixins: true
4+
enableStrictCallChecks: true
5+
enableSuperMixins: true
6+
errors:
7+
# Allow having TODOs in the code
8+
todo: ignore
9+
510
linter:
611
rules:
7-
# Errors
12+
# these rules are documented on and in the same order as
13+
# the Dart Lint rules page to make maintenance easier
14+
# http://dart-lang.github.io/linter/lints/
15+
16+
# === error rules ===
817
- avoid_empty_else
18+
# TODO - comment_references
19+
- cancel_subscriptions
20+
# TODO - close_sinks
921
- control_flow_in_finally
1022
- empty_statements
23+
- hash_and_equals
24+
- invariant_booleans
25+
- iterable_contains_unrelated_type
26+
- list_remove_unrelated_type
27+
- literal_only_boolean_expressions
1128
- test_types_in_equals
1229
- throw_in_finally
30+
- unrelated_type_equality_checks
1331
- valid_regexps
1432

15-
# Style
16-
# TODO: - annotate_overrides
33+
# === style rules ===
34+
- always_declare_return_types
35+
# TODO - always_specify_types
36+
# TODO - annotate_overrides
37+
# TODO - avoid_as
1738
- avoid_init_to_null
1839
- avoid_return_types_on_setters
1940
- await_only_futures
20-
- camel_case_types
21-
# TODO: - comment_references
41+
# TODO - camel_case_types
42+
# TODO - constant_identifier_names
43+
- control_flow_in_finally
2244
- empty_catches
2345
- empty_constructor_bodies
24-
- hash_and_equals
46+
- implementation_imports
47+
- library_names
2548
- library_prefixes
2649
- non_constant_identifier_names
50+
- one_member_abstracts
51+
- only_throw_errors
52+
- overridden_fields
53+
- package_api_docs
54+
- package_prefixed_library_names
2755
- prefer_is_not_empty
56+
# TODO - public_member_api_docs
2857
- slash_for_doc_comments
58+
- sort_constructors_first
59+
# TODO - sort_unnamed_constructors_first
60+
- super_goes_last
61+
# TODO - type_annotate_public_apis
2962
- type_init_formals
30-
- unrelated_type_equality_checks
63+
# TODO - unawaited_futures
64+
- unnecessary_brace_in_string_interp
65+
- unnecessary_getters_setters
66+
67+
# === pub rules ===
68+
- package_names

CHANGELOG.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
* Improved `toString` implementations in file system entity classes
44
* Added `ForwardingFileSystem` and associated forwarding classes to the
5-
main `file` library.
5+
main `file` library
66
* Removed `FileSystem.pathSeparator`, and added a more comprehensive
7-
`FileSystem.path` property.
7+
`FileSystem.path` property
8+
* Added `FileSystemEntity.basename` and `FileSystemEntity.dirname`
9+
* Added the `record_replay` library
10+
* Added the `testing` library
811

912
#### 1.0.1
1013

lib/record_replay.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
export 'src/backends/record_replay/events.dart'
6+
show InvocationEvent, PropertyGetEvent, PropertySetEvent, MethodEvent;
7+
export 'src/backends/record_replay/recording.dart';
8+
export 'src/backends/record_replay/recording_file_system.dart'
9+
show RecordingFileSystem;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
/// Encoded value of the file system in a recording.
6+
const String kFileSystemEncodedValue = '__fs__';
7+
8+
/// The name of the recording manifest file.
9+
const String kManifestName = 'MANIFEST.txt';
10+
11+
/// Gets an id guaranteed to be unique on this isolate for objects within this
12+
/// library.
13+
int newUid() => _nextUid++;
14+
int _nextUid = 1;
15+
16+
/// Gets the name of the specified [symbol].
17+
// TODO(tvolkert): Symbol.name (https://github.com/dart-lang/sdk/issues/28372)
18+
String getSymbolName(Symbol symbol) {
19+
// Format of `str` is `Symbol("<name>")`
20+
String str = symbol.toString();
21+
int offset = str.indexOf('"') + 1;
22+
return str.substring(offset, str.indexOf('"', offset));
23+
}
Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
// Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:convert';
6+
7+
import 'package:file/file.dart';
8+
import 'package:path/path.dart' as p;
9+
10+
import 'common.dart';
11+
import 'events.dart';
12+
import 'recording_directory.dart';
13+
import 'recording_file.dart';
14+
import 'recording_file_system_entity.dart';
15+
import 'recording_io_sink.dart';
16+
import 'recording_link.dart';
17+
import 'recording_random_access_file.dart';
18+
19+
/// Encodes an object into a JSON-ready representation.
20+
typedef dynamic _Encoder(dynamic object);
21+
22+
/// This class is a work-around for the "is" operator not accepting a variable
23+
/// value as its right operand (https://github.com/dart-lang/sdk/issues/27680).
24+
class _TypeMatcher<T> {
25+
/// Creates a type matcher for the given type parameter.
26+
const _TypeMatcher();
27+
28+
/// Returns `true` if the given object is of type `T`.
29+
bool check(dynamic object) => object is T;
30+
}
31+
32+
/// Known encoders. Types not covered here will be encoded using
33+
/// [_encodeDefault].
34+
///
35+
/// When encoding an object, we will walk this map in iteration order looking
36+
/// for a matching encoder. Thus, when there are two encoders that match an
37+
// object, the first one will win.
38+
const Map<_TypeMatcher, _Encoder> _kEncoders = const <_TypeMatcher, _Encoder>{
39+
const _TypeMatcher<num>(): _encodeRaw,
40+
const _TypeMatcher<bool>(): _encodeRaw,
41+
const _TypeMatcher<String>(): _encodeRaw,
42+
const _TypeMatcher<Null>(): _encodeRaw,
43+
const _TypeMatcher<List>(): _encodeRaw,
44+
const _TypeMatcher<Map>(): _encodeMap,
45+
const _TypeMatcher<Iterable>(): _encodeIterable,
46+
const _TypeMatcher<Symbol>(): getSymbolName,
47+
const _TypeMatcher<DateTime>(): _encodeDateTime,
48+
const _TypeMatcher<Uri>(): _encodeUri,
49+
const _TypeMatcher<p.Context>(): _encodePathContext,
50+
const _TypeMatcher<EventImpl>(): _encodeEvent,
51+
const _TypeMatcher<FileSystem>(): _encodeFileSystem,
52+
const _TypeMatcher<RecordingDirectory>(): _encodeFileSystemEntity,
53+
const _TypeMatcher<RecordingFile>(): _encodeFileSystemEntity,
54+
const _TypeMatcher<RecordingLink>(): _encodeFileSystemEntity,
55+
const _TypeMatcher<RecordingIOSink>(): _encodeIOSink,
56+
const _TypeMatcher<RecordingRandomAccessFile>(): _encodeRandomAccessFile,
57+
const _TypeMatcher<Encoding>(): _encodeEncoding,
58+
const _TypeMatcher<FileMode>(): _encodeFileMode,
59+
const _TypeMatcher<FileStat>(): _encodeFileStat,
60+
const _TypeMatcher<FileSystemEntityType>(): _encodeFileSystemEntityType,
61+
const _TypeMatcher<FileSystemEvent>(): _encodeFileSystemEvent,
62+
};
63+
64+
/// Encodes [object] into a JSON-ready representation.
65+
///
66+
/// This function is intended to be used as the `toEncodable` argument to the
67+
/// `JsonEncoder` constructors.
68+
///
69+
/// See also:
70+
/// - [JsonEncoder.withIndent]
71+
dynamic encode(dynamic object) {
72+
_Encoder encoder = _encodeDefault;
73+
for (_TypeMatcher matcher in _kEncoders.keys) {
74+
if (matcher.check(object)) {
75+
encoder = _kEncoders[matcher];
76+
break;
77+
}
78+
}
79+
return encoder(object);
80+
}
81+
82+
/// Default encoder (used for types not covered in [_kEncoders]).
83+
String _encodeDefault(dynamic object) => object.runtimeType.toString();
84+
85+
/// Pass-through encoder.
86+
dynamic _encodeRaw(dynamic object) => object;
87+
88+
List<T> _encodeIterable<T>(Iterable<T> iterable) => iterable.toList();
89+
90+
/// Encodes the map keys, and passes the values through.
91+
///
92+
/// As [JsonEncoder] encodes an object graph, it will repeatedly call
93+
/// `toEncodable` to encode unknown types, so any values in a map that need
94+
/// special encoding will already be handled by `JsonEncoder`. However, the
95+
/// encoder won't try to encode map *keys* by default, which is why we encode
96+
/// them here.
97+
Map<String, T> _encodeMap<T>(Map<dynamic, T> map) {
98+
Map<String, T> encoded = <String, T>{};
99+
for (dynamic key in map.keys) {
100+
String encodedKey = encode(key);
101+
encoded[encodedKey] = map[key];
102+
}
103+
return encoded;
104+
}
105+
106+
int _encodeDateTime(DateTime dateTime) => dateTime.millisecondsSinceEpoch;
107+
108+
String _encodeUri(Uri uri) => uri.toString();
109+
110+
Map<String, String> _encodePathContext(p.Context context) => <String, String>{
111+
'style': context.style.name,
112+
'cwd': context.current,
113+
};
114+
115+
Map<String, dynamic> _encodeEvent(EventImpl event) => event.encode();
116+
117+
String _encodeFileSystem(FileSystem fs) => kFileSystemEncodedValue;
118+
119+
/// Encodes a file system entity by using its `uid` as a reference identifier.
120+
/// During replay, this allows us to tie the return value of of one event to
121+
/// the object of another.
122+
String _encodeFileSystemEntity(RecordingFileSystemEntity entity) {
123+
return '${entity.runtimeType}@${entity.uid}';
124+
}
125+
126+
String _encodeIOSink(RecordingIOSink sink) {
127+
return '${sink.runtimeType}@${sink.uid}';
128+
}
129+
130+
String _encodeRandomAccessFile(RecordingRandomAccessFile raf) {
131+
return '${raf.runtimeType}@${raf.uid}';
132+
}
133+
134+
String _encodeEncoding(Encoding encoding) => encoding.name;
135+
136+
String _encodeFileMode(FileMode fileMode) {
137+
switch (fileMode) {
138+
case FileMode.READ:
139+
return 'READ';
140+
case FileMode.WRITE:
141+
return 'WRITE';
142+
case FileMode.APPEND:
143+
return 'APPEND';
144+
case FileMode.WRITE_ONLY:
145+
return 'WRITE_ONLY';
146+
case FileMode.WRITE_ONLY_APPEND:
147+
return 'WRITE_ONLY_APPEND';
148+
}
149+
throw new ArgumentError('Invalid value: $fileMode');
150+
}
151+
152+
Map<String, dynamic> _encodeFileStat(FileStat stat) => <String, dynamic>{
153+
'changed': stat.changed,
154+
'modified': stat.modified,
155+
'accessed': stat.accessed,
156+
'type': stat.type,
157+
'mode': stat.mode,
158+
'size': stat.size,
159+
'modeString': stat.modeString(),
160+
};
161+
162+
String _encodeFileSystemEntityType(FileSystemEntityType type) =>
163+
type.toString();
164+
165+
Map<String, dynamic> _encodeFileSystemEvent(FileSystemEvent event) =>
166+
<String, dynamic>{
167+
'type': event.type,
168+
'path': event.path,
169+
};

0 commit comments

Comments
 (0)