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

Commit 85c5601

Browse files
committed
Add more tests for System.IO.Path
Path has a bunch of corner cases it tries to cover, especially in normalization. This brings code coverage up to > 90% to hit many more of those cases. I'm doing this as part of moving to the Path class in corefx rather than in mscorlib.
1 parent 5604615 commit 85c5601

File tree

1 file changed

+206
-22
lines changed
  • src/System.Runtime.Extensions/tests/System/IO

1 file changed

+206
-22
lines changed

src/System.Runtime.Extensions/tests/System/IO/Path.cs

Lines changed: 206 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -4,42 +4,58 @@
44
using System;
55
using System.Collections.Generic;
66
using System.IO;
7+
using System.Runtime.InteropServices;
8+
using System.Text;
79
using Xunit;
810

911
public static class PathTests
1012
{
11-
[Fact]
12-
public static void ChangeExtension()
13+
[Theory]
14+
[InlineData(null, null, null)]
15+
[InlineData(null, null, "exe")]
16+
[InlineData("", "", "")]
17+
[InlineData("file", "file.exe", null)]
18+
[InlineData("file.", "file.exe", "")]
19+
[InlineData("file.exe", "file", "exe")]
20+
[InlineData("file.exe", "file", ".exe")]
21+
[InlineData("file.exe", "file.txt", "exe")]
22+
[InlineData("file.exe", "file.txt", ".exe")]
23+
[InlineData("file.txt.exe", "file.txt.bin", "exe")]
24+
[InlineData("dir/file.exe", "dir/file.t", "exe")]
25+
[InlineData("dir/file.t", "dir/file.exe", "t")]
26+
[InlineData("dir/file.exe", "dir/file", "exe")]
27+
public static void ChangeExtension(string expected, string path, string newExtension)
1328
{
14-
Assert.Null(Path.ChangeExtension(null, null));
15-
Assert.Equal("file", Path.ChangeExtension("file.exe", null));
16-
Assert.Equal("file.exe", Path.ChangeExtension("file.txt", "exe"));
29+
if (expected != null)
30+
expected = expected.Replace('/', Path.DirectorySeparatorChar);
31+
if (path != null)
32+
path = path.Replace('/', Path.DirectorySeparatorChar);
33+
Assert.Equal(expected, Path.ChangeExtension(path, newExtension));
1734
}
1835

1936
[Fact]
2037
public static void GetDirectoryName()
2138
{
39+
Assert.Null(Path.GetDirectoryName(null));
2240
Assert.Equal("dir", Path.GetDirectoryName(Path.Combine("dir", "baz")));
41+
Assert.Equal(Path.GetDirectoryName("."), Path.GetDirectoryName("dir"));
2342
Assert.Equal(null, Path.GetDirectoryName(Path.GetPathRoot(Directory.GetCurrentDirectory())));
2443
}
2544

26-
[Fact]
27-
public static void GetExtension()
45+
[Theory]
46+
[InlineData(".exe", "file.exe")]
47+
[InlineData("", "file")]
48+
[InlineData(null, null)]
49+
[InlineData("", "file.")]
50+
[InlineData(".s", "file.s")]
51+
[InlineData("", "test/file")]
52+
[InlineData(".extension", "test/file.extension")]
53+
public static void GetExtension(string expected, string path)
2854
{
29-
Assert.Equal(".exe", Path.GetExtension("file.exe"));
30-
Assert.True(Path.HasExtension("file.exe"));
31-
32-
Assert.Equal(string.Empty, Path.GetExtension("file"));
33-
Assert.False(Path.HasExtension("file"));
34-
35-
Assert.Null(Path.GetExtension(null));
36-
Assert.False(Path.HasExtension(null));
37-
38-
Assert.Equal("", Path.GetExtension("file."));
39-
Assert.False(Path.HasExtension("file."));
40-
41-
Assert.Equal("", Path.GetExtension(Path.Combine("test", "file")));
42-
Assert.False(Path.HasExtension(Path.Combine("test", "file")));
55+
if (path != null)
56+
path = path.Replace('/', Path.DirectorySeparatorChar);
57+
Assert.Equal(expected, Path.GetExtension(path));
58+
Assert.Equal(!string.IsNullOrEmpty(expected), Path.HasExtension(path));
4359
}
4460

4561
[Fact]
@@ -60,21 +76,32 @@ public static void GetFileNameWithoutExtension()
6076
[Fact]
6177
public static void GetPathRoot()
6278
{
79+
Assert.Null(Path.GetPathRoot(null));
80+
6381
string cwd = Directory.GetCurrentDirectory();
6482
Assert.Equal(cwd.Substring(0, cwd.IndexOf(Path.DirectorySeparatorChar) + 1), Path.GetPathRoot(cwd));
6583
Assert.True(Path.IsPathRooted(cwd));
84+
6685
Assert.Equal(string.Empty, Path.GetPathRoot(@"file.exe"));
6786
Assert.False(Path.IsPathRooted("file.exe"));
87+
88+
if (Interop.IsWindows) // UNC paths
89+
{
90+
Assert.Equal(@"\\test\unc", Path.GetPathRoot(@"\\test\unc\path\to\something"));
91+
Assert.True(Path.IsPathRooted(@"\\test\unc\path\to\something"));
92+
}
6893
}
6994

7095
[Fact]
7196
public static void GetRandomFileName()
7297
{
98+
var fileNames = new HashSet<string>();
7399
for (int i = 0; i < 100; i++)
74100
{
75-
String s = Path.GetRandomFileName();
101+
string s = Path.GetRandomFileName();
76102
Assert.Equal(s.Length, 8 + 1 + 3);
77103
Assert.Equal(s[8], '.');
104+
Assert.True(fileNames.Add(s));
78105
}
79106
}
80107

@@ -85,6 +112,22 @@ public static void GetInvalidPathChars()
85112
Assert.NotSame(Path.GetInvalidPathChars(), Path.GetInvalidPathChars());
86113
Assert.Equal((IEnumerable<char>)Path.GetInvalidPathChars(), (IEnumerable<char>)Path.GetInvalidPathChars());
87114
Assert.True(Path.GetInvalidPathChars().Length > 0);
115+
Assert.All(Path.GetInvalidPathChars(), c =>
116+
{
117+
string bad = c.ToString();
118+
Assert.Throws<ArgumentException>(() => Path.ChangeExtension(bad, "ok"));
119+
Assert.Throws<ArgumentException>(() => Path.Combine(bad, "ok"));
120+
Assert.Throws<ArgumentException>(() => Path.Combine("ok", "ok", bad));
121+
Assert.Throws<ArgumentException>(() => Path.Combine("ok", "ok", bad, "ok"));
122+
Assert.Throws<ArgumentException>(() => Path.Combine(bad, bad, bad, bad, bad));
123+
Assert.Throws<ArgumentException>(() => Path.GetDirectoryName(bad));
124+
Assert.Throws<ArgumentException>(() => Path.GetExtension(bad));
125+
Assert.Throws<ArgumentException>(() => Path.GetFileName(bad));
126+
Assert.Throws<ArgumentException>(() => Path.GetFileNameWithoutExtension(bad));
127+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(bad));
128+
Assert.Throws<ArgumentException>(() => Path.GetPathRoot(bad));
129+
Assert.Throws<ArgumentException>(() => Path.IsPathRooted(bad));
130+
});
88131
}
89132

90133
[Fact]
@@ -106,6 +149,53 @@ public static void GetTempPath()
106149
Assert.True(Directory.Exists(tmpPath));
107150
}
108151

152+
[PlatformSpecific(PlatformID.Windows)]
153+
[Theory]
154+
[InlineData(@"C:\Users\someuser\AppData\Local\Temp\", @"C:\Users\someuser\AppData\Local\Temp")]
155+
[InlineData(@"C:\Users\someuser\AppData\Local\Temp\", @"C:\Users\someuser\AppData\Local\Temp\")]
156+
[InlineData(@"C:\", @"C:\")]
157+
[InlineData(@"C:\tmp\", @"C:\tmp")]
158+
[InlineData(@"C:\tmp\", @"C:\tmp\")]
159+
public static void GetTempPath_SetEnvVar_Windows(string expected, string newTempPath)
160+
{
161+
GetTempPath_SetEnvVar("TMP", expected, newTempPath);
162+
}
163+
164+
[PlatformSpecific(PlatformID.AnyUnix)]
165+
[Theory]
166+
[InlineData("/tmp/", "/tmp")]
167+
[InlineData("/tmp/", "/tmp/")]
168+
[InlineData("/", "/")]
169+
[InlineData("/var/tmp/", "/var/tmp")]
170+
[InlineData("/var/tmp/", "/var/tmp/")]
171+
[InlineData("~/", "~")]
172+
[InlineData("~/", "~/")]
173+
[InlineData(".tmp/", ".tmp")]
174+
[InlineData("./tmp/", "./tmp")]
175+
[InlineData("/home/someuser/sometempdir/", "/home/someuser/sometempdir/")]
176+
public static void GetTempPath_SetEnvVar_Unix(string expected, string newTempPath)
177+
{
178+
GetTempPath_SetEnvVar("TMPDIR", expected, newTempPath);
179+
}
180+
181+
private static void GetTempPath_SetEnvVar(string envVar, string expected, string newTempPath)
182+
{
183+
string original = Path.GetTempPath();
184+
Assert.NotNull(original);
185+
try
186+
{
187+
Environment.SetEnvironmentVariable(envVar, newTempPath);
188+
Assert.Equal(
189+
Path.GetFullPath(expected),
190+
Path.GetFullPath(Path.GetTempPath()));
191+
}
192+
finally
193+
{
194+
Environment.SetEnvironmentVariable(envVar, null);
195+
Assert.Equal(original, Path.GetTempPath());
196+
}
197+
}
198+
109199
[Fact]
110200
public static void GetTempFileName()
111201
{
@@ -127,12 +217,106 @@ public static void GetTempFileName()
127217
[Fact]
128218
public static void GetFullPath()
129219
{
220+
// Basic invalid arg checks
130221
Assert.Throws<ArgumentNullException>(() => Path.GetFullPath(null));
222+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(""));
223+
Assert.Throws<ArgumentException>(() => Path.GetFullPath("http://www.microsoft.com"));
224+
Assert.Throws<ArgumentException>(() => Path.GetFullPath("file://www.microsoft.com"));
131225

226+
// Basic expansions (e.g. self to self, period to self, normalization of lots of periods, etc.)
132227
string curDir = Directory.GetCurrentDirectory();
228+
Assert.Equal(curDir, Path.GetFullPath(curDir));
229+
Assert.Equal(curDir, Path.GetFullPath("."));
133230
Assert.Equal(curDir, Path.GetFullPath(Path.Combine(curDir, ".", ".", ".", ".", ".")));
134231
Assert.Equal(curDir, Path.GetFullPath(curDir + Path.DirectorySeparatorChar + Path.DirectorySeparatorChar + Path.DirectorySeparatorChar + "."));
135232
Assert.Equal(curDir, Path.GetFullPath(Path.Combine(curDir, "..", Path.GetFileName(curDir), ".", "..", Path.GetFileName(curDir))));
233+
Assert.Equal(Path.GetPathRoot(curDir), Path.GetFullPath(Path.Combine(Path.GetPathRoot(curDir), "somedir", "..")));
234+
Assert.Equal(Path.GetPathRoot(curDir), Path.GetFullPath(Path.Combine(Path.GetPathRoot(curDir), ".")));
235+
236+
// Try out a long path that normalizes down to less than MaxPath
237+
var longPath = new StringBuilder(curDir);
238+
for (int i = 0; i < 1000; i++)
239+
longPath.Append(Path.DirectorySeparatorChar).Append('.');
240+
Assert.Equal(curDir, Path.GetFullPath(longPath.ToString()));
241+
242+
// Some Windows-only checks
243+
if (Interop.IsWindows)
244+
{
245+
// Try out a long path that normalizes down to more than MaxPath
246+
for (int i = 0; i < 500; i++)
247+
longPath.Append(Path.DirectorySeparatorChar).Append('a').Append(Path.DirectorySeparatorChar).Append('.');
248+
Assert.Throws<PathTooLongException>(() => Path.GetFullPath(longPath.ToString()));
249+
250+
// alternate data streams aren't supported
251+
Assert.Throws<NotSupportedException>(() => Path.GetFullPath(@"C:\some\bad:path"));
252+
Assert.Throws<NotSupportedException>(() => Path.GetFullPath(@"bad:path"));
253+
254+
// Some Windows-specific bad paths
255+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(Path.DirectorySeparatorChar + ".. ." + Path.DirectorySeparatorChar));
256+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(Path.DirectorySeparatorChar + ". ." + Path.DirectorySeparatorChar));
257+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(Path.DirectorySeparatorChar + " ." + Path.DirectorySeparatorChar));
258+
Assert.Throws<ArgumentException>(() => Path.GetFullPath("C:..."));
259+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(@"C:...\somedir"));
260+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(@"C :"));
261+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(@"C :\somedir"));
262+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(@"bad::$DATA"));
263+
Assert.Throws<PathTooLongException>(() => Path.GetFullPath(@"C:\" + new string('a', 255) + @"\"));
264+
265+
// Some Windows-specific strange but legal paths
266+
Assert.Equal(
267+
Path.GetFullPath(curDir + Path.DirectorySeparatorChar),
268+
Path.GetFullPath(curDir + Path.DirectorySeparatorChar + ". " + Path.DirectorySeparatorChar));
269+
Assert.Equal(
270+
Path.GetFullPath(Path.GetDirectoryName(curDir) + Path.DirectorySeparatorChar),
271+
Path.GetFullPath(curDir + Path.DirectorySeparatorChar + "..." + Path.DirectorySeparatorChar));
272+
Assert.Equal(
273+
Path.GetFullPath(Path.GetDirectoryName(curDir) + Path.DirectorySeparatorChar),
274+
Path.GetFullPath(curDir + Path.DirectorySeparatorChar + ".. " + Path.DirectorySeparatorChar));
275+
276+
// Windows-specific UNC paths
277+
Assert.Equal(@"\\server\share", Path.GetFullPath(@"\\server\share"));
278+
Assert.Equal(@"\\server\share", Path.GetFullPath(@" \\server\share"));
279+
Assert.Equal(@"\\server\share\dir", Path.GetFullPath(@"\\server\share\dir"));
280+
Assert.Equal(@"\\server\share", Path.GetFullPath(@"\\server\share\."));
281+
Assert.Equal(@"\\server\share", Path.GetFullPath(@"\\server\share\.."));
282+
Assert.Equal(@"\\server\share\", Path.GetFullPath(@"\\server\share\ "));
283+
Assert.Equal(@"\\server\ share\", Path.GetFullPath(@"\\server\ share\"));
284+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(@"\\"));
285+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(@"\\server"));
286+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(@"\\server\"));
287+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(@"\\server\.."));
288+
Assert.Throws<ArgumentException>(() => Path.GetFullPath(@"\\?\GLOBALROOT\"));
289+
290+
// Windows short paths
291+
string tempFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N") + ".txt");
292+
File.Create(tempFilePath).Dispose();
293+
try
294+
{
295+
// Validate a short name can be expanded
296+
var sb = new StringBuilder(260);
297+
if (GetShortPathName(tempFilePath, sb, sb.Capacity) > 0) // only proceed if we could successfully create the short name
298+
{
299+
Assert.Equal(tempFilePath, Path.GetFullPath(sb.ToString()));
300+
301+
// Validate case where short name doesn't expand to a real file
302+
string invalidShortName = @"S:\DOESNT~1\USERNA~1.RED\LOCALS~1\Temp\bg3ylpzp";
303+
Assert.Equal(invalidShortName, Path.GetFullPath(invalidShortName));
304+
305+
// Same thing, but with a long path that normalizes down to a short enough one
306+
var shortLongName = new StringBuilder(invalidShortName);
307+
for (int i = 0; i < 1000; i++)
308+
shortLongName.Append(Path.DirectorySeparatorChar).Append('.');
309+
Assert.Equal(invalidShortName, Path.GetFullPath(shortLongName.ToString()));
310+
}
311+
}
312+
finally
313+
{
314+
File.Delete(tempFilePath);
315+
}
316+
}
136317
}
137318

319+
// Windows-only P/Invoke to create 8.3 short names from long names
320+
[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
321+
private static extern uint GetShortPathName(string lpszLongPath, StringBuilder lpszShortPath, int cchBuffer);
138322
}

0 commit comments

Comments
 (0)