Skip to content

Commit 5216e0d

Browse files
authored
fix: make Mock{File,Directory}Info cache file attributes (#791)
This makes them behave more similar to the real file system implementation Fixes #661
1 parent f46d7c4 commit 5216e0d

File tree

6 files changed

+183
-76
lines changed

6 files changed

+183
-76
lines changed

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,6 @@ public class MockDirectoryData : MockFileData
1111
[NonSerialized]
1212
private DirectorySecurity accessControl;
1313

14-
/// <inheritdoc />
15-
public override bool IsDirectory { get { return true; } }
16-
1714
/// <inheritdoc />
1815
public MockDirectoryData() : base(string.Empty)
1916
{

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public class MockDirectoryInfo : DirectoryInfoBase
1313
private readonly IMockFileDataAccessor mockFileDataAccessor;
1414
private readonly string directoryPath;
1515
private readonly string originalPath;
16+
private MockFileData cachedMockFileData;
17+
private bool refreshOnNextRead;
1618

1719
/// <summary>
1820
/// Initializes a new instance of the <see cref="MockDirectoryInfo"/> class.
@@ -33,6 +35,7 @@ public MockDirectoryInfo(IMockFileDataAccessor mockFileDataAccessor, string dire
3335
directoryPath = directoryPath.TrimEnd(' ');
3436
}
3537
this.directoryPath = directoryPath;
38+
Refresh();
3639
}
3740

3841
/// <inheritdoc />
@@ -44,7 +47,7 @@ public override void Delete()
4447
/// <inheritdoc />
4548
public override void Refresh()
4649
{
47-
// Nothing to do here. Mock file system is always up-to-date.
50+
cachedMockFileData = mockFileDataAccessor.GetFile(directoryPath)?.Clone();
4851
}
4952

5053
/// <inheritdoc />
@@ -71,7 +74,7 @@ public override DateTime CreationTimeUtc
7174
/// <inheritdoc />
7275
public override bool Exists
7376
{
74-
get { return mockFileDataAccessor.Directory.Exists(FullName); }
77+
get { return GetMockFileDataForRead() != MockFileData.NullObject; }
7578
}
7679

7780
/// <inheritdoc />
@@ -380,11 +383,17 @@ public override IDirectoryInfo Root
380383

381384
private MockFileData GetMockFileDataForRead()
382385
{
383-
return mockFileDataAccessor.GetFile(directoryPath) ?? MockFileData.NullObject;
386+
if (refreshOnNextRead)
387+
{
388+
Refresh();
389+
refreshOnNextRead = false;
390+
}
391+
return cachedMockFileData ?? MockFileData.NullObject;
384392
}
385393

386394
private MockFileData GetMockFileDataForWrite()
387395
{
396+
refreshOnNextRead = true;
388397
return mockFileDataAccessor.GetFile(directoryPath)
389398
?? throw CommonExceptions.FileNotFound(directoryPath);
390399
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class MockFileData
4242
/// <summary>
4343
/// Gets a value indicating whether the <see cref="MockFileData"/> is a directory or not.
4444
/// </summary>
45-
public virtual bool IsDirectory { get { return false; } }
45+
public bool IsDirectory { get { return Attributes.HasFlag(FileAttributes.Directory); } }
4646

4747
/// <summary>
4848
/// Initializes a new instance of the <see cref="MockFileData"/> class with an empty content.
@@ -183,5 +183,10 @@ internal void CheckFileAccess(string path, FileAccess access)
183183
throw CommonExceptions.ProcessCannotAccessFileInUse(path);
184184
}
185185
}
186+
187+
internal virtual MockFileData Clone()
188+
{
189+
return new MockFileData(this);
190+
}
186191
}
187192
}

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

Lines changed: 84 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,17 @@ public class MockFileInfo : FileInfoBase
1010
{
1111
private readonly IMockFileDataAccessor mockFileSystem;
1212
private string path;
13-
private string originalPath;
13+
private readonly string originalPath;
14+
private MockFileData cachedMockFileData;
15+
private bool refreshOnNextRead;
1416

1517
/// <inheritdoc />
1618
public MockFileInfo(IMockFileDataAccessor mockFileSystem, string path) : base(mockFileSystem?.FileSystem)
1719
{
1820
this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem));
1921
this.originalPath = path ?? throw new ArgumentNullException(nameof(path));
2022
this.path = mockFileSystem.Path.GetFullPath(path);
21-
22-
}
23-
24-
MockFileData MockFileData
25-
{
26-
get { return mockFileSystem.GetFile(path); }
23+
Refresh();
2724
}
2825

2926
/// <inheritdoc />
@@ -35,27 +32,21 @@ public override void Delete()
3532
/// <inheritdoc />
3633
public override void Refresh()
3734
{
38-
// Nothing to do here. Mock file system is always up-to-date.
35+
cachedMockFileData = mockFileSystem.GetFile(path)?.Clone();
3936
}
4037

4138
/// <inheritdoc />
4239
public override FileAttributes Attributes
4340
{
4441
get
4542
{
46-
if (MockFileData == null)
47-
{
48-
throw CommonExceptions.FileNotFound(path);
49-
}
50-
return MockFileData.Attributes;
43+
var mockFileData = GetMockFileDataForRead();
44+
return mockFileData.Attributes;
5145
}
5246
set
5347
{
54-
if (MockFileData == null)
55-
{
56-
throw CommonExceptions.FileNotFound(path);
57-
}
58-
MockFileData.Attributes = value;
48+
var mockFileData = GetMockFileDataForWrite();
49+
mockFileData.Attributes = value;
5950
}
6051
}
6152

@@ -64,19 +55,13 @@ public override DateTime CreationTime
6455
{
6556
get
6657
{
67-
if (MockFileData == null)
68-
{
69-
throw CommonExceptions.FileNotFound(path);
70-
}
71-
return MockFileData.CreationTime.DateTime;
58+
var mockFileData = GetMockFileDataForRead();
59+
return mockFileData.CreationTime.DateTime;
7260
}
7361
set
7462
{
75-
if (MockFileData == null)
76-
{
77-
throw CommonExceptions.FileNotFound(path);
78-
}
79-
MockFileData.CreationTime = value;
63+
var mockFileData = GetMockFileDataForWrite();
64+
mockFileData.CreationTime = value;
8065
}
8166
}
8267

@@ -85,20 +70,24 @@ public override DateTime CreationTimeUtc
8570
{
8671
get
8772
{
88-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
89-
return MockFileData.CreationTime.UtcDateTime;
73+
var mockFileData = GetMockFileDataForRead();
74+
return mockFileData.CreationTime.UtcDateTime;
9075
}
9176
set
9277
{
93-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
94-
MockFileData.CreationTime = value.ToLocalTime();
78+
var mockFileData = GetMockFileDataForWrite();
79+
mockFileData.CreationTime = value.ToLocalTime();
9580
}
9681
}
9782

9883
/// <inheritdoc />
9984
public override bool Exists
10085
{
101-
get { return MockFileData != null && !MockFileData.IsDirectory; }
86+
get
87+
{
88+
var mockFileData = GetMockFileDataForRead(throwIfNotExisting: false);
89+
return mockFileData != null && !mockFileData.IsDirectory;
90+
}
10291
}
10392

10493
/// <inheritdoc />
@@ -123,13 +112,13 @@ public override DateTime LastAccessTime
123112
{
124113
get
125114
{
126-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
127-
return MockFileData.LastAccessTime.DateTime;
115+
var mockFileData = GetMockFileDataForRead();
116+
return mockFileData.LastAccessTime.DateTime;
128117
}
129118
set
130119
{
131-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
132-
MockFileData.LastAccessTime = value;
120+
var mockFileData = GetMockFileDataForWrite();
121+
mockFileData.LastAccessTime = value;
133122
}
134123
}
135124

@@ -138,13 +127,13 @@ public override DateTime LastAccessTimeUtc
138127
{
139128
get
140129
{
141-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
142-
return MockFileData.LastAccessTime.UtcDateTime;
130+
var mockFileData = GetMockFileDataForRead();
131+
return mockFileData.LastAccessTime.UtcDateTime;
143132
}
144133
set
145134
{
146-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
147-
MockFileData.LastAccessTime = value;
135+
var mockFileData = GetMockFileDataForWrite();
136+
mockFileData.LastAccessTime = value;
148137
}
149138
}
150139

@@ -153,13 +142,13 @@ public override DateTime LastWriteTime
153142
{
154143
get
155144
{
156-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
157-
return MockFileData.LastWriteTime.DateTime;
145+
var mockFileData = GetMockFileDataForRead();
146+
return mockFileData.LastWriteTime.DateTime;
158147
}
159148
set
160149
{
161-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
162-
MockFileData.LastWriteTime = value;
150+
var mockFileData = GetMockFileDataForWrite();
151+
mockFileData.LastWriteTime = value;
163152
}
164153
}
165154

@@ -168,13 +157,13 @@ public override DateTime LastWriteTimeUtc
168157
{
169158
get
170159
{
171-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
172-
return MockFileData.LastWriteTime.UtcDateTime;
160+
var mockFileData = GetMockFileDataForRead();
161+
return mockFileData.LastWriteTime.UtcDateTime;
173162
}
174163
set
175164
{
176-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
177-
MockFileData.LastWriteTime = value.ToLocalTime();
165+
var mockFileData = GetMockFileDataForWrite();
166+
mockFileData.LastWriteTime = value.ToLocalTime();
178167
}
179168
}
180169

@@ -201,7 +190,11 @@ public override IFileInfo CopyTo(string destFileName, bool overwrite)
201190
{
202191
if (!Exists)
203192
{
204-
if (MockFileData == null) throw CommonExceptions.FileNotFound(FullName);
193+
var mockFileData = GetMockFileDataForRead(throwIfNotExisting: false);
194+
if (mockFileData == null)
195+
{
196+
throw CommonExceptions.FileNotFound(FullName);
197+
}
205198
}
206199
if (destFileName == FullName)
207200
{
@@ -226,17 +219,15 @@ public override StreamWriter CreateText()
226219
/// <inheritdoc />
227220
public override void Decrypt()
228221
{
229-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
230-
231-
MockFileData.Attributes &= ~FileAttributes.Encrypted;
222+
var mockFileData = GetMockFileDataForWrite();
223+
mockFileData.Attributes &= ~FileAttributes.Encrypted;
232224
}
233225

234226
/// <inheritdoc />
235227
public override void Encrypt()
236228
{
237-
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
238-
239-
MockFileData.Attributes |= FileAttributes.Encrypted;
229+
var mockFileData = GetMockFileDataForWrite();
230+
mockFileData.Attributes |= FileAttributes.Encrypted;
240231
}
241232

242233
/// <inheritdoc />
@@ -341,25 +332,19 @@ public override bool IsReadOnly
341332
{
342333
get
343334
{
344-
if (MockFileData == null)
345-
{
346-
throw CommonExceptions.FileNotFound(path);
347-
}
348-
return (MockFileData.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
335+
var mockFileData = GetMockFileDataForRead();
336+
return (mockFileData.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
349337
}
350338
set
351339
{
352-
if (MockFileData == null)
353-
{
354-
throw CommonExceptions.FileNotFound(path);
355-
}
340+
var mockFileData = GetMockFileDataForWrite();
356341
if (value)
357342
{
358-
MockFileData.Attributes |= FileAttributes.ReadOnly;
343+
mockFileData.Attributes |= FileAttributes.ReadOnly;
359344
}
360345
else
361346
{
362-
MockFileData.Attributes &= ~FileAttributes.ReadOnly;
347+
mockFileData.Attributes &= ~FileAttributes.ReadOnly;
363348
}
364349
}
365350
}
@@ -369,11 +354,12 @@ public override long Length
369354
{
370355
get
371356
{
372-
if (MockFileData == null || MockFileData.IsDirectory)
357+
var mockFileData = GetMockFileDataForRead(throwIfNotExisting: false);
358+
if (mockFileData == null || mockFileData.IsDirectory)
373359
{
374360
throw CommonExceptions.FileNotFound(path);
375361
}
376-
return MockFileData.Contents.Length;
362+
return mockFileData.Contents.Length;
377363
}
378364
}
379365

@@ -382,5 +368,34 @@ public override string ToString()
382368
{
383369
return originalPath;
384370
}
371+
372+
private MockFileData GetMockFileDataForRead(bool throwIfNotExisting = true)
373+
{
374+
if (refreshOnNextRead)
375+
{
376+
Refresh();
377+
refreshOnNextRead = false;
378+
}
379+
var mockFileData = cachedMockFileData;
380+
if (mockFileData == null)
381+
{
382+
if (throwIfNotExisting)
383+
{
384+
throw CommonExceptions.FileNotFound(path);
385+
}
386+
else
387+
{
388+
return null;
389+
}
390+
}
391+
return mockFileData;
392+
}
393+
394+
private MockFileData GetMockFileDataForWrite()
395+
{
396+
refreshOnNextRead = true;
397+
return mockFileSystem.GetFile(path)
398+
?? throw CommonExceptions.FileNotFound(path);
399+
}
385400
}
386401
}

0 commit comments

Comments
 (0)