Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit d2a2221

Browse files
authored
Fix a bug in TranslateWin32Expression (#27539)
Add test coverage.
1 parent 7924c62 commit d2a2221

File tree

4 files changed

+119
-9
lines changed

4 files changed

+119
-9
lines changed

src/System.IO.FileSystem/src/System/IO/Enumeration/FileSystemName.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public static string TranslateWin32Expression(string expression)
4444
{
4545
case '.':
4646
modified = true;
47-
if (i > 1 && i == length - 1 && expression[i - 1] == '*')
47+
if (i >= 1 && i == length - 1 && expression[i - 1] == '*')
4848
{
4949
sb[sb.Length - 1] = '<'; // DOS_STAR (ends in *.)
5050
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Linq;
6+
using Xunit;
7+
8+
namespace System.IO.Tests.Enumeration
9+
{
10+
public abstract class MatchTypesTests : FileSystemTest
11+
{
12+
protected abstract string[] GetPaths(string directory, string pattern, EnumerationOptions options);
13+
14+
[Fact]
15+
public void QuestionMarkBehavior()
16+
{
17+
DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
18+
FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "a.one"));
19+
FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "ab.two"));
20+
FileInfo fileThree = new FileInfo(Path.Combine(testDirectory.FullName, "abc.three"));
21+
22+
fileOne.Create().Dispose();
23+
fileTwo.Create().Dispose();
24+
fileThree.Create().Dispose();
25+
26+
// Question marks collapse to periods in Win32
27+
string[] paths = GetPaths(testDirectory.FullName, "a??.*", new EnumerationOptions { MatchType = MatchType.Win32 });
28+
FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName, fileThree.FullName }, paths);
29+
30+
paths = GetPaths(testDirectory.FullName, "*.?????", new EnumerationOptions { MatchType = MatchType.Win32 });
31+
FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileTwo.FullName, fileThree.FullName }, paths);
32+
33+
// Simple, one question mark is one character
34+
paths = GetPaths(testDirectory.FullName, "a??.*", new EnumerationOptions { MatchType = MatchType.Simple });
35+
FSAssert.EqualWhenOrdered(new string[] { fileThree.FullName }, paths);
36+
37+
paths = GetPaths(testDirectory.FullName, "*.?????", new EnumerationOptions { MatchType = MatchType.Simple });
38+
FSAssert.EqualWhenOrdered(new string[] { fileThree.FullName }, paths);
39+
}
40+
41+
[Fact]
42+
public void StarDotBehavior()
43+
{
44+
DirectoryInfo testDirectory = Directory.CreateDirectory(GetTestFilePath());
45+
FileInfo fileOne = new FileInfo(Path.Combine(testDirectory.FullName, "one"));
46+
FileInfo fileTwo = new FileInfo(Path.Combine(testDirectory.FullName, "one.two"));
47+
string fileThree = Path.Combine(testDirectory.FullName, "three.");
48+
49+
fileOne.Create().Dispose();
50+
fileTwo.Create().Dispose();
51+
52+
// Need extended device syntax to create a name with a trailing dot.
53+
File.Create(PlatformDetection.IsWindows ? @"\\?\" + fileThree : fileThree).Dispose();
54+
55+
// *. means any file without an extension
56+
string[] paths = GetPaths(testDirectory.FullName, "*.", new EnumerationOptions { MatchType = MatchType.Win32 });
57+
FSAssert.EqualWhenOrdered(new string[] { fileOne.FullName, fileThree }, paths);
58+
59+
// Simple, anything with a trailing period
60+
paths = GetPaths(testDirectory.FullName, "*.", new EnumerationOptions { MatchType = MatchType.Simple });
61+
FSAssert.EqualWhenOrdered(new string[] { fileThree }, paths);
62+
}
63+
}
64+
65+
public class MatchTypesTests_Directory_GetFiles : MatchTypesTests
66+
{
67+
protected override string[] GetPaths(string directory, string pattern, EnumerationOptions options)
68+
{
69+
return Directory.GetFiles(directory, pattern, options);
70+
}
71+
}
72+
73+
public class MatchTypesTests_DirectoryInfo_GetFiles : MatchTypesTests
74+
{
75+
protected override string[] GetPaths(string directory, string pattern, EnumerationOptions options)
76+
{
77+
return new DirectoryInfo(directory).GetFiles(pattern, options).Select(i => i.FullName).ToArray();
78+
}
79+
}
80+
}

src/System.IO.FileSystem/tests/Enumeration/Win32MatcherTests.netcoreapp.cs

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,32 @@ namespace System.IO.Tests
99
{
1010
public class Win32MatcherTests
1111
{
12-
[Theory, MemberData(nameof(Win32MatchData)), MemberData(nameof(EscapedWin32MatchData))]
12+
[Theory,
13+
MemberData(nameof(SimpleMatchData)),
14+
MemberData(nameof(EscapedSimpleMatchData)),
15+
MemberData(nameof(Win32MatchData)),
16+
MemberData(nameof(EscapedWin32MatchData))]
1317
public static void Win32Match(string expression, string name, bool ignoreCase, bool expected)
1418
{
1519
Assert.Equal(expected, FileSystemName.MatchesWin32Expression(expression, name.AsSpan(), ignoreCase));
1620
}
1721

18-
public static TheoryData<string, string, bool, bool> EscapedWin32MatchData => new TheoryData<string, string, bool, bool>
22+
[Theory,
23+
MemberData(nameof(SimpleMatchData)),
24+
MemberData(nameof(EscapedSimpleMatchData))]
25+
public static void SimpleMatch(string expression, string name, bool ignoreCase, bool expected)
26+
{
27+
Assert.Equal(expected, FileSystemName.MatchesSimpleExpression(expression, name.AsSpan(), ignoreCase));
28+
}
29+
30+
public static TheoryData<string, string, bool, bool> EscapedSimpleMatchData => new TheoryData<string, string, bool, bool>
1931
{
2032
// Trailing escape matches as it is considered "invisible"
2133
{ "\\", "\\", false, true },
2234
{ "\\", "\\", true, true },
2335
{ "\\\\", "\\", false, true },
2436
{ "\\\\", "\\", true, true },
2537

26-
2738
{ "\\*", "a", false, false },
2839
{ "\\*", "a", true, false },
2940
{ "\\*", "*", false, true },
@@ -36,14 +47,17 @@ public static void Win32Match(string expression, string name, bool ignoreCase, b
3647
{ "*\\*", "***A", true, false },
3748
{ "*\\*", "ABC*A", false, false },
3849
{ "*\\*", "ABC*A", true, false },
50+
};
3951

52+
public static TheoryData<string, string, bool, bool> EscapedWin32MatchData => new TheoryData<string, string, bool, bool>
53+
{
4054
{ "\\\"", "a", false, false },
4155
{ "\\\"", "a", true, false },
4256
{ "\\\"", "\"", false, true },
4357
{ "\\\"", "\"", true, true },
4458
};
4559

46-
public static TheoryData<string, string, bool, bool> Win32MatchData => new TheoryData<string, string, bool, bool>
60+
public static TheoryData<string, string, bool, bool> SimpleMatchData => new TheoryData<string, string, bool, bool>
4761
{
4862
{ null, "", false, false },
4963
{ null, "", true, false },
@@ -58,14 +72,17 @@ public static void Win32Match(string expression, string name, bool ignoreCase, b
5872
{ "*foo", "nofoo", true, true },
5973
{ "*foo", "NoFOO", true, true },
6074
{ "*foo", "noFOO", false, false },
61-
6275
{ @"*", @"foo.txt", true, true },
6376
{ @".", @"foo.txt", true, false },
6477
{ @".", @"footxt", true, false },
6578
{ @"*.*", @"foo.txt", true, true },
6679
{ @"*.*", @"foo.", true, true },
6780
{ @"*.*", @".foo", true, true },
6881
{ @"*.*", @"footxt", true, false },
82+
};
83+
84+
public static TheoryData<string, string, bool, bool> Win32MatchData => new TheoryData<string, string, bool, bool>
85+
{
6986
{ "<\"*", @"footxt", true, true }, // DOS equivalent of *.*
7087
{ "<\"*", @"foo.txt", true, true }, // DOS equivalent of *.*
7188
{ "<\"*", @".foo", true, true }, // DOS equivalent of *.*
@@ -112,5 +129,19 @@ public static void Win32Match(string expression, string name, bool ignoreCase, b
112129
{ @"<.", @"a.", true, true },
113130
{ @"<.", @"a.b", true, false },
114131
};
132+
133+
[Theory,
134+
InlineData("", "*"),
135+
InlineData("*.*", "*"),
136+
InlineData("*", "*"),
137+
InlineData(".", "."),
138+
InlineData("?", ">"),
139+
InlineData("*.", "<"),
140+
InlineData("?.?", ">\">"),
141+
InlineData("foo*.", "foo<")]
142+
public void TranslateExpression(string expression, string expected)
143+
{
144+
Assert.Equal(expected, FileSystemName.TranslateWin32Expression(expression));
145+
}
115146
}
116147
}

src/System.IO.FileSystem/tests/System.IO.FileSystem.Tests.csproj

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
<Compile Include="Enumeration\PatternTransformTests.netcoreapp.cs" />
6565
<Compile Include="Enumeration\RootTests.netcoreapp.cs" />
6666
<Compile Include="Enumeration\AttributeTests.netcoreapp.cs" />
67+
<Compile Include="Enumeration\MatchTypesTests.netcoreapp.cs" />
6768
</ItemGroup>
6869
<ItemGroup>
6970
<!-- Rewritten -->
@@ -195,8 +196,6 @@
195196
<ItemGroup>
196197
<EmbeddedResource Include="Resources\$(AssemblyName).rd.xml" />
197198
</ItemGroup>
198-
<ItemGroup>
199-
<Folder Include="Matchers\" />
200-
</ItemGroup>
199+
<ItemGroup />
201200
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
202201
</Project>

0 commit comments

Comments
 (0)