Skip to content

Commit f66fee3

Browse files
authored
fix: initialize drives with root directory (#847)
This PR fixes a bug where drives in the mock file system were not properly initialized with their root directory containers, causing issues when trying to enumerate files on newly created drives. The fix ensures that when a drive is created, its corresponding root directory container is also created in the storage system. ### Key changes: - Initializes the main drive with a root directory container during storage construction - Updates the `GetOrAddDrive` method to create root directory containers for new drives - Add tests to verify that drives can be enumerated without errors
1 parent c6a2782 commit f66fee3

File tree

4 files changed

+79
-23
lines changed

4 files changed

+79
-23
lines changed

Source/Testably.Abstractions.Testing/MockFileSystem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ public MockFileSystem WithDrive(string? drive,
223223
Action<IStorageDrive>? driveCallback = null)
224224
{
225225
IStorageDrive driveInfoMock =
226-
drive == null
226+
drive == null || string.Equals(drive, Execute.Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)
227227
? Storage.MainDrive
228228
: Storage.GetOrAddDrive(drive);
229229
driveCallback?.Invoke(driveInfoMock);

Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ public InMemoryStorage(MockFileSystem fileSystem)
3636
CurrentDirectory = string.Empty.PrefixRoot(_fileSystem);
3737
DriveInfoMock mainDrive = DriveInfoMock.New(CurrentDirectory, _fileSystem);
3838
_drives.TryAdd(mainDrive.GetName(), mainDrive);
39+
IStorageLocation rootLocation =
40+
InMemoryLocation.New(_fileSystem, mainDrive, mainDrive.GetName());
41+
_containers.TryAdd(rootLocation, InMemoryContainer.NewDirectory(rootLocation, _fileSystem));
42+
3943
MainDrive = mainDrive;
4044
}
4145

@@ -189,7 +193,7 @@ public IEnumerable<IStorageLocation> EnumerateLocations(
189193
EnumerationOptions? enumerationOptions = null)
190194
{
191195
ValidateExpression(searchPattern);
192-
if (!_containers.TryGetValue(location, out var parentContainer))
196+
if (!_containers.TryGetValue(location, out IStorageContainer? parentContainer))
193197
{
194198
throw ExceptionFactory.DirectoryNotFound(location.FullPath);
195199
}
@@ -227,11 +231,11 @@ public IEnumerable<IStorageLocation> EnumerateLocations(
227231
}
228232

229233
if (enumerationOptions.ReturnSpecialDirectories &&
230-
type == FileSystemTypes.Directory)
234+
type == FileSystemTypes.Directory)
231235
{
232236
IStorageDrive? drive = _fileSystem.Storage.GetDrive(fullPath);
233237
if (drive == null &&
234-
!fullPath.IsUncPath(_fileSystem))
238+
!fullPath.IsUncPath(_fileSystem))
235239
{
236240
drive = _fileSystem.Storage.MainDrive;
237241
}
@@ -255,11 +259,12 @@ public IEnumerable<IStorageLocation> EnumerateLocations(
255259

256260
foreach (KeyValuePair<IStorageLocation, IStorageContainer> item in _containers
257261
.Where(x => x.Key.FullPath.StartsWith(fullPath,
258-
_fileSystem.Execute.StringComparisonMode) &&
259-
!x.Key.Equals(location)))
262+
_fileSystem.Execute.StringComparisonMode) &&
263+
!x.Key.Equals(location)))
260264
{
261265
if (type.HasFlag(item.Value.Type) &&
262-
IncludeItemInEnumeration(item, fullPathWithoutTrailingSlash, enumerationOptions))
266+
IncludeItemInEnumeration(item, fullPathWithoutTrailingSlash,
267+
enumerationOptions))
263268
{
264269
string? itemPath = item.Key.FullPath;
265270
if (itemPath.EndsWith(_fileSystem.Path.DirectorySeparatorChar))
@@ -269,14 +274,14 @@ public IEnumerable<IStorageLocation> EnumerateLocations(
269274

270275
string name = _fileSystem.Execute.Path.GetFileName(itemPath);
271276
if (EnumerationOptionsHelper.MatchesPattern(
272-
_fileSystem.Execute,
273-
enumerationOptions,
274-
name,
275-
searchPattern) ||
276-
(_fileSystem.Execute.IsNetFramework &&
277-
SearchPatternMatchesFileExtensionOnNetFramework(
278-
searchPattern,
279-
_fileSystem.Execute.Path.GetExtension(name))))
277+
_fileSystem.Execute,
278+
enumerationOptions,
279+
name,
280+
searchPattern) ||
281+
(_fileSystem.Execute.IsNetFramework &&
282+
SearchPatternMatchesFileExtensionOnNetFramework(
283+
searchPattern,
284+
_fileSystem.Execute.Path.GetExtension(name))))
280285
{
281286
yield return item.Key;
282287
}
@@ -346,8 +351,19 @@ public IEnumerable<IStorageDrive> GetDrives()
346351
drive = _fileSystem.Storage.MainDrive;
347352
}
348353

349-
return InMemoryLocation.New(_fileSystem, drive, path.GetFullPathOrWhiteSpace(_fileSystem),
350-
path);
354+
string fullPath;
355+
if (path.IsUncPath(_fileSystem) &&
356+
_fileSystem.Execute is { IsNetFramework: true } or {IsWindows: false } &&
357+
path.LastIndexOf(_fileSystem.Path.DirectorySeparatorChar) <= 2)
358+
{
359+
fullPath = path;
360+
}
361+
else
362+
{
363+
fullPath = path.GetFullPathOrWhiteSpace(_fileSystem);
364+
}
365+
366+
return InMemoryLocation.New(_fileSystem, drive, fullPath, path);
351367
}
352368

353369
/// <inheritdoc cref="IStorage.GetOrAddDrive(string)" />
@@ -360,7 +376,14 @@ public IEnumerable<IStorageDrive> GetDrives()
360376
}
361377

362378
DriveInfoMock drive = DriveInfoMock.New(driveName, _fileSystem);
363-
return _drives.GetOrAdd(drive.GetName(), _ => drive);
379+
return _drives.GetOrAdd(drive.GetName(), _ =>
380+
{
381+
IStorageLocation rootLocation =
382+
InMemoryLocation.New(_fileSystem, drive, drive.GetName());
383+
_containers.TryAdd(rootLocation,
384+
InMemoryContainer.NewDirectory(rootLocation, _fileSystem));
385+
return drive;
386+
});
364387
}
365388

366389
/// <inheritdoc cref="IStorage.GetOrCreateContainer" />

Tests/Testably.Abstractions.Testing.Tests/MockFileSystemTests.cs

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.IO;
1+
using System.Collections.Generic;
2+
using System.IO;
23
using System.Linq;
34
using Testably.Abstractions.Helpers;
45
using Testably.Abstractions.Testing.FileSystem;
@@ -115,13 +116,14 @@ public async Task ToString_ShouldContainStorageInformation()
115116

116117
string result = sut.ToString();
117118

118-
await That(result).Contains("directories: 0, files: 1");
119+
await That(result).Contains("directories: 1, files: 1");
119120
}
120121

121122
[Theory]
122123
[AutoData]
123-
public async Task WithAccessControl_Denied_CreateDirectoryShouldThrowUnauthorizedAccessException(
124-
string path)
124+
public async Task
125+
WithAccessControl_Denied_CreateDirectoryShouldThrowUnauthorizedAccessException(
126+
string path)
125127
{
126128
Skip.If(!Test.RunsOnWindows);
127129

@@ -241,6 +243,20 @@ await That(drives).HasSingle().Matching(d
241243
=> string.Equals(d.Name, expectedDriveName, StringComparison.Ordinal));
242244
}
243245

246+
[Fact]
247+
public async Task WithDrive_ShouldInitializeDrivesWithRootDirectory()
248+
{
249+
Skip.IfNot(Test.RunsOnWindows, "Linux does not support different drives.");
250+
251+
MockFileSystem sut = new MockFileSystem().WithDrive("V");
252+
List<IFileSystemInfo> fileSystemInfos = sut.DriveInfo.GetDrives()
253+
.SelectMany(drive => drive.RootDirectory
254+
.EnumerateFileSystemInfos("*", SearchOption.AllDirectories))
255+
.ToList();
256+
257+
await That(fileSystemInfos).HasCount(0);
258+
}
259+
244260
[Theory]
245261
[AutoData]
246262
public async Task WithDrive_WithCallback_ShouldUpdateDrive(long totalSize)
@@ -305,6 +321,22 @@ public async Task WithUncDrive_ShouldCreateUncDrive(
305321
await That(result).IsEqualTo(contents);
306322
}
307323

324+
[Fact]
325+
public async Task WithUncDrive_ShouldInitializeDrivesWithRootDirectory()
326+
{
327+
MockFileSystem sut = new();
328+
string uncPrefix = new(sut.Path.DirectorySeparatorChar, 2);
329+
string uncDrive = $"{uncPrefix}UNC-Path";
330+
sut.WithUncDrive("UNC-Path");
331+
IDriveInfo drive = sut.DriveInfo.New(uncDrive);
332+
333+
List<IFileSystemInfo> fileSystemInfos = drive.RootDirectory
334+
.EnumerateFileSystemInfos("*", SearchOption.AllDirectories)
335+
.ToList();
336+
337+
await That(fileSystemInfos).HasCount(0);
338+
}
339+
308340
[Theory]
309341
[AutoData]
310342
public async Task WithUncDrive_ShouldNotBeIncludedInGetDrives(

Tests/Testably.Abstractions.Testing.Tests/Storage/InMemoryContainerTests.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,8 @@ public async Task ToString_File_ShouldIncludePathAndFileSize(byte[] bytes)
303303
MockFileSystem fileSystem = new();
304304
string expectedPath = fileSystem.Path.GetFullPath("foo.txt");
305305
fileSystem.File.WriteAllBytes(expectedPath, bytes);
306-
IStorageContainer sut = fileSystem.StorageContainers.Single();
306+
IStorageContainer sut = fileSystem.StorageContainers
307+
.Single(x => x.Type == FileSystemTypes.File);
307308

308309
string? result = sut.ToString();
309310

0 commit comments

Comments
 (0)