Skip to content

Commit

Permalink
feat: support more properties of EnumerationOptions (#589)
Browse files Browse the repository at this point in the history
* Add failing tests to verify the behavior

* Support MaxRecursionDepth

* Support ReturnSpecialDirectories

* Support AttributesToSkip
  • Loading branch information
vbreuss authored May 1, 2024
1 parent d5309a7 commit 29ccbad
Show file tree
Hide file tree
Showing 5 changed files with 442 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ private IEnumerable<string> EnumerateInternal(FileSystemTypes fileSystemTypes,
adjustedLocation.SearchPattern,
enumerationOptions)
.Select(x => _fileSystem
.GetSubdirectoryPath(x.FullPath, adjustedLocation.GivenPath));
.GetSubdirectoryPath(x.FullPath, x.FriendlyName, adjustedLocation.GivenPath));
}

private IDirectoryInfo LoadDirectoryInfoOrThrowNotFoundException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ internal static IStorageLocation GetMoveLocation(this MockFileSystem fileSystem,
/// </summary>
internal static string GetSubdirectoryPath(this MockFileSystem fileSystem,
string fullFilePath,
string friendlyName,
string givenPath)
{
if (fileSystem.Execute.Path.IsPathRooted(givenPath))
Expand All @@ -66,12 +67,24 @@ internal static string GetSubdirectoryPath(this MockFileSystem fileSystem,
{
fullFilePath = fullFilePath.Substring(currentDirectory.Length);
}
else if (fullFilePath.Equals(
currentDirectory + fileSystem.Execute.Path.DirectorySeparatorChar,
fileSystem.Execute.StringComparisonMode))
{
fullFilePath = ".";
}
else if (fullFilePath.StartsWith(
currentDirectory + fileSystem.Execute.Path.DirectorySeparatorChar,
fileSystem.Execute.StringComparisonMode))
{
fullFilePath = fullFilePath.Substring(currentDirectory.Length + 1);
}
else if (friendlyName.EndsWith(
$"{fileSystem.Execute.Path.DirectorySeparatorChar}..",
StringComparison.Ordinal))
{
fullFilePath = "..";
}
else
{
string? parentName = currentDirectory;
Expand Down
57 changes: 54 additions & 3 deletions Source/Testably.Abstractions.Testing/Storage/InMemoryStorage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,28 @@ public IEnumerable<IStorageLocation> EnumerateLocations(
_fileSystem.Execute.Path.DirectorySeparatorChar);
}

if (enumerationOptions.ReturnSpecialDirectories &&
type == FileSystemTypes.Directory)
{
IStorageDrive? drive = _fileSystem.Storage.GetDrive(fullPath);
if (drive == null &&
!fullPath.IsUncPath(_fileSystem))
{
drive = _fileSystem.Storage.MainDrive;
}

yield return InMemoryLocation.New(_fileSystem, drive, fullPath,
$"{location.FriendlyName}{_fileSystem.Execute.Path.DirectorySeparatorChar}.");
string? parentPath = _fileSystem.Execute.Path.GetDirectoryName(
fullPath.TrimEnd(_fileSystem.Execute.Path
.DirectorySeparatorChar));
if (parentPath != null)
{
yield return InMemoryLocation.New(_fileSystem, drive, parentPath,
$"{location.FriendlyName}{_fileSystem.Execute.Path.DirectorySeparatorChar}..");
}
}

foreach (KeyValuePair<IStorageLocation, IStorageContainer> item in _containers
.Where(x => x.Key.FullPath.StartsWith(fullPath,
_fileSystem.Execute.StringComparisonMode) &&
Expand All @@ -203,9 +225,38 @@ public IEnumerable<IStorageLocation> EnumerateLocations(
_fileSystem.Execute.Path.GetDirectoryName(
item.Key.FullPath.TrimEnd(_fileSystem.Execute.Path
.DirectorySeparatorChar));
if (!enumerationOptions.RecurseSubdirectories &&
parentPath?.Equals(fullPathWithoutTrailingSlash,
_fileSystem.Execute.StringComparisonMode) != true)
if (parentPath == null)
{
continue;
}

if (!parentPath.Equals(fullPathWithoutTrailingSlash,
_fileSystem.Execute.StringComparisonMode))
{
#if NETSTANDARD2_1
if (!enumerationOptions.RecurseSubdirectories)
{
continue;
}
#else
int recursionDepth = parentPath
.Substring(fullPathWithoutTrailingSlash.Length)
.Count(x => x == _fileSystem.Execute.Path.DirectorySeparatorChar);
if (!enumerationOptions.RecurseSubdirectories ||
recursionDepth > enumerationOptions.MaxRecursionDepth)
{
continue;
}
#endif
}

#if NET8_0_OR_GREATER
FileAttributes defaultAttributeToSkip = FileAttributes.None;
#else
FileAttributes defaultAttributeToSkip = 0;
#endif
if (enumerationOptions.AttributesToSkip != defaultAttributeToSkip &&
item.Value.Attributes.HasFlag(enumerationOptions.AttributesToSkip))
{
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,31 +187,205 @@ public void EnumerateDirectories_SearchPattern_WithFileExtension_ShouldReturnExp
[SkippableTheory]
[AutoData]
public void
EnumerateDirectories_WithEnumerationOptions_ShouldConsiderSetOptions(
EnumerateDirectories_WithEnumerationOptions_ShouldConsiderAttributesToSkip(
string path)
{
EnumerationOptions enumerationOptions = new()
{
AttributesToSkip = FileAttributes.ReadOnly
};
IDirectoryInfo baseDirectory =
FileSystem.Directory.CreateDirectory(path);
baseDirectory.CreateSubdirectory("foo/xyz");
baseDirectory.CreateSubdirectory("bar");
baseDirectory.CreateSubdirectory("foo");
baseDirectory.CreateSubdirectory("bar").Attributes = FileAttributes.ReadOnly;

List<string> result = FileSystem.Directory
.EnumerateDirectories(path, "XYZ",
new EnumerationOptions
{
MatchCasing = MatchCasing.CaseInsensitive,
RecurseSubdirectories = true,
// Filename could start with a leading '.' indicating it as Hidden in Linux
AttributesToSkip = FileAttributes.System
}).ToList();
.EnumerateDirectories(path, "*", enumerationOptions).ToList();

result.Count.Should().Be(1);
result.Should().NotContain(FileSystem.Path.Combine(path, "foo"));
result.Should().Contain(FileSystem.Path.Combine(path, "foo", "xyz"));
result.Should().Contain(FileSystem.Path.Combine(path, "foo"));
result.Should().NotContain(FileSystem.Path.Combine(path, "bar"));
}
#endif

#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS
[SkippableTheory]
[InlineAutoData(MatchCasing.CaseInsensitive)]
[InlineAutoData(MatchCasing.CaseSensitive)]
public void
EnumerateDirectories_WithEnumerationOptions_ShouldConsiderMatchCasing(
MatchCasing matchCasing,
string path)
{
EnumerationOptions enumerationOptions = new()
{
MatchCasing = matchCasing
};
IDirectoryInfo baseDirectory =
FileSystem.Directory.CreateDirectory(path);
baseDirectory.CreateSubdirectory("foo");
baseDirectory.CreateSubdirectory("bar");

List<string> result = FileSystem.Directory
.EnumerateDirectories(path, "FOO", enumerationOptions).ToList();

result.Count.Should().Be(matchCasing == MatchCasing.CaseInsensitive ? 1 : 0);
if (matchCasing == MatchCasing.CaseInsensitive)
{
result.Should().Contain(FileSystem.Path.Combine(path, "foo"));
}

result.Should().NotContain(FileSystem.Path.Combine(path, "bar"));
}
#endif

#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS
[SkippableTheory]
[InlineAutoData(MatchType.Simple)]
[InlineAutoData(MatchType.Win32)]
public void
EnumerateDirectories_WithEnumerationOptions_ShouldConsiderMatchType(
MatchType matchType,
string path)
{
EnumerationOptions enumerationOptions = new()
{
MatchType = matchType
};
IDirectoryInfo baseDirectory =
FileSystem.Directory.CreateDirectory(path);
baseDirectory.CreateSubdirectory("foo");
baseDirectory.CreateSubdirectory("bar");

List<string> result = FileSystem.Directory
.EnumerateDirectories(path, "*.", enumerationOptions).ToList();

result.Count.Should().Be(matchType == MatchType.Win32 ? 2 : 0);
if (matchType == MatchType.Win32)
{
result.Should().Contain(FileSystem.Path.Combine(path, "foo"));
result.Should().Contain(FileSystem.Path.Combine(path, "bar"));
}
}
#endif

#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS
[SkippableTheory]
[InlineAutoData(true, 0)]
[InlineAutoData(true, 1)]
[InlineAutoData(true, 2)]
[InlineAutoData(true, 3)]
[InlineAutoData(false, 2)]
public void
EnumerateDirectories_WithEnumerationOptions_ShouldConsiderMaxRecursionDepthWhenRecurseSubdirectoriesIsSet(
bool recurseSubdirectories,
int maxRecursionDepth,
string path)
{
EnumerationOptions enumerationOptions = new()
{
MaxRecursionDepth = maxRecursionDepth,
RecurseSubdirectories = recurseSubdirectories
};
IDirectoryInfo baseDirectory =
FileSystem.Directory.CreateDirectory(path);
baseDirectory.CreateSubdirectory("a/b/c/d/e/foo");
baseDirectory.CreateSubdirectory("a/b/c/d/foo");
baseDirectory.CreateSubdirectory("a/b/c/foo");
baseDirectory.CreateSubdirectory("a/b/foo");
baseDirectory.CreateSubdirectory("a/foo");
baseDirectory.CreateSubdirectory("bar");

List<string> result = FileSystem.Directory
.EnumerateDirectories(path, "foo", enumerationOptions).ToList();

result.Count.Should().Be(recurseSubdirectories ? maxRecursionDepth : 0);
if (recurseSubdirectories)
{
if (maxRecursionDepth > 0)
{
result.Should().Contain(FileSystem.Path.Combine(path, "a", "foo"));
}

if (maxRecursionDepth > 1)
{
result.Should().Contain(FileSystem.Path.Combine(path, "a", "b", "foo"));
}

if (maxRecursionDepth > 2)
{
result.Should().Contain(FileSystem.Path.Combine(path, "a", "b", "c", "foo"));
}
}

result.Should().NotContain(FileSystem.Path.Combine(path, "bar"));
}
#endif

#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS
[SkippableTheory]
[InlineAutoData(true)]
[InlineAutoData(false)]
public void
EnumerateDirectories_WithEnumerationOptions_ShouldConsiderRecurseSubdirectories(
bool recurseSubdirectories,
string path)
{
EnumerationOptions enumerationOptions = new()
{
RecurseSubdirectories = recurseSubdirectories
};
IDirectoryInfo baseDirectory =
FileSystem.Directory.CreateDirectory(path);
baseDirectory.CreateSubdirectory("xyz/foo");
baseDirectory.CreateSubdirectory("bar");

List<string> result = FileSystem.Directory
.EnumerateDirectories(path, "foo", enumerationOptions).ToList();

result.Count.Should().Be(recurseSubdirectories ? 1 : 0);
result.Should().NotContain(FileSystem.Path.Combine(path, "xyz"));
if (recurseSubdirectories)
{
result.Should().Contain(FileSystem.Path.Combine(path, "xyz", "foo"));
}

result.Should().NotContain(FileSystem.Path.Combine(path, "bar"));
}
#endif

#if FEATURE_FILESYSTEM_ENUMERATION_OPTIONS
[SkippableTheory]
[InlineAutoData(true)]
[InlineAutoData(false)]
public void
EnumerateDirectories_WithEnumerationOptions_ShouldConsiderReturnSpecialDirectories(
bool returnSpecialDirectories,
string path)
{
EnumerationOptions enumerationOptions = new()
{
ReturnSpecialDirectories = returnSpecialDirectories
};
IDirectoryInfo baseDirectory =
FileSystem.Directory.CreateDirectory(path);
baseDirectory.CreateSubdirectory("foo");
baseDirectory.CreateSubdirectory("bar");

List<string> result = FileSystem.Directory
.EnumerateDirectories(path, "*", enumerationOptions).ToList();

result.Count.Should().Be(returnSpecialDirectories ? 4 : 2);
result.Should().Contain(FileSystem.Path.Combine(path, "foo"));
result.Should().Contain(FileSystem.Path.Combine(path, "bar"));
if (returnSpecialDirectories)
{
result.Should().Contain(FileSystem.Path.Combine(path, "."));
result.Should().Contain(FileSystem.Path.Combine(path, ".."));
}
}
#endif

[SkippableTheory]
[AutoData]
public void EnumerateDirectories_WithNewline_ShouldThrowArgumentException(
Expand Down
Loading

0 comments on commit 29ccbad

Please sign in to comment.