|
| 1 | +using Microsoft.Diagnostics.Utilities; |
| 2 | +using Xunit; |
| 3 | + |
| 4 | +namespace PerfViewTests.Memory |
| 5 | +{ |
| 6 | + public class PathUtilitiesTests |
| 7 | + { |
| 8 | + [Theory] |
| 9 | + [InlineData(@"\\server\share\module.dll")] |
| 10 | + [InlineData(@"\\?\UNC\server\share\module.dll")] |
| 11 | + [InlineData(@"\\?\unc\server\share\module.dll")] |
| 12 | + [InlineData(@"\\.\UNC\server\share\module.dll")] |
| 13 | + [InlineData(@"\\?\GLOBALROOT\Device\Mup\server\share\module.dll")] |
| 14 | + [InlineData(@"\\?\GLOBALROOT\Device\LanmanRedirector\server\share\module.dll")] |
| 15 | + [InlineData(@"\??\UNC\server\share\module.dll")] |
| 16 | + [InlineData(@"\??\unc\server\share\module.dll")] |
| 17 | + [InlineData("https://server/share/module.dll")] |
| 18 | + [InlineData("http://server/share/module.dll")] |
| 19 | + [InlineData("ftp://server/module.dll")] |
| 20 | + public void IsRemotePathDetectsRemotePaths(string modulePath) |
| 21 | + { |
| 22 | + Assert.True(PathUtilities.IsRemotePath(modulePath)); |
| 23 | + } |
| 24 | + |
| 25 | + [Theory] |
| 26 | + [InlineData(@"C:\Symbols\foo.pdb\01234567890123456789012345678901FFFFFFFF\foo.pdb")] |
| 27 | + [InlineData(@"C:\Users\dev\src\bin\foo.dll")] |
| 28 | + [InlineData(@"module.dll")] |
| 29 | + [InlineData(@"subdir\module.dll")] |
| 30 | + [InlineData(@"..\module.dll")] |
| 31 | + [InlineData(@"D:\drive\path.dll")] |
| 32 | + [InlineData(@"\\?\C:\Windows\notepad.exe")] |
| 33 | + [InlineData(@"\\.\C:\Windows\notepad.exe")] |
| 34 | + [InlineData(@"\\?\Volume{12345678-1234-1234-1234-1234567890ab}\foo.dll")] |
| 35 | + public void IsRemotePathAcceptsLocalPaths(string modulePath) |
| 36 | + { |
| 37 | + Assert.False(PathUtilities.IsRemotePath(modulePath)); |
| 38 | + } |
| 39 | + |
| 40 | + [Theory] |
| 41 | + [InlineData(null)] |
| 42 | + [InlineData("")] |
| 43 | + public void IsRemotePathHandlesEmptyInputWithoutThrowing(string modulePath) |
| 44 | + { |
| 45 | + // Empty/null inputs are not remote (they will be caught elsewhere). |
| 46 | + Assert.False(PathUtilities.IsRemotePath(modulePath)); |
| 47 | + } |
| 48 | + |
| 49 | + [Theory] |
| 50 | + [InlineData(null)] |
| 51 | + [InlineData("")] |
| 52 | + [InlineData(".")] |
| 53 | + [InlineData("..")] |
| 54 | + public void SanitizeFileName_ReturnsNullForRejectedInput(string input) |
| 55 | + { |
| 56 | + // null / empty / "." / ".." all return null so callers can choose how to |
| 57 | + // handle the missing name (skip the resource, substitute a placeholder, |
| 58 | + // etc.) instead of being forced to accept an arbitrary string. |
| 59 | + Assert.Null(PathUtilities.SanitizeFileName(input)); |
| 60 | + } |
| 61 | + |
| 62 | + [Theory] |
| 63 | + [InlineData(@"..\outside", ".._outside")] |
| 64 | + [InlineData(@"..\..\..\Startup\x", ".._.._.._Startup_x")] |
| 65 | + [InlineData("../forward/slash", ".._forward_slash")] |
| 66 | + [InlineData(@"C:\Windows\System32\evil", "C__Windows_System32_evil")] |
| 67 | + [InlineData(@"\\server\share\evil", "__server_share_evil")] |
| 68 | + [InlineData(@"with:colons", "with_colons")] |
| 69 | + [InlineData("with|pipes?and*wildcards", "with_pipes_and_wildcards")] |
| 70 | + public void SanitizeFileName_ReplacesInvalidCharactersAndSeparators(string input, string expected) |
| 71 | + { |
| 72 | + // Every path separator, volume separator, and Path.GetInvalidFileNameChars |
| 73 | + // character is replaced with '_'. Control characters are also replaced. |
| 74 | + Assert.Equal(expected, PathUtilities.SanitizeFileName(input)); |
| 75 | + } |
| 76 | + |
| 77 | + [Theory] |
| 78 | + [InlineData("CON", "_CON")] |
| 79 | + [InlineData("nul", "_nul")] |
| 80 | + [InlineData("PRN", "_PRN")] |
| 81 | + [InlineData("AUX", "_AUX")] |
| 82 | + [InlineData("COM1", "_COM1")] |
| 83 | + [InlineData("LPT9", "_LPT9")] |
| 84 | + [InlineData("CLOCK$", "_CLOCK$")] |
| 85 | + [InlineData("CONIN$", "_CONIN$")] |
| 86 | + [InlineData("conout$", "_conout$")] |
| 87 | + [InlineData("CONIN$.log", "_CONIN$.log")] |
| 88 | + [InlineData("NUL.", "_NUL")] |
| 89 | + [InlineData("NUL ", "_NUL")] |
| 90 | + [InlineData("NUL. . ", "_NUL")] |
| 91 | + [InlineData("NUL.evil", "_NUL.evil")] |
| 92 | + [InlineData("CON.foo.bar", "_CON.foo.bar")] |
| 93 | + [InlineData("com1.data", "_com1.data")] |
| 94 | + [InlineData("LPT5.tar.gz", "_LPT5.tar.gz")] |
| 95 | + public void SanitizeFileName_RewritesReservedDosDeviceNames(string input, string expected) |
| 96 | + { |
| 97 | + // Win32 matches a reserved device name on the stem before the first '.' |
| 98 | + // in the basename, so "NUL.evil" or "COM1.data" still open the device. |
| 99 | + // The sanitizer prefixes such names with '_' to make them safe. |
| 100 | + Assert.Equal(expected, PathUtilities.SanitizeFileName(input)); |
| 101 | + } |
| 102 | + |
| 103 | + [Theory] |
| 104 | + [InlineData("Trailing.", "Trailing")] |
| 105 | + [InlineData("Trailing ", "Trailing")] |
| 106 | + [InlineData("Trailing.. .", "Trailing")] |
| 107 | + public void SanitizeFileName_StripsTrailingDotsAndSpaces(string input, string expected) |
| 108 | + { |
| 109 | + // Windows silently trims trailing '.' and ' ' from file names; stripping |
| 110 | + // them ourselves prevents two distinct names colliding on disk and |
| 111 | + // closes the "NUL." device-name evasion. |
| 112 | + Assert.Equal(expected, PathUtilities.SanitizeFileName(input)); |
| 113 | + } |
| 114 | + |
| 115 | + [Theory] |
| 116 | + [InlineData("...")] |
| 117 | + [InlineData(" ")] |
| 118 | + public void SanitizeFileName_ReturnsNullWhenAllCharactersAreStripped(string input) |
| 119 | + { |
| 120 | + // Inputs that consist entirely of trailing-trim characters (or sanitize |
| 121 | + // to nothing) return null rather than the empty string. |
| 122 | + Assert.Null(PathUtilities.SanitizeFileName(input)); |
| 123 | + } |
| 124 | + |
| 125 | + [Theory] |
| 126 | + [InlineData("Contoso.Provider-Valid_1", "Contoso.Provider-Valid_1")] |
| 127 | + [InlineData("My Provider", "My Provider")] |
| 128 | + [InlineData("provider.with.dots", "provider.with.dots")] |
| 129 | + public void SanitizeFileName_PreservesValidNames(string input, string expected) |
| 130 | + { |
| 131 | + Assert.Equal(expected, PathUtilities.SanitizeFileName(input)); |
| 132 | + } |
| 133 | + } |
| 134 | +} |
| 135 | + |
0 commit comments