Skip to content

Commit 7ecbd63

Browse files
committed
Adjust times in MockFileSystem
Also apply time in MockFileStream Mark MockFile_AfterSetAccessControl_ShouldUpdateLastAccessTime as Windows-Only
1 parent ad875d7 commit 7ecbd63

12 files changed

+502
-17
lines changed

src/System.IO.Abstractions.TestingHelpers/IMockFileDataAccessor.cs

+8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ namespace System.IO.Abstractions.TestingHelpers
88
/// </summary>
99
public interface IMockFileDataAccessor : IFileSystem
1010
{
11+
/// <summary>
12+
/// Adjust the times of the <paramref name="fileData"/>.
13+
/// </summary>
14+
/// <param name="fileData">The <see cref="MockFileData"/> for which the times should be adjusted.</param>
15+
/// <param name="timeAdjustments">The adjustments to make on the <see cref="MockFileData"/>.</param>
16+
/// <returns>The adjusted file.</returns>
17+
MockFileData AdjustTimes(MockFileData fileData, TimeAdjustments timeAdjustments);
18+
1119
/// <summary>
1220
/// Gets a file.
1321
/// </summary>

src/System.IO.Abstractions.TestingHelpers/MockFile.cs

+29-13
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,13 @@ public override void AppendAllText(string path, string contents, Encoding encodi
5757
if (!mockFileDataAccessor.FileExists(path))
5858
{
5959
VerifyDirectoryExists(path);
60-
mockFileDataAccessor.AddFile(path, new MockFileData(contents, encoding));
60+
mockFileDataAccessor.AddFile(path, mockFileDataAccessor.AdjustTimes(new MockFileData(contents, encoding), TimeAdjustments.All));
6161
}
6262
else
6363
{
6464
var file = mockFileDataAccessor.GetFile(path);
6565
file.CheckFileAccess(path, FileAccess.Write);
66+
mockFileDataAccessor.AdjustTimes(file, TimeAdjustments.LastAccessTime | TimeAdjustments.LastWriteTime);
6667
var bytesToAppend = encoding.GetBytes(contents);
6768
file.Contents = file.Contents.Concat(bytesToAppend).ToArray();
6869
}
@@ -126,7 +127,7 @@ public override void Copy(string sourceFileName, string destFileName, bool overw
126127
var sourceFileData = mockFileDataAccessor.GetFile(sourceFileName);
127128
sourceFileData.CheckFileAccess(sourceFileName, FileAccess.Read);
128129
var destFileData = new MockFileData(sourceFileData);
129-
destFileData.CreationTime = destFileData.LastAccessTime = DateTime.Now;
130+
mockFileDataAccessor.AdjustTimes(destFileData, TimeAdjustments.CreationTime | TimeAdjustments.LastAccessTime);
130131
mockFileDataAccessor.AddFile(destFileName, destFileData);
131132
}
132133

@@ -153,6 +154,7 @@ private Stream CreateInternal(string path, FileAccess access, FileOptions option
153154
VerifyDirectoryExists(path);
154155

155156
var mockFileData = new MockFileData(new byte[0]);
157+
mockFileDataAccessor.AdjustTimes(mockFileData, TimeAdjustments.All);
156158
mockFileDataAccessor.AddFile(path, mockFileData);
157159
return OpenInternal(path, FileMode.Open, access, options);
158160
}
@@ -396,8 +398,8 @@ public override void Move(string sourceFileName, string destFileName)
396398
throw CommonExceptions.ProcessCannotAccessFileInUse();
397399
}
398400
VerifyDirectoryExists(destFileName);
399-
400-
mockFileDataAccessor.AddFile(destFileName, new MockFileData(sourceFile));
401+
402+
mockFileDataAccessor.AddFile(destFileName, mockFileDataAccessor.AdjustTimes(new MockFileData(sourceFile), TimeAdjustments.LastAccessTime));
401403
mockFileDataAccessor.RemoveFile(sourceFileName);
402404
}
403405

@@ -443,7 +445,7 @@ public override void Move(string sourceFileName, string destFileName, bool overw
443445
}
444446
VerifyDirectoryExists(destFileName);
445447

446-
mockFileDataAccessor.AddFile(destFileName, new MockFileData(sourceFile));
448+
mockFileDataAccessor.AddFile(destFileName, mockFileDataAccessor.AdjustTimes(new MockFileData(sourceFile), TimeAdjustments.LastAccessTime));
447449
mockFileDataAccessor.RemoveFile(sourceFileName);
448450
}
449451
#endif
@@ -501,6 +503,12 @@ private Stream OpenInternal(
501503

502504
var mockFileData = mockFileDataAccessor.GetFile(path);
503505
mockFileData.CheckFileAccess(path, access);
506+
var timeAdjustments = TimeAdjustments.LastAccessTime;
507+
if (access != FileAccess.Read)
508+
{
509+
timeAdjustments |= TimeAdjustments.LastWriteTime;
510+
}
511+
mockFileDataAccessor.AdjustTimes(mockFileData, timeAdjustments);
504512

505513
return new MockFileStream(mockFileDataAccessor, path, mode, access, options);
506514
}
@@ -540,7 +548,9 @@ public override byte[] ReadAllBytes(string path)
540548
throw CommonExceptions.FileNotFound(path);
541549
}
542550
mockFileDataAccessor.GetFile(path).CheckFileAccess(path, FileAccess.Read);
543-
return mockFileDataAccessor.GetFile(path).Contents.ToArray();
551+
var fileData = mockFileDataAccessor.GetFile(path);
552+
mockFileDataAccessor.AdjustTimes(fileData, TimeAdjustments.LastAccessTime);
553+
return fileData.Contents.ToArray();
544554
}
545555

546556
/// <inheritdoc />
@@ -552,10 +562,11 @@ public override string[] ReadAllLines(string path)
552562
{
553563
throw CommonExceptions.FileNotFound(path);
554564
}
555-
mockFileDataAccessor.GetFile(path).CheckFileAccess(path, FileAccess.Read);
565+
var fileData = mockFileDataAccessor.GetFile(path);
566+
fileData.CheckFileAccess(path, FileAccess.Read);
567+
mockFileDataAccessor.AdjustTimes(fileData, TimeAdjustments.LastAccessTime);
556568

557-
return mockFileDataAccessor
558-
.GetFile(path)
569+
return fileData
559570
.TextContents
560571
.SplitLines();
561572
}
@@ -575,9 +586,11 @@ public override string[] ReadAllLines(string path, Encoding encoding)
575586
throw CommonExceptions.FileNotFound(path);
576587
}
577588

578-
mockFileDataAccessor.GetFile(path).CheckFileAccess(path, FileAccess.Read);
589+
var fileData = mockFileDataAccessor.GetFile(path);
590+
fileData.CheckFileAccess(path, FileAccess.Read);
591+
mockFileDataAccessor.AdjustTimes(fileData, TimeAdjustments.LastAccessTime);
579592

580-
using (var ms = new MemoryStream(mockFileDataAccessor.GetFile(path).Contents))
593+
using (var ms = new MemoryStream(fileData.Contents))
581594
using (var sr = new StreamReader(ms, encoding))
582595
{
583596
return sr.ReadToEnd().SplitLines();
@@ -675,6 +688,7 @@ public override void SetAccessControl(string path, FileSecurity fileSecurity)
675688
}
676689

677690
var fileData = mockFileDataAccessor.GetFile(path);
691+
mockFileDataAccessor.AdjustTimes(fileData, TimeAdjustments.LastAccessTime);
678692
fileData.AccessControl = fileSecurity;
679693
}
680694

@@ -698,6 +712,7 @@ public override void SetAttributes(string path, FileAttributes fileAttributes)
698712
}
699713
else
700714
{
715+
mockFileDataAccessor.AdjustTimes(possibleFileData, TimeAdjustments.LastAccessTime);
701716
possibleFileData.Attributes = fileAttributes;
702717
}
703718
}
@@ -786,7 +801,7 @@ public override void WriteAllBytes(string path, byte[] bytes)
786801
mockFileDataAccessor.PathVerifier.IsLegalAbsoluteOrRelative(path, "path");
787802
VerifyDirectoryExists(path);
788803

789-
mockFileDataAccessor.AddFile(path, new MockFileData(bytes.ToArray()));
804+
mockFileDataAccessor.AddFile(path, mockFileDataAccessor.AdjustTimes(new MockFileData(bytes.ToArray()), TimeAdjustments.All));
790805
}
791806

792807
/// <summary>
@@ -1056,7 +1071,7 @@ public override void WriteAllText(string path, string contents, Encoding encodin
10561071
VerifyDirectoryExists(path);
10571072

10581073
MockFileData data = contents == null ? new MockFileData(new byte[0]) : new MockFileData(contents, encoding);
1059-
mockFileDataAccessor.AddFile(path, data);
1074+
mockFileDataAccessor.AddFile(path, mockFileDataAccessor.AdjustTimes(data, TimeAdjustments.All));
10601075
}
10611076

10621077
internal static string ReadAllBytes(byte[] contents, Encoding encoding)
@@ -1072,6 +1087,7 @@ private string ReadAllTextInternal(string path, Encoding encoding)
10721087
{
10731088
var mockFileData = mockFileDataAccessor.GetFile(path);
10741089
mockFileData.CheckFileAccess(path, FileAccess.Read);
1090+
mockFileDataAccessor.AdjustTimes(mockFileData, TimeAdjustments.LastAccessTime);
10751091
return ReadAllBytes(mockFileData.Contents, encoding);
10761092
}
10771093

src/System.IO.Abstractions.TestingHelpers/MockFileStream.cs

+7-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public MockFileStream(
3434
fileData = mockFileDataAccessor.GetFile(path);
3535
fileData.CheckFileAccess(path, access);
3636

37+
mockFileDataAccessor.AdjustTimes(fileData, TimeAdjustments.LastAccessTime);
3738
var existingContents = fileData.Contents;
3839
var keepExistingContents =
3940
existingContents?.Length > 0 &&
@@ -60,7 +61,8 @@ public MockFileStream(
6061
}
6162

6263
fileData = new MockFileData(new byte[] { });
63-
fileData.CreationTime = fileData.LastWriteTime = fileData.LastAccessTime = DateTime.Now;
64+
mockFileDataAccessor.AdjustTimes(fileData,
65+
TimeAdjustments.CreationTime | TimeAdjustments.LastAccessTime);
6466
mockFileDataAccessor.AddFile(path, fileData);
6567
}
6668

@@ -76,14 +78,16 @@ public MockFileStream(
7678
/// <inheritdoc />
7779
public override int Read(byte[] buffer, int offset, int count)
7880
{
79-
fileData.LastAccessTime = DateTime.Now;
81+
mockFileDataAccessor.AdjustTimes(fileData,
82+
TimeAdjustments.LastAccessTime);
8083
return base.Read(buffer, offset, count);
8184
}
8285

8386
/// <inheritdoc />
8487
public override void Write(byte[] buffer, int offset, int count)
8588
{
86-
fileData.LastWriteTime = fileData.LastAccessTime = DateTime.Now;
89+
mockFileDataAccessor.AdjustTimes(fileData,
90+
TimeAdjustments.LastAccessTime | TimeAdjustments.LastWriteTime);
8791
base.Write(buffer, offset, count);
8892
}
8993

src/System.IO.Abstractions.TestingHelpers/MockFileSystem.cs

+41-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public class MockFileSystem : FileSystemBase, IMockFileDataAccessor
1515

1616
private readonly IDictionary<string, FileSystemEntry> files;
1717
private readonly PathVerifier pathVerifier;
18+
[NonSerialized]
19+
private Func<DateTime> dateTimeProvider = defaultDateTimeProvider;
20+
private static Func<DateTime> defaultDateTimeProvider = () => DateTime.Now;
1821

1922
/// <inheritdoc />
2023
public MockFileSystem() : this(null) { }
@@ -88,6 +91,19 @@ public MockFileSystem(IDictionary<string, MockFileData> files, string currentDir
8891
/// <inheritdoc />
8992
public PathVerifier PathVerifier => pathVerifier;
9093

94+
/// <summary>
95+
/// Replaces the time provider with a mocked instance. This allows to influence the used time in tests.
96+
/// <para />
97+
/// If not set, the default implementation returns <see cref="DateTime.Now"/>.
98+
/// </summary>
99+
/// <param name="dateTimeProvider">The function that returns the current <see cref="DateTime"/>.</param>
100+
/// <returns></returns>
101+
public MockFileSystem MockTime(Func<DateTime> dateTimeProvider)
102+
{
103+
this.dateTimeProvider = dateTimeProvider ?? defaultDateTimeProvider;
104+
return this;
105+
}
106+
91107
private string FixPath(string path, bool checkCaps = false)
92108
{
93109
if (path == null)
@@ -124,6 +140,28 @@ private string GetPathWithCorrectDirectoryCapitalization(string fullPath)
124140
return fullPath.TrimSlashes();
125141
}
126142

143+
/// <inheritdoc />
144+
public MockFileData AdjustTimes(MockFileData fileData, TimeAdjustments timeAdjustments)
145+
{
146+
var now = (dateTimeProvider ?? defaultDateTimeProvider)();
147+
if ((timeAdjustments & TimeAdjustments.CreationTime) != TimeAdjustments.None)
148+
{
149+
fileData.CreationTime = now;
150+
}
151+
152+
if ((timeAdjustments & TimeAdjustments.LastAccessTime) != TimeAdjustments.None)
153+
{
154+
fileData.LastAccessTime = now;
155+
}
156+
157+
if ((timeAdjustments & TimeAdjustments.LastWriteTime) != TimeAdjustments.None)
158+
{
159+
fileData.LastWriteTime = now;
160+
}
161+
162+
return fileData;
163+
}
164+
127165
/// <inheritdoc />
128166
public MockFileData GetFile(string path)
129167
{
@@ -143,6 +181,7 @@ public void AddFile(string path, MockFileData mockFile)
143181
var fixedPath = FixPath(path, true);
144182
lock (files)
145183
{
184+
mockFile ??= new MockFileData(string.Empty);
146185
var file = GetFile(fixedPath);
147186

148187
if (file != null)
@@ -155,6 +194,7 @@ public void AddFile(string path, MockFileData mockFile)
155194
throw CommonExceptions.AccessDenied(path);
156195
}
157196
file.CheckFileAccess(fixedPath, FileAccess.Write);
197+
mockFile.CreationTime = file.CreationTime;
158198
}
159199

160200
var directoryPath = Path.GetDirectoryName(fixedPath);
@@ -164,7 +204,7 @@ public void AddFile(string path, MockFileData mockFile)
164204
AddDirectory(directoryPath);
165205
}
166206

167-
SetEntry(fixedPath, mockFile ?? new MockFileData(string.Empty));
207+
SetEntry(fixedPath, mockFile);
168208
}
169209
}
170210

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
namespace System.IO.Abstractions.TestingHelpers
2+
{
3+
/// <summary>
4+
/// Flags indicating which times to adjust for a <see cref="MockFileData"/>.
5+
/// </summary>
6+
[Flags]
7+
public enum TimeAdjustments
8+
{
9+
/// <summary>
10+
/// Adjusts no times on the <see cref="MockFileData"/>
11+
/// </summary>
12+
None = 0,
13+
/// <summary>
14+
/// Adjusts the <see cref="MockFileData.CreationTime"/>
15+
/// </summary>
16+
CreationTime = 1 << 0,
17+
/// <summary>
18+
/// Adjusts the <see cref="MockFileData.LastAccessTime"/>
19+
/// </summary>
20+
LastAccessTime = 1 << 1,
21+
/// <summary>
22+
/// Adjusts the <see cref="MockFileData.LastWriteTime"/>
23+
/// </summary>
24+
LastWriteTime = 1 << 2,
25+
/// <summary>
26+
/// Adjusts all times on the <see cref="MockFileData"/>
27+
/// </summary>
28+
All = ~0
29+
}
30+
}

0 commit comments

Comments
 (0)