Skip to content
This repository was archived by the owner on Dec 9, 2024. It is now read-only.

Commit 6a06dce

Browse files
committed
Monotonic clock version of MemoryFileSystem
1 parent c70c123 commit 6a06dce

File tree

4 files changed

+119
-6
lines changed

4 files changed

+119
-6
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright (c) 2018, 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+
/// Interface describing clocks used by the [MemoryFileSystem].
6+
///
7+
/// The [MemoryFileSystem] uses a clock to determine the modification times of
8+
/// files that are created in that file system.
9+
abstract class MemoryFileSystemClock {
10+
/// Abstract const constructor. This constructor enables subclasses to provide
11+
/// const constructors so that they can be used in const expressions.
12+
const MemoryFileSystemClock();
13+
14+
/// A real-time clock.
15+
///
16+
/// Uses [DateTime.now] to reflect the actual time reported by the operating
17+
/// system.
18+
const factory MemoryFileSystemClock.realTime() = _RealtimeMemoryFileSystemClock;
19+
20+
/// A monotonically-increasing test clock.
21+
///
22+
/// Each time [now] is called, the time increases by one minute.
23+
///
24+
/// The `start` argument can be used to set the seed time for the clock.
25+
/// The first value will be that time plus one minute.
26+
factory MemoryFileSystemClock.monotonicTest() = _MonotonicTestMemoryFileSystemClock;
27+
28+
/// Returns the value of the clock.
29+
DateTime get now;
30+
}
31+
32+
class _RealtimeMemoryFileSystemClock extends MemoryFileSystemClock {
33+
const _RealtimeMemoryFileSystemClock();
34+
35+
@override
36+
DateTime get now => DateTime.now();
37+
}
38+
39+
class _MonotonicTestMemoryFileSystemClock extends MemoryFileSystemClock {
40+
_MonotonicTestMemoryFileSystemClock({
41+
DateTime start,
42+
}) : _current = start ?? DateTime(2000);
43+
44+
DateTime _current;
45+
46+
@override
47+
DateTime get now {
48+
_current = _current.add(const Duration(minutes: 1));
49+
return _current;
50+
}
51+
}

packages/file/lib/src/backends/memory/memory_file_system.dart

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ import 'dart:async';
66

77
import 'package:file/file.dart';
88
import 'package:file/src/io.dart' as io;
9+
import 'package:meta/meta.dart';
910
import 'package:path/path.dart' as p;
1011

12+
import 'clock.dart';
1113
import 'common.dart';
1214
import 'memory_directory.dart';
1315
import 'memory_file.dart';
@@ -35,16 +37,44 @@ abstract class MemoryFileSystem implements StyleableFileSystem {
3537
/// The file system will be empty, and the current directory will be the
3638
/// root directory.
3739
///
40+
/// The clock will be a real-time clock, file modification times will
41+
/// reflect the real time as reported by the operating system.
42+
///
43+
/// If [style] is specified, the file system will use the specified path
44+
/// style. The default is [FileSystemStyle.posix].
45+
factory MemoryFileSystem({
46+
FileSystemStyle style = FileSystemStyle.posix,
47+
}) => _MemoryFileSystem(
48+
style: style,
49+
clock: const MemoryFileSystemClock.realTime(),
50+
);
51+
52+
/// Creates a new `MemoryFileSystem` that has a fake clock.
53+
///
54+
/// The file system will be empty, and the current directory will be the
55+
/// root directory.
56+
///
57+
/// The clock will increase monotonically each time it is used, disconnected
58+
/// from any real-world clock.
59+
///
3860
/// If [style] is specified, the file system will use the specified path
3961
/// style. The default is [FileSystemStyle.posix].
40-
factory MemoryFileSystem({FileSystemStyle style}) = _MemoryFileSystem;
62+
factory MemoryFileSystem.test({
63+
FileSystemStyle style = FileSystemStyle.posix,
64+
}) => _MemoryFileSystem(
65+
style: style,
66+
clock: MemoryFileSystemClock.monotonicTest(),
67+
);
4168
}
4269

4370
/// Internal implementation of [MemoryFileSystem].
4471
class _MemoryFileSystem extends FileSystem
4572
implements MemoryFileSystem, NodeBasedFileSystem {
46-
_MemoryFileSystem({this.style = FileSystemStyle.posix})
47-
: assert(style != null) {
73+
_MemoryFileSystem({
74+
this.style = FileSystemStyle.posix,
75+
@required this.clock,
76+
}) : assert(style != null),
77+
assert(clock != null) {
4878
_root = RootNode(this);
4979
_context = style.contextFor(style.root);
5080
}
@@ -53,6 +83,9 @@ class _MemoryFileSystem extends FileSystem
5383
String _systemTemp;
5484
p.Context _context;
5585

86+
@override
87+
final MemoryFileSystemClock clock;
88+
5689
@override
5790
final FileSystemStyle style;
5891

packages/file/lib/src/backends/memory/node.dart

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'dart:typed_data';
77
import 'package:file/file.dart';
88
import 'package:file/src/io.dart' as io;
99

10+
import 'clock.dart';
1011
import 'common.dart';
1112
import 'memory_file_stat.dart';
1213
import 'style.dart';
@@ -46,6 +47,10 @@ abstract class NodeBasedFileSystem implements StyleableFileSystem {
4647
/// The path of the current working directory.
4748
String get cwd;
4849

50+
/// The clock to use when finding the current time (e.g. to set the creation
51+
/// time of a new node).
52+
MemoryFileSystemClock get clock;
53+
4954
/// Gets the backing node of the entity at the specified path. If the tail
5055
/// element of the path does not exist, this will return null. If the tail
5156
/// element cannot be reached because its directory does not exist, a
@@ -142,12 +147,14 @@ abstract class Node {
142147
abstract class RealNode extends Node {
143148
/// Constructs a new [RealNode] as a child of the specified [parent].
144149
RealNode(DirectoryNode parent) : super(parent) {
145-
int now = DateTime.now().millisecondsSinceEpoch;
150+
int now = clock.now.millisecondsSinceEpoch;
146151
changed = now;
147152
modified = now;
148153
accessed = now;
149154
}
150155

156+
MemoryFileSystemClock get clock => parent.clock;
157+
151158
/// Last changed time in milliseconds since the Epoch.
152159
int changed;
153160

@@ -177,7 +184,7 @@ abstract class RealNode extends Node {
177184

178185
/// Updates the last modified time of the node.
179186
void touch() {
180-
modified = DateTime.now().millisecondsSinceEpoch;
187+
modified = clock.now.millisecondsSinceEpoch;
181188
}
182189
}
183190

@@ -210,6 +217,9 @@ class RootNode extends DirectoryNode {
210217
@override
211218
final NodeBasedFileSystem fs;
212219

220+
@override
221+
MemoryFileSystemClock get clock => fs.clock;
222+
213223
@override
214224
DirectoryNode get parent => this;
215225

@@ -253,7 +263,7 @@ class FileNode extends RealNode {
253263
/// fields will be reset as opposed to copied to indicate that this file
254264
/// has been modified and changed.
255265
void copyFrom(FileNode source) {
256-
modified = changed = DateTime.now().millisecondsSinceEpoch;
266+
modified = changed = clock.now.millisecondsSinceEpoch;
257267
accessed = source.accessed;
258268
mode = source.mode;
259269
_content = Uint8List.fromList(source.content);

packages/file/test/memory_test.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,23 @@ void main() {
6767
});
6868
});
6969
});
70+
71+
test('MemoryFileSystem.test', () {
72+
final MemoryFileSystem fs = MemoryFileSystem.test(); // creates root directory
73+
fs.file('/test1.txt').createSync(); // creates file
74+
fs.file('/test2.txt').createSync(); // creates file
75+
expect(fs.directory('/').statSync().modified, DateTime(2000, 1, 1, 0, 1));
76+
expect(fs.file('/test1.txt').statSync().modified, DateTime(2000, 1, 1, 0, 2));
77+
expect(fs.file('/test2.txt').statSync().modified, DateTime(2000, 1, 1, 0, 3));
78+
fs.file('/test1.txt').createSync();
79+
fs.file('/test2.txt').createSync();
80+
expect(fs.file('/test1.txt').statSync().modified, DateTime(2000, 1, 1, 0, 2)); // file already existed
81+
expect(fs.file('/test2.txt').statSync().modified, DateTime(2000, 1, 1, 0, 3)); // file already existed
82+
fs.file('/test1.txt').writeAsStringSync('test'); // touches file
83+
expect(fs.file('/test1.txt').statSync().modified, DateTime(2000, 1, 1, 0, 4));
84+
expect(fs.file('/test2.txt').statSync().modified, DateTime(2000, 1, 1, 0, 3)); // didn't touch it
85+
fs.file('/test1.txt').copySync('/test2.txt'); // creates file, then mutates file (so time changes twice)
86+
expect(fs.file('/test1.txt').statSync().modified, DateTime(2000, 1, 1, 0, 4)); // didn't touch it
87+
expect(fs.file('/test2.txt').statSync().modified, DateTime(2000, 1, 1, 0, 6));
88+
});
7089
}

0 commit comments

Comments
 (0)