Skip to content

Commit

Permalink
fix: make Mock{File,Directory}Info cache file attributes (TestableIO#791
Browse files Browse the repository at this point in the history
)

This makes them behave more similar to the real file system implementation

Fixes TestableIO#661
  • Loading branch information
fgreinacher authored Jan 9, 2022
1 parent f46d7c4 commit 5216e0d
Show file tree
Hide file tree
Showing 6 changed files with 183 additions and 76 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ public class MockDirectoryData : MockFileData
[NonSerialized]
private DirectorySecurity accessControl;

/// <inheritdoc />
public override bool IsDirectory { get { return true; } }

/// <inheritdoc />
public MockDirectoryData() : base(string.Empty)
{
Expand Down
15 changes: 12 additions & 3 deletions src/System.IO.Abstractions.TestingHelpers/MockDirectoryInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ public class MockDirectoryInfo : DirectoryInfoBase
private readonly IMockFileDataAccessor mockFileDataAccessor;
private readonly string directoryPath;
private readonly string originalPath;
private MockFileData cachedMockFileData;
private bool refreshOnNextRead;

/// <summary>
/// Initializes a new instance of the <see cref="MockDirectoryInfo"/> class.
Expand All @@ -33,6 +35,7 @@ public MockDirectoryInfo(IMockFileDataAccessor mockFileDataAccessor, string dire
directoryPath = directoryPath.TrimEnd(' ');
}
this.directoryPath = directoryPath;
Refresh();
}

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

/// <inheritdoc />
Expand All @@ -71,7 +74,7 @@ public override DateTime CreationTimeUtc
/// <inheritdoc />
public override bool Exists
{
get { return mockFileDataAccessor.Directory.Exists(FullName); }
get { return GetMockFileDataForRead() != MockFileData.NullObject; }
}

/// <inheritdoc />
Expand Down Expand Up @@ -380,11 +383,17 @@ public override IDirectoryInfo Root

private MockFileData GetMockFileDataForRead()
{
return mockFileDataAccessor.GetFile(directoryPath) ?? MockFileData.NullObject;
if (refreshOnNextRead)
{
Refresh();
refreshOnNextRead = false;
}
return cachedMockFileData ?? MockFileData.NullObject;
}

private MockFileData GetMockFileDataForWrite()
{
refreshOnNextRead = true;
return mockFileDataAccessor.GetFile(directoryPath)
?? throw CommonExceptions.FileNotFound(directoryPath);
}
Expand Down
7 changes: 6 additions & 1 deletion src/System.IO.Abstractions.TestingHelpers/MockFileData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class MockFileData
/// <summary>
/// Gets a value indicating whether the <see cref="MockFileData"/> is a directory or not.
/// </summary>
public virtual bool IsDirectory { get { return false; } }
public bool IsDirectory { get { return Attributes.HasFlag(FileAttributes.Directory); } }

/// <summary>
/// Initializes a new instance of the <see cref="MockFileData"/> class with an empty content.
Expand Down Expand Up @@ -183,5 +183,10 @@ internal void CheckFileAccess(string path, FileAccess access)
throw CommonExceptions.ProcessCannotAccessFileInUse(path);
}
}

internal virtual MockFileData Clone()
{
return new MockFileData(this);
}
}
}
153 changes: 84 additions & 69 deletions src/System.IO.Abstractions.TestingHelpers/MockFileInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,20 +10,17 @@ public class MockFileInfo : FileInfoBase
{
private readonly IMockFileDataAccessor mockFileSystem;
private string path;
private string originalPath;
private readonly string originalPath;
private MockFileData cachedMockFileData;
private bool refreshOnNextRead;

/// <inheritdoc />
public MockFileInfo(IMockFileDataAccessor mockFileSystem, string path) : base(mockFileSystem?.FileSystem)
{
this.mockFileSystem = mockFileSystem ?? throw new ArgumentNullException(nameof(mockFileSystem));
this.originalPath = path ?? throw new ArgumentNullException(nameof(path));
this.path = mockFileSystem.Path.GetFullPath(path);

}

MockFileData MockFileData
{
get { return mockFileSystem.GetFile(path); }
Refresh();
}

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

/// <inheritdoc />
public override FileAttributes Attributes
{
get
{
if (MockFileData == null)
{
throw CommonExceptions.FileNotFound(path);
}
return MockFileData.Attributes;
var mockFileData = GetMockFileDataForRead();
return mockFileData.Attributes;
}
set
{
if (MockFileData == null)
{
throw CommonExceptions.FileNotFound(path);
}
MockFileData.Attributes = value;
var mockFileData = GetMockFileDataForWrite();
mockFileData.Attributes = value;
}
}

Expand All @@ -64,19 +55,13 @@ public override DateTime CreationTime
{
get
{
if (MockFileData == null)
{
throw CommonExceptions.FileNotFound(path);
}
return MockFileData.CreationTime.DateTime;
var mockFileData = GetMockFileDataForRead();
return mockFileData.CreationTime.DateTime;
}
set
{
if (MockFileData == null)
{
throw CommonExceptions.FileNotFound(path);
}
MockFileData.CreationTime = value;
var mockFileData = GetMockFileDataForWrite();
mockFileData.CreationTime = value;
}
}

Expand All @@ -85,20 +70,24 @@ public override DateTime CreationTimeUtc
{
get
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
return MockFileData.CreationTime.UtcDateTime;
var mockFileData = GetMockFileDataForRead();
return mockFileData.CreationTime.UtcDateTime;
}
set
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
MockFileData.CreationTime = value.ToLocalTime();
var mockFileData = GetMockFileDataForWrite();
mockFileData.CreationTime = value.ToLocalTime();
}
}

/// <inheritdoc />
public override bool Exists
{
get { return MockFileData != null && !MockFileData.IsDirectory; }
get
{
var mockFileData = GetMockFileDataForRead(throwIfNotExisting: false);
return mockFileData != null && !mockFileData.IsDirectory;
}
}

/// <inheritdoc />
Expand All @@ -123,13 +112,13 @@ public override DateTime LastAccessTime
{
get
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
return MockFileData.LastAccessTime.DateTime;
var mockFileData = GetMockFileDataForRead();
return mockFileData.LastAccessTime.DateTime;
}
set
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
MockFileData.LastAccessTime = value;
var mockFileData = GetMockFileDataForWrite();
mockFileData.LastAccessTime = value;
}
}

Expand All @@ -138,13 +127,13 @@ public override DateTime LastAccessTimeUtc
{
get
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
return MockFileData.LastAccessTime.UtcDateTime;
var mockFileData = GetMockFileDataForRead();
return mockFileData.LastAccessTime.UtcDateTime;
}
set
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
MockFileData.LastAccessTime = value;
var mockFileData = GetMockFileDataForWrite();
mockFileData.LastAccessTime = value;
}
}

Expand All @@ -153,13 +142,13 @@ public override DateTime LastWriteTime
{
get
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
return MockFileData.LastWriteTime.DateTime;
var mockFileData = GetMockFileDataForRead();
return mockFileData.LastWriteTime.DateTime;
}
set
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
MockFileData.LastWriteTime = value;
var mockFileData = GetMockFileDataForWrite();
mockFileData.LastWriteTime = value;
}
}

Expand All @@ -168,13 +157,13 @@ public override DateTime LastWriteTimeUtc
{
get
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
return MockFileData.LastWriteTime.UtcDateTime;
var mockFileData = GetMockFileDataForRead();
return mockFileData.LastWriteTime.UtcDateTime;
}
set
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);
MockFileData.LastWriteTime = value.ToLocalTime();
var mockFileData = GetMockFileDataForWrite();
mockFileData.LastWriteTime = value.ToLocalTime();
}
}

Expand All @@ -201,7 +190,11 @@ public override IFileInfo CopyTo(string destFileName, bool overwrite)
{
if (!Exists)
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(FullName);
var mockFileData = GetMockFileDataForRead(throwIfNotExisting: false);
if (mockFileData == null)
{
throw CommonExceptions.FileNotFound(FullName);
}
}
if (destFileName == FullName)
{
Expand All @@ -226,17 +219,15 @@ public override StreamWriter CreateText()
/// <inheritdoc />
public override void Decrypt()
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);

MockFileData.Attributes &= ~FileAttributes.Encrypted;
var mockFileData = GetMockFileDataForWrite();
mockFileData.Attributes &= ~FileAttributes.Encrypted;
}

/// <inheritdoc />
public override void Encrypt()
{
if (MockFileData == null) throw CommonExceptions.FileNotFound(path);

MockFileData.Attributes |= FileAttributes.Encrypted;
var mockFileData = GetMockFileDataForWrite();
mockFileData.Attributes |= FileAttributes.Encrypted;
}

/// <inheritdoc />
Expand Down Expand Up @@ -341,25 +332,19 @@ public override bool IsReadOnly
{
get
{
if (MockFileData == null)
{
throw CommonExceptions.FileNotFound(path);
}
return (MockFileData.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
var mockFileData = GetMockFileDataForRead();
return (mockFileData.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly;
}
set
{
if (MockFileData == null)
{
throw CommonExceptions.FileNotFound(path);
}
var mockFileData = GetMockFileDataForWrite();
if (value)
{
MockFileData.Attributes |= FileAttributes.ReadOnly;
mockFileData.Attributes |= FileAttributes.ReadOnly;
}
else
{
MockFileData.Attributes &= ~FileAttributes.ReadOnly;
mockFileData.Attributes &= ~FileAttributes.ReadOnly;
}
}
}
Expand All @@ -369,11 +354,12 @@ public override long Length
{
get
{
if (MockFileData == null || MockFileData.IsDirectory)
var mockFileData = GetMockFileDataForRead(throwIfNotExisting: false);
if (mockFileData == null || mockFileData.IsDirectory)
{
throw CommonExceptions.FileNotFound(path);
}
return MockFileData.Contents.Length;
return mockFileData.Contents.Length;
}
}

Expand All @@ -382,5 +368,34 @@ public override string ToString()
{
return originalPath;
}

private MockFileData GetMockFileDataForRead(bool throwIfNotExisting = true)
{
if (refreshOnNextRead)
{
Refresh();
refreshOnNextRead = false;
}
var mockFileData = cachedMockFileData;
if (mockFileData == null)
{
if (throwIfNotExisting)
{
throw CommonExceptions.FileNotFound(path);
}
else
{
return null;
}
}
return mockFileData;
}

private MockFileData GetMockFileDataForWrite()
{
refreshOnNextRead = true;
return mockFileSystem.GetFile(path)
?? throw CommonExceptions.FileNotFound(path);
}
}
}
Loading

0 comments on commit 5216e0d

Please sign in to comment.