Skip to content

Commit

Permalink
feat: add testability for DriveInfo (#1094)
Browse files Browse the repository at this point in the history
* feat: add testability for drives

* review comments

---------

Co-authored-by: Valentin Breuß <vbreuss@gmail.com>
  • Loading branch information
FantasyTeddy and vbreuss committed Mar 9, 2024
1 parent d517e19 commit 3229ce9
Show file tree
Hide file tree
Showing 9 changed files with 474 additions and 196 deletions.
Expand Up @@ -23,6 +23,13 @@ public interface IMockFileDataAccessor : IFileSystem
/// <returns>The file. <see langword="null"/> if the file does not exist.</returns>
MockFileData GetFile(string path);

/// <summary>
/// Gets a drive.
/// </summary>
/// <param name="name">The name of the drive to get.</param>
/// <returns>The drive. <see langword="null"/> if the drive does not exist.</returns>
MockDriveData GetDrive(string name);

/// <summary>
/// </summary>
void AddFile(string path, MockFileData mockFile);
Expand All @@ -31,6 +38,10 @@ public interface IMockFileDataAccessor : IFileSystem
/// </summary>
void AddDirectory(string path);

/// <summary>
/// </summary>
void AddDrive(string name, MockDriveData mockDrive);

/// <summary>
/// </summary>
void AddFileFromEmbeddedResource(string path, Assembly resourceAssembly, string embeddedResourcePath);
Expand Down Expand Up @@ -74,6 +85,11 @@ public interface IMockFileDataAccessor : IFileSystem
/// </summary>
IEnumerable<string> AllDirectories { get; }

/// <summary>
/// Gets the names of all drives.
/// </summary>
IEnumerable<string> AllDrives { get; }

/// <summary>
/// Gets a helper for string operations.
/// </summary>
Expand Down
@@ -0,0 +1,76 @@

namespace System.IO.Abstractions.TestingHelpers
{
/// <summary>
/// The class represents the associated data of a drive.
/// </summary>
#if FEATURE_SERIALIZABLE
[Serializable]
#endif
public class MockDriveData
{
/// <summary>
/// Initializes a new instance of the <see cref="MockDriveData"/> class.
/// </summary>
public MockDriveData()
{
IsReady = true;
}

/// <summary>
/// Initializes a new instance of the <see cref="MockDriveData"/> class by copying the given <see cref="MockDriveData"/>.
/// </summary>
/// <param name="template">The template instance.</param>
/// <exception cref="ArgumentNullException">Thrown if <paramref name="template"/> is <see langword="null"/>.</exception>
public MockDriveData(MockDriveData template)
{
if (template == null)
{
throw new ArgumentNullException(nameof(template));
}

AvailableFreeSpace = template.AvailableFreeSpace;
DriveFormat = template.DriveFormat;
DriveType = template.DriveType;
IsReady = template.IsReady;
TotalFreeSpace = template.TotalFreeSpace;
TotalSize = template.TotalSize;
VolumeLabel = template.VolumeLabel;
}

/// <summary>
/// Gets or sets the amount of available free space of the <see cref="MockDriveData"/>, in bytes.
/// </summary>
public long AvailableFreeSpace { get; set; }

/// <summary>
/// Gets or sets the name of the file system of the <see cref="MockDriveData"/>, such as NTFS or FAT32.
/// </summary>
public string DriveFormat { get; set; }

/// <summary>
/// Gets or sets the drive type of the <see cref="MockDriveData"/>, such as CD-ROM, removable, network, or fixed.
/// </summary>
public DriveType DriveType { get; set; }

/// <summary>
/// Gets or sets the value that indicates whether the <see cref="MockDriveData"/> is ready.
/// </summary>
public bool IsReady { get; set; }

/// <summary>
/// Gets or sets the total amount of free space available on the <see cref="MockDriveData"/>, in bytes.
/// </summary>
public long TotalFreeSpace { get; set; }

/// <summary>
/// Gets or sets the total size of storage space on the <see cref="MockDriveData"/>, in bytes.
/// </summary>
public long TotalSize { get; set; }

/// <summary>
/// Gets or sets the volume label of the <see cref="MockDriveData"/>.
/// </summary>
public string VolumeLabel { get; set; }
}
}
109 changes: 76 additions & 33 deletions src/TestableIO.System.IO.Abstractions.TestingHelpers/MockDriveInfo.cs
Expand Up @@ -7,51 +7,60 @@
public class MockDriveInfo : DriveInfoBase
{
private readonly IMockFileDataAccessor mockFileDataAccessor;
private readonly string name;

/// <inheritdoc />
public MockDriveInfo(IMockFileDataAccessor mockFileDataAccessor, string name) : base(mockFileDataAccessor?.FileSystem)
{
this.mockFileDataAccessor = mockFileDataAccessor ?? throw new ArgumentNullException(nameof(mockFileDataAccessor));
this.name = mockFileDataAccessor.PathVerifier.NormalizeDriveName(name);
}

if (name == null)
/// <inheritdoc />
public override long AvailableFreeSpace
{
get
{
throw new ArgumentNullException(nameof(name));
var mockDriveData = GetMockDriveData();
return mockDriveData.AvailableFreeSpace;
}
}

const string DRIVE_SEPARATOR = @":\";

if (name.Length == 1
|| (name.Length == 2 && name[1] == ':')
|| (name.Length == 3 && mockFileDataAccessor.StringOperations.EndsWith(name, DRIVE_SEPARATOR)))
/// <inheritdoc />
public override string DriveFormat
{
get
{
name = name[0] + DRIVE_SEPARATOR;
var mockDriveData = GetMockDriveData();
return mockDriveData.DriveFormat;
}
else
{
mockFileDataAccessor.PathVerifier.CheckInvalidPathChars(name);
name = mockFileDataAccessor.Path.GetPathRoot(name);
}

if (string.IsNullOrEmpty(name) || mockFileDataAccessor.StringOperations.StartsWith(name, @"\\"))
{
throw new ArgumentException(
@"Object must be a root directory (""C:\"") or a drive letter (""C"").");
}
/// <inheritdoc />
public override DriveType DriveType
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.DriveType;
}

Name = name;
IsReady = true;
}

/// <inheritdoc />
public new long AvailableFreeSpace { get; set; }
/// <inheritdoc />
public new string DriveFormat { get; set; }
/// <inheritdoc />
public new DriveType DriveType { get; set; }
/// <inheritdoc />
public new bool IsReady { get; protected set; }
public override bool IsReady
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.IsReady;
}
}

/// <inheritdoc />
public override string Name { get; protected set; }
public override string Name
{
get { return name; }
}

/// <inheritdoc />
public override IDirectoryInfo RootDirectory
Expand All @@ -63,16 +72,50 @@ public override IDirectoryInfo RootDirectory
}

/// <inheritdoc />
public override string ToString()
public override long TotalFreeSpace
{
return Name;
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.TotalFreeSpace;
}
}

/// <inheritdoc />
public new long TotalFreeSpace { get; protected set; }
public override long TotalSize
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.TotalSize;
}
}

/// <inheritdoc />
public new long TotalSize { get; protected set; }
public override string VolumeLabel
{
get
{
var mockDriveData = GetMockDriveData();
return mockDriveData.VolumeLabel;
}
set
{
var mockDriveData = GetMockDriveData();
mockDriveData.VolumeLabel = value;
}
}

/// <inheritdoc />
public override string VolumeLabel { get; set; }
public override string ToString()
{
return Name;
}

private MockDriveData GetMockDriveData()
{
return mockFileDataAccessor.GetDrive(name)
?? throw CommonExceptions.FileNotFound(name);
}
}
}
Expand Up @@ -23,15 +23,8 @@ public IFileSystem FileSystem
/// <inheritdoc />
public IDriveInfo[] GetDrives()
{
var driveLetters = new HashSet<string>(new DriveEqualityComparer(mockFileSystem));
foreach (var path in mockFileSystem.AllPaths)
{
var pathRoot = mockFileSystem.Path.GetPathRoot(path);
driveLetters.Add(pathRoot);
}

var result = new List<DriveInfoBase>();
foreach (string driveLetter in driveLetters)
foreach (string driveLetter in mockFileSystem.AllDrives)
{
try
{
Expand Down

0 comments on commit 3229ce9

Please sign in to comment.